零秒精通JavaScript是真的

编程就如周末在小区阳光下的石条凳上与老人下象棋。
chess

在第一节中,我们学习了程序构成的基本元素,这就如掌握了“车马炮”的走法,仅仅是破题迈开的第一步。学“象棋”,我们还要继续学习如何开局,如何布局,战略战术,弃子与搏杀。作为编程的新手,我们还不知道“下哪一步棋是有价值的?”(定义有价值的函数),更加不能如高手一样”看出去三步棋”或者“看出去五步棋”(预测函数执行的结果)。

这项能在头脑中可视化棋局的发展和预测每个动作的连锁反应的能力是成为象棋高手的诀窍也是迈向顶级程序员的必经之路。成为象棋高手,我们必须常常在头脑中下盲棋;而成为编程高手,同样需要在头脑中将程序的运行视觉化。

评论区连载。

1赞

兄弟,能不能简洁点?

传递知识大家都欢迎,但是每次都绕那么大圈说事情是不是太绕了?

直接点嘛,看那么大段文字,你写的累不累?

2赞

而且能不能别写零秒精通?

这个世界上高手那么多,我从来没有见过敢说零秒精通的,不误人子弟已经很不容易了。

咱能不能实在点,别定期零秒精通xxx?你真的精通了吗?

10赞

写的有Learn Javascript in Y Minutes的感觉,就是标题起的有点嘲讽。

就是那种看了之后感觉自己什么都会了,等真写起来就发现自己什么都不会 :rofl:

我真的感觉楼主可以多写点干货,就像前两天有个朋友写pcase的用法那样,没有啥修饰,但是读几遍都很有用,内容很实际。

天马行空可以,通篇和elisp以及emacs毛关系都没有,你不是浪费大家的时间吗?标题取得那么显眼,那么精通,内容呢,除了废话有啥?

1赞

这就去搞个零秒精通Clojure

呀,狗哥也掌握时空技术了?

一、构成程序的基本元素

程序的构成有两项要素,即数据data和函数function。构思程序起于青萍之末的原始材料数字,扶摇直上到“变量”,到表达力飞跃的“函数”,又及创造智能的“条件判断”,进而到自动化的“迭代与递归”。最后作为思考工具,我们将函数作为“思维抽象体“看待,而非“运算执行实体”。

下面,我们将一一展开叙述:

1.数学表达式 Expression

JavaScript使用完全符合我们直觉的infix中序表达式:

// 加减乘除
> 173 + 373
546
> 1002 - 457
545
> 8 * 43
344
> 11 / 3
3.6666666666666665
> 11.0 / 4
2.75
> 10 / 2
5
> 3.7 + 10
13.7
//模除
> 11 % 3
2
//乘方
> 2 ** 11
2048
//组合
> (1 - ((5 / 2) * 4)) + 3
-6

2.定义常量与变量

以抽象的变量指代具体的对象,是程序迈开步伐走向抽象的第一步:

//定义常量
> const pi = 3.1415926 //const for constant 不变的值
undefined
//定义变量
> var radius = 11 //var for variable 变化的值
undefined
> pi * radius ** 2
380.1327046
> var area = pi * radius ** 2
undefined
> area
380.1327046
> var circumference = 2 * pi * radius
undefined
> circumference
69.1150372

3.函数 Function

函数是程序走向走向的第二步,表达力骤然飙升。

> function square(x) {
... return x ** 2;
... }
undefined
> square(11)
121

定义函数的格式为:

function name(parameters) { return expression; }

往函数中传入表达式:

> square(11 + 13);
576

组合函数求勾股定理:

> function sum_of_squares(x,y) {
... return square(x) + square(y);
... }
> sum_of_squares(3, 4)
25

在此基础上,进一步构建:

> function f(a) {
... return sum_of_squares(a+1, a*2);
... }
undefined
> f(7)
260

4.条件判断表达式 Conditional Expressions and Predicates

条件判断赋予程序以”智能“!!!

predicate ?  //predicate 这个术语用得最妙
consequent-expression : 
alternative-expression

求绝对值:

function abs(x) {
    return x >= 0 ? x : -x;
}

> abs(-5);
5

条件判断除了基本的数字运算符 >=, >, <, <=, ===, and !==之外,还有表达力更上一层楼的逻辑运算符:(注意相等与不等的判断)

> expression1 && expression2
> expression1 || expression2
> ! expression

于是,定义域15<x<20可以表达为:

x > 15 && x < 20

注意逻辑运算符&&的优先级”低于“算术运算符<>

小试牛刀:

> function greater_or_equal(x, y) {
... return x > y || x === y;
... }
> function greater_or_equal_alter(x, y) {
... return ! (x < y);
... }

测试Nomal-Order与Application-Order:

> function p() {return p();}

> function test(x, y) {
... return x === 0 ? 0 : y;
... }

> test(0, p())
Uncaught RangeError: Maximum call stack size exceeded
    at p (REPL80:1:15)
    at p (REPL80:1:22)
    at p (REPL80:1:22)
    at p (REPL80:1:22)
    at p (REPL80:1:22)
    at p (REPL80:1:22)
    at p (REPL80:1:22)
    at p (REPL80:1:22)
    at p (REPL80:1:22)
    at p (REPL80:1:22)

5.迭代与递归

现在是函数展现“真正实力”的时候了。

我们尝试应用循环结构求平方根,并且采用牛顿迭代算法:

牛顿求平方根法用的是‘连续逼近’的思路 “successive approximations”。我们首先瞎猜一个y作为x的平方根,然后在这个瞎猜的基础上更好的瞎猜,不断地接近平方根的真实值。这个better-guess为前面的瞎猜值与x/y的平均值,分布过程展示为:

零秒精通Javascript:构成程序的基本元素有7项

牛顿迭代法

> function sqrt_iter(guess, x) {
...     return good_enough(guess, x)
...         ? guess
...         : sqrt_iter(improve(guess, x), x);
... }

> function improve(guess, x) {
...     return average(guess, x/guess);
... }

> function average(x, y) {
...     return (x + y) / 2;
... }

> function good_enough(guess, x){
...     return abs(square(guess) - x) < 0.001;
... }

> function sqrt(x) {
...     return sqrt_iter(1, x);
... }

> sqrt(9)
3.00009155413138

求立方根的公式(牛顿迭代法求立方根,基于以下公式):

(x/y**2 + 2y) / 3
function abs(x) {
    return x >= 0 ? x : -x;
}

function cube(x) {
    return x * x * x;
}

function good_enough(guess, x) {
    return abs(cube(guess) - x) < 0.001;
}
function div3(x, y) {
     return (x + y) / 3;
}
function improve(guess, x) {
    return div3(x / (guess * guess), 2 * guess);
}
function cube_root(guess, x) {
    return good_enough(guess, x)
               ? guess
               : cube_root(improve(guess, x), x);
}
//对仗真是工整呀。
cube_root(3, 27);

6.函数作为抽象的思维工具 Functions as Black-Box Abstractions

应用一个尚未定义的函数时,我们思维里是“函数的抽象”而非“函数的实体”。

下面两个函数,虽然实现方法不同,但均实现同样的目的。

function square(x) {
    return x * x;
}

function square(x) {
    return math_exp(double(math_log(x)));
}
function double(x) {
    return x + x;
}

局域变量Local-Names

function square(x) {
    return x * x;
}
function square() {
    return y* y;
}

变量名不同,运算相同。

函数的嵌套 Internal definitions declarations and block structure

function abs(x) {
    return x >= 0 ? x : -x;
}

function square(x) {
    return x * x;
}

function average(x,y) {
    return (x + y) / 2;
}

function sqrt(x) {
    function good_enough(guess, x) {
        return abs(square(guess) - x) < 0.001;
    }
    function improve(guess, x) {
        return average(guess, x / guess);
    }
    function sqrt_iter(guess, x) {
        return good_enough(guess, x) 
                   ? guess
                   : sqrt_iter(improve(guess, x), x);
    }
    return sqrt_iter(1, x);
}

sqrt(5);

7.收尾总结

程序的构成有两项要素,数据data和函数function。构思程序起于青萍之末的primitive-date数字,扶摇螺旋上升到“变量”,表达力飞跃的“函数”,创造智能的“条件判断”,进而到自动化的迭代与递归。最后作为思考工具,我们将函数作为“思维抽象体“看待,而非“运算执行实体”。

从这一节开始,我们将踏上思维的饕餮盛宴。

估计就是因为这样写不但不累,还特別爽,爽到不让別人拜读一下就不行,几天不写手就痒的病。

说点鼓励话:楼主的内容其实也还不错的,就那精神就让我很敬佩,虽然我也同意内容有点过于绕,如果直接点,会更好。楼主加油,毕竟坚持做一个事情不容易。

2赞

感谢 :smiley:

总结第一节:
1.数字是最原始的数据,运算符是最原始的函数。
2.在此基础上,编程语言引入“命名变量”程序踏步迈入抽象化表达门径;
3.作为“变量”的复杂形式,给动态的程序命名的函数,将程序的表达力垂直拔起。
4.“条件判断”赋予程序以“智能”,
5.而“迭代与递归”实现从“人扛马拉”到自动化的奋然一跃。
6.最后,编程语言Javascript作为语言的一种,是思维的利器,将函数作为“思维抽象体”thinking-abstraction,给解决问题的思维过程添砖加瓦。

2赞

个人比较喜欢作者文风, 看多了干瘪无味的技术文,偶尔来点调味也非常不错。

2赞

谢谢 :grinning:

这个系列的帖子是从 SICP in JS 翻译过来的,掺杂了些个人想法。

https://source-academy.github.io/sicp/split/

10天0star,有点出乎意料。每回重读sicp都沉浸在手舞足蹈的狂喜之中,恨不得字字句句背下来。

但scheme只能漏夜独思,当看到这套打磨了12年的sicp in js,念及竟然能将sicp的理念完全用到手上脚上,beyond exciting.

收尾帖子,github上的clone也关闭了。