第九章 ...and Again, and Again, and, Again, ...
你想来点鱼子酱吗?
那就去找它吧。
(looking a lat)是什么,其中a是 caviar, lat是(6 2 4 caviar5 7 3)
#t真,caviar当然是lat了
(looking a lat), 其中a是 caviar, lat是(6 2 grits caviar 5 7 3)
#f
你察觉到什么不同吗
是啊,caviar不是一直在lat中吗
没错,但是lat中第一个数是多少
6
lat的第六个元素是什么
7
lat的第七个元素是什么
3
所以 looking 是找不到 caviar 的
是的,因为第三个元素是 grits,这个不是 caviar
下面这是 looking
(define looking
(lambda (a lat)
(keep-looking a (pick 1 lat) lat)))
写个函数 keep-looking
我们不期待你能懂这个。
(looking a lat), 其中a是 caviar , lat 是(6 2 4 caviar 5 7 3)
#t,因为(keep-looking a 6 lat)和(keep-looking a (pick 1 lat) lat)答案相同。
(pick 6 lat)是什么,其中lat是(6 2 4 caviar 5 7 3)
7
所以我们该干什么
(keep-looking a 7 lat), 其中a是caviar,lat是(6 2 4 caviar 5 7 3)
(pick 7 lat)是什么,其中lat是(6 2 4 caviar 5 7 3)
3
(keep-looking a 3 lat)是什么,其中a是caviar,lat是(6 2 4 caviar 5 7 3)
也就是(keep-looking a 4 lat)。
也就是?
#t。
写出 keep-looking
(define keep-looking
(lambda (a sorn lat)
(cond
((number? sorn) (keep-looking a (pick sorn lat) lat))
(else (eq? sorn a)))))
你能猜出sorn表示什么吗
符号或者数(Symbol or number)
keep-looking 有什么特殊的地方吗
它没有递归 lat的部分
我们把这个称为非自然递归"unnatural" recursion。
的确不自然。
keep-looking慢慢的接近它的目标了吗
是的,各方面证据确凿。
它总是接近自己的目标吗
有时候表中可能既没有 caviar 也没有 grits。
一个表可能是一个tup。
是的,如果我们 looking (7 2 4 7 5 6 3),我们将永远不会停止looking。
(looking a lat) 是什么,其中a是caviar,lat是(7 1 2 caviar 5 6 3)
这个太奇怪了!
是很奇怪。发生了什么?
我们不停的 looking...
像 looking 这样的函数称为 partial function。那么你认为我们之前见过的函数叫什么呢?
total。
你能定义一个函数,对于一些参数,它永远达不到他的目标吗?
(define eternity
(lambda (x)
(eternity x)))
多少个参数可以让函数 eternity 到达它的目标?
没有。这怕是最不自然的递归了。
eternity是partial的吗?
它是最partial的function了。
(shift x) 是什么,其中x是((a b) c)
(a (b c))
(shift x) 是什么,其中x是((a b) (c d))
(a (b (c d)))
定义 shift 函数
小事一桩;这连递归都不是!
(define shift
(lambda (pair)
(build (first (first pair))
(build (second (first pair))
(second pair)))))
描述 shift 做了什么
下面是我们的说法:
“函数 shift 输入参数为一个pair。输入参数的第一个元件是一个pair。函数把第一个元件的fisrt部分移到了第二个元件中,形成一个新的pair。”
现在看看下面这个函数:
(define align
(lambda (pora)
(cond
((atom? pora) pora)
((a-pair? (first pora)) (align (shift pora)))
(else (build (first pora) (align (second pora)))))))
这与函数 keep-looking 有什么共同点
两个函数都在递归时改变参数但是两种情况的改变都不能保证接近目标。
为什么我们不能保证 align 的进展?
在 cond 第二行 shift 为 align 产生的参数不是原来参数的一部分。
那样违反了哪个戒律?
第七戒
新的参数至少比原来的小?
align里边可没看出来。
为什么不是?
函数 shift 仅仅把 pair 中的顺序调整了一下。
所以?
得到的结果和 shift 的参数都有同样多的原子。
你能写出一个函数来计align的参数的原子数目吗?
没问题:
(define length*
(lambda (pora)
(cond
((atom? pora) 1)
(else
(+ (length* (first pora))
(length* (second pora)))))))
align是一个 partial function 吗?
我们还不知道。或许有参数可以让它align。
输入给align的参数在递归时,有别的什么变化吗?
有的。pair的第一个元件变得更加简单,而第二个元件变得更加复杂。
第一个元件是怎样变得简单的?
它仅仅是原来pair的第一个元件的一部分。
这不就是意味着 length* 是错误的检测参数长度的函数?你能找到更好的函数吗?
更好的函数应该更加注意第一个元件。
那我们需要对第一个元件的多少注意力呢?
至少是多一倍。
意思是像 weight* 这样的吗
(define weight*
(lambda (pora)
(cond
((atom? pora) 1)
(else
(+ (× (weight* (first pora)) 2)
(weight* (second pora)))))))
这才对。
笔记:真不知道作者把这个length*和这个weight*放在这是什么意思。
(weight* x)是什么,其中x是((a b) c)
7
这意味着参数变得简单了?
是的, align的参数的weight*依次变得更小了。
align是一个 partial function?
不是,它对任何参数都得到一个值。
下面这个类似align的函数 shuffle ,使用了第七章的 revpair 函数替代了 shift:
(define shuffle
(lambda (pora)
(cond
((atom? pora) pora)
((a-pair? (first pora)) (shuffle (revpair pora)))
(else (build (first pora) (shuffle (second pora)))))))
这表示 shuffle 是 total function 吗?
我们不知道。
让我们试试。(shuffle x)的值是什么,其中x是(a (b c))
(a (b c))
(shuffle x), 其中x是(a b)
(a b)
让我们试试有趣的东西。(shuffle x)的值是什么,其中x是((a b) (c d))。
为了确定这个值,我们必须找出(shuffle (revpair pora))的值,其中 pora 是((a b) (c d))
我们现在打算怎么做?
我们打算确定(shuffle pora)的值,其中 pora 是 ((c d) (a b))。
那不是意味着我们需要知道(shuffle (revpair pora))的值,其中 (revpair pora) 是((a b) (c d))
是的。
然后呢?
shuffle 函数不是 total function, 因为它再一次交换了pair的元件,这意味着我们用重头开始了。
这个是 total function吗
(define C
(lambda (n)
(cond
((one? n) 1)
(else
(cond
((even? n) (C (÷ n 2)))
(else (C (add1 (× 3 n)))))))))
对于0,它没有值,其它的就天知道了。感谢您,Lothan Collatz (1910-1990)。
(A 1 0)的值是多少
2
(A 1 1)
3
(A 2 2)
7
下面是A的定义
(define A
(lambda (n m)
(cond
((zero? n) (add1 m))
((zero? m) (A (sub1 n) 1))
(else (A (sub1 n) (A n (sub1 m)))))))
感谢您,Wilhelm Ackermann (1853-1946)。
A与 shuffle 和 looking 有什么共同点?
A的参数像 shuffle和 looking的一样在递归的时候减小都不是必要的。
举个例子?
很简单:(A 1 2)需要求(A 0 (A 1 1))。这又需要我们求(A 0 3)。
A总是给出答案吗?
是的,它是 total function。
那(A 4 3)是多少?
对于实际情况,这是没有答案的。
什么意思?
在你读完这一页之后很久很久(A 4 3)的值都难以计算完成。
但是答案没有出来——真奇怪,因为它们吃掉了所有的答案。
The Walrus and The Carpenter ——Lewis Carroll
如果我们能写一个函数来告诉我们哪写方程对任意输入都有返回值不是很好吗?
当然可以。既然我们已经知道有一些函数永远没有返回值或者返回值得到的太慢了,我们应该搞一些这样的工具。
Okey,我们开始写吧。
听起来好复杂。一个函数可以对输入许多不同的参数都运行正常。
那我们把它做简单点。作为热身练习,让我们关注一种函数,它检查某些函数是否在输入空表时会停止。
这样化简了很多。
下面这是函数的开头:
(define will-stop?
(lambda (f)
...))
你能填写省略的部分吗?
它做什么啊?
will-stop? 对所有参数都有返回值吗?
这个简单:我们说它要么返回#t要么返回#f,这依赖于当输入参数(某个函数)对空表()作用时是否停止。
will-stop?是 total function吗?
是的。它总是返回#t或者#f。
那我们来看一些例子。(will-stop? f),其中f是length
我们知道(length l)是0,其中l是()。
所以?
(will-stop? length)的值是#t。
当然。其它例子呢?(will-stop? eternity)的值是什么?
先前我们已经知道(eternity (quote ()))不会返回一个值。
就是说(will-stop? eternity)的值是#f吗?
对。
我们需要更多的例子吗?
还需更多例子。
笔记:也可以参见维基百科的停机问题。
Okey,下面的这个对于 will-stop 是一个有意思的参数。
(define last-try
(lambda (x)
(and (will-stop? last-try) (eternity x))))
(will-stop? last-try)是什么?
它是做什么的?
我们需要用()做测试。
如果我们想要得到(last-try (quote ())),的值我们必须求得
(and (will-stop? last-try) (eternity (quote ())))
(and (will-stop? last-try) (eternity (quote ())))
的值是什么?
这依赖于(will-stop? last-try)的值。
这有两种可能。我们假设(will-stop? last-try)是#f 笔记:注意这里假设了矛
Okey,那么(and #f (eternity (quote ())))是#f,因为(and #f ...)总是#f。
所以(last-try (quote ())) 停止了,对吗? 笔记:注意这里推出了盾
是的。
那 will-stop?不就会预言反了吗?
是的。我们假设的是当(will-stop? last-try)是#f时,last-try 不停止。
所以我们假设(will-stop? last-try)错了?
是的。它必须返回#t,因为 will-stop? 总是给出一个答案。我们称之为 total。
好的。如果(will-stop? last-try)是#t,那么(last-try (quote ()))的值是什么?
现在我们只需要判别(and #t (eternity (quote ()))),这和(eternity (quote ()))的值是相同的。
(eternity (quote ())) 的值是什么?
它没有值。我们知道它不会停的。
但是这表示我们又错了!
是的,因为这次我们假设的是 (will-stop? last-try)是#t。
你认为这说明了什么?
这是我们的表述:
“我们认真的推导了两种可能的情况。如果我们定义 will-stop 那么 (will-stop? last-try)必须是要么#t要么#f。但是它不能——因为 will-stop? 没有作到定义要求。这就是说 will-stop?不能定义出来。”
这是唯一的吗?
是的。它是我们能够确切描述但是无法用我们的语言定义的函数。
这个问题有解决方法吗?
没有。感谢您,Alan M. Turing (1912-1954) 和Kurt Godel (1906-1978)。
(define ...)是什么?
这是个有趣的问题。我们刚才看到(define ...)对 will-stop?不起作用。
所以递归的定义是什么?
抱紧,深吸一口气,等你准备好了再投身前进。
笔记:书里边下面的内容Y算子,但是讲的效果不好。建议直接参见维基百科的Y算子。或者参考其他书。
这是函数 length 吗
(define length
(lambda (l)
(cond
((null? l) 0)
(else (add1 (length (cdr l)))))))
是啊。
假如我们没有(define ...)呢?我们依旧可以定义 length 吗?
没有(define ...), length与什么都没有联系,和 length 的函数体更没有关系。
下面这个函数做了什么
(lambda (l)
(cond
((null? l) 0)
(else (add1 (eternity (cdr l))))))
他确定了空表的长度,其它的什么也没做。
当我们把它作用在非空表上呢
没有答案。如果我们给 eternity 一个参数,它不会给出答案。
这个看起来像是 length 函数的定义有什么用?
它对非空表都没有答案。
假设我们可以对这个定义一个函数名,那么可以怎么起?
length0。因为它只能确定空表的长度。
你如何写一个函数来确定含有1个或者更少项的表的长度呢?
我们可以这样子:
(lambda (l)
(cond
((null? l) 0)
(else (add1 (length0 (cdr l))))))
差不多,但是length0 没有使用(define ...)去定义。
所以,length0被它的定义代替。
(lambda (l)
(cond
((null? l) 0)
(else (add1 ((lambda(l)
(cond
((null? l) 0)
(else (add1 (eternity (cdr l))))))
(cdr l))))))
给这个函数其个什么名字好呢?
简单: length≤1。
下面这个函数是不是可以确定含有两个或者更少的项的表的长度呢?
(lambda (l)
(cond
((null? l) 0)
(else (add1 ((lambda(l)
(cond
((null? l) 0)
(else (add1 ((lambda(l)
(cond
((null? l) 0)
(else (add1 (eternity (cdr l))))))
(cdr l))))))
(cdr l))))))
是的,这是 length≤2 。我们把 eternity替换为下一个版本的 length。
现在你认为n递归是什么?
什么意思?
现在,我们已经知道如何确定没有项的表,有一项的表,有两项的表的长度。我们怎样使函数 length 返回?
如果我们能写出一个 length0,length≤1,length≤2,...的无穷函数,那么我们就能写出函数 length∞来。这样我们就可以处理所有长度的表的情况了。
我们能搞出多长的表?
空表,或者1个元素,或者2个元素,或者3个元素,...,1001个,...
但是我们写不出来无穷函数。
是写不出来啊。
我们在这些个函数里边重复了许多模式。
是的。
这些模式看起来是什么样子的?
所有的这些个程序都包含像 length的部分。或许我们应该把这个函数抽象出来:见第九戒。
我们开始吧!
我们需要一个看起来像是 length 函数,它以(lambda (length) ...) 开头。
是说像这样子吗?
((lambda (length)
(lambda (l)
(cond
((null? l) 0)
(else (add1 (length (cdr l))))))) eternity)
没错,就是这样。它创建了 length0。
笔记:使用beta规约(就是eternity替换length)就可以得到length0
同样的方式重写 length≤1。
((lambda (f)
(lambda (l)
(cond
((null? l) 0)
(else (add1 (f (cdr l)))))))
((lambda (g)
(lambda (l)
(cond
((null? l) 0)
(else (add1 (g (cdr l)))))))
eternity))
笔记:使用beta规约(eternity替换g),再次使用beta规约(第二大块代码替换f)就可以得到 length≤1。
我们必须把参数命名为 length 吗?
不需要,我们直接用f和g。只要我们是一贯的,一切都还好。
length≤2呢?
((lambda (length)
(lambda (l)
(cond
((null? l) 0)
(else (add1 (length (cdr l)))))))
((lambda (length)
(lambda (l)
(cond
((null? l) 0)
(else (add1 (length (cdr l)))))))
((lambda (length)
(lambda (l)
(cond
((null? l) 0)
(else (add1 (length (cdr l)))))))
eternity)))
笔记:使用beta规约3次得到length≤2。从后往前。
更进一步了,但是依然有重复。
是的,让我们消除重复。
我们应该从哪里开始?
命名一个函数,这个函数以 length 为参数,并且返回一个类似 length 的函数。
起个什么名字好呢?
“make length”如何?
好的。就这样子,写个 length0。
没问题。
((lambda (mk-length)
(mk-length eternity))
(lambda (length)
(lambda (l)
(cond
((null? l) 0)
(else (add1 (length (cdr l))))))))
下面这是 length≤1
((lambda (mk-length)
(mk-length
(mk-length eternity)))
(lambda (length)
(lambda (l)
(cond
((null? l) 0)
(else (add1 (length (cdr l))))))))
没错。下面这是length≤2。
((lambda (mk-length)
(mk-length
(mk-length
(mk-length eternity))))
(lambda (length)
(lambda (l)
(cond
((null? l) 0)
(else (add1 (length (cdr l))))))))
你能写出类似的 length≤3 吗?
当然。就是这样。
((lambda (mk-length)
(mk-length
(mk-length
(mk-length
(mk-length eternity)))))
(lambda (length)
(lambda (l)
(cond
((null? l) 0)
(else (add1 (length (cdr l))))))))
递归像是什么样?
mk-length就像无穷层的塔一样,应用一个 arbitrary function。
我们必须要一个无穷层的塔吗?
当然没必要。每次使用 length, 我们都是对一个有限的个数来使用,但是我们永远不知道该是多少。
我们能猜需要多少吗?
当然,但是我们可能猜的数不够大。
我们什么时候知道我们猜的数不够大?
当我们应用到了 mk-length 中最内层的 eternity时。
要是当执行 eternity时,我们能够创建 mk-length 来执行呢?
那样只能把问题向后推一次,即使那样我们又能做什么呢?
好,那么既然给 mk-length 输入的是什么参数无所谓,那我们一开始把 mk-length 作为参数输入到 mk-length中去。
好主意。我们对 eternity 调用 mk-length以及对cdr调用其结果,这样我们就能使塔有更多层。
下面这个还是 length0 吗?
((lambda (mk-length)
(mk-length mk-length))
(lambda (length)
(lambda (l)
(cond
((null? l) 0)
(else (add1
(length (cdr l))))))))
还是length0啊。我们甚至可以用 mk-length 替代 length。笔记:就是alpha规约,除了自变量名替换了什么都没变。
((lambda (mk-length)
(mk-length mk-length))
(lambda (mk-length)
(lambda (l)
(cond
((null? l) 0)
(else (add1
(mk-length (cdr l))))))))
我们为什么要那样做?
所有的名字都是等效的,但是一些名字比其它的名字更加等效。All names are equal, but some names are more equal than others.
注:George Orwell(1903-1950)
的确:只要我们名字持续,一切都好。
mk-length 是一个比 length 更加通用的名字。如果我们使用 mk-length,那么它总是提示我们 mk-length 的第一个参数是 mk-length。
既然把 mk-length 作为参数传递给了 mk-length,那么我们能用参数创造递归来使用吗?
能。当我们执行了一次 mk-length,我们得到 length≤1
((lambda (mk-length)
(mk-length mk-length))
(lambda (mk-length)
(lambda (l)
(cond
((null? l) 0)
(else (add1
(mk-length eternity (cdr l))))))))
假设l是(apple)问下面执行的结果是什么?
(define 'l (apple))
(((lambda (mk-length)
(mk-length mk-length))
(lambda (mk-length)
(lambda (l)
(cond
((null? l) 0)
(else (add1
(mk-length eternity (cdr l))))))))
l)
这是一个好例子。用纸和笔算算吧。
笔记:(mk-length (cdr l))即(mk-length ())即0,于是加1,得到1。
我们能不止一次这样做吗?
能,只要不断向 mk-length 传递参数 mk-length就行。我们可以经常这样做。
你把下面这个函数称作什么?
((lambda (mk-length)
(mk-length mk-length))
(lambda (mk-length)
(lambda (l)
(cond
((null? l) 0)
(else (add1
((mk-length mk-length) (cdr l))))))))
当然是length啰。
它是怎么工作的?
当它要expire(挂掉?)时它通过把 mk-length 传给自己不断添加递归。
还剩下一个问题:它再也不含像函数 length 的部分了。
((lambda (mk-length)
(mk-length mk-length))
(lambda (mk-length)
(lambda (l)
(cond
((null? l) 0)
(else (add1
((mk-length mk-length) (cdr l))))))))
你能解决这个问题吗?
我们可以把这个新的应用从 mk-length 中抽取出来,把它称为 length。
为什么?
因为它的确创建了函数 length。
那下面这个如何?
((lambda (mk-length)
(mk-length mk-length))
(lambda (mk-length)
((lambda (length)
(lambda (l)
(cond
((null? l) 0)
(else (add1 (length (cdr l)))))))
(mk-length mk-length))))
这个看起来好些。
让我们看看他是否执行无误。
Okey。
下面这个代码值是多少?
(define 'l (apple))
(((lambda (mk-length)
(mk-length mk-length))
(lambda (mk-length)
((lambda (length)
(lambda (l)
(cond
((null? l) 0)
(else (add1 (length (cdr l)))))))
(mk-length mk-length))))
l)
应该是1。
首先我们需要计算
((lambda (mk-length)
(mk-length mk-length)) ;;第一个 mk-length展开,得到后面的一问的回答。
(lambda (mk-length)
((lambda (length)
(lambda (l)
(cond
((null? l) 0)
(else (add1 (length (cdr l)))))))
(mk-length mk-length))))
没错,因为这个表达式是我们需要对l执行的函数。
所以我们需要求得
((lambda (mk-length)
((lambda (length)
(lambda (l)
(cond
((null? l) 0)
(else (add1 (length (cdr l)))))))
(mk-length mk-length)))
(lambda (mk-length)
((lambda (length)
(lambda (l)
(cond
((null? l) 0)
(else (add1 (length (cdr l)))))))
(mk-length mk-length))))
是的。
笔记:这有个问题:这个变换说明了 mk-length 就是
(lambda (length)
(lambda (l)
(cond
((null? l) 0)
(else (add1 (length (cdr l)))))))
了。但是这个并不明显。
但是我们就必须求得
((lambda (length)
(lambda (l)
(cond
((null? l) 0)
(else (add1 (length (cdr l)))))))
((lambda (mk-length)
((lambda (length)
(lambda (l)
(cond
((null? l) 0)
(else (add1 (length (cdr l)))))))
(mk-length mk-length)))
(lambda (mk-length)
((lambda (length)
(lambda (l)
(cond
((null? l) 0)
(else (add1 (length (cdr l)))))))
(mk-length mk-length)))))
笔记:没看出来这个变换是怎么弄得。
是的。什么时候才是头呢?我们难道不需要求得下面这个表达式的值吗?
((lambda (length)
(lambda (l)
(cond
(( null? l) 0)
(else ( add1 ( length ( cdr l)))))))
((lambda (length)
(lambda (l)
(cond
((null? l) 0)
(else (add1 ( length ( cdr l)))))))
((lambda (mk-length)
((lambda (length)
(lambda (l)
(cond
((null? l) 0)
(else (add1 (length (cdr l)))))))
( mk-length mk-length)))
(lambda (mk-length)
((lambda (length)
(lambda (l)
(cond
((null? l) 0)
(else (add1 (length (cdr l)))))))
(mk-length mk-length)))))
)
是的,没有尽头了啊。怎么回事?
因为我们持续不断的把 mk-length参数传递给它自己,不停的,不停的...
奇怪吗?
因为当我们给 mk-length 传递一个参数,他返回的是一个函数。实际上,它并不介意我们传递它的是什么参数。
但是现在既然我们已经从能够创建 length函数的函数中提取出(mk-length mk-length)来,它就不会返回任何函数了。
它不返回函数,我们该怎么办?
把那个对 mk-length 的应用的最后一个正确的版本转成一个函数。
((lambda (mk-length)
(mk-length mk-length))
(lambda (mk-length)
(lambda (l)
(cond
((null? l) 0)
(else (add1
((mk-length mk-length) (cdr l))))))))
怎么弄?
下面这里是一个不一样的做法。如果f是一个函数的参数,那么(lambda (x) (f x)) 是一个函数的参数吗?
是的。
如果(mk-length mk-length)返回一个函数的参数,那么
(lambda (x)
((mk-length mk-length) x))
返回一个函数的参数吗?
没错。
(lambda (x)
((mk-length mk-length) x))
是一个函数。
好的。现在让我们把这个用在 mk-length对自己的应用上。
((lambda (mk-length)
(mk-length mk-length))
(lambda (mk-length)
(lambda (l)
(cond
((null? l) 0)
(else (add1
((lambda (x)
((mk-length mk-length) x)) (cdr l))))))))
把新的函数移出来,这样我们就可以把 length 移回去。
((lambda (mk-length)
(mk-length mk-length))
(lambda (mk-length)
((lambda (length)
(lambda (l)
(cond
((null? l) 0)
(else (add1
(length (cdr l)))))))
(lambda (x)
((mk-length mk-length) x)))))
这样移动函数有问题吗?
没有问题,我们只是反着来做了一个名字替换值罢了。这里我们提取值然后给它一个名字。
我们可以把盒子里看起来像是 length 的函数提取出来然后给它个名字吗?
可以啊,它并不依赖于 mk-length。
这是正确的函数吗?
((lambda (le)
((lambda (mk-length)
(mk-length mk-length))
(lambda (mk-length)
(le (lambda (x)
((mk-length mk-length) x))))))
(lambda (length)
(lambda (l)
(cond
((null? l) 0)
(else (add1
(length (cdr l))))))))
是的。
我们的到了了什么?
我们把原来的函数 mk-length 抽取出来。
让我们把函数中看起来像 length 的函数分离出来。
简单:
(lambda (le)
((lambda (mk-length)
(mk-length mk-length))
(lambda (mk-length)
(le (lambda (x)
((mk-length mk-length) x))))))
这个函数有名字吗?
有,它被称为Y 算子(Y combinator)。
(define Y
(lambda (le)
((lambda (f) (f f))
(lambda (f)
(le (lambda (x) ((f f) x)))))))
(define ...)又能用了?
能用了,现在我们搞清楚什么是递归了。
你知道为什么Y 算子正常执行吗?
把本章再读一遍你就懂了。
(Y Y)是什么?
谁知道呢,管用就行。
笔记:这里可以参见维基百科的Y算子。讲解很简明很清楚,不像这一章讲的明显效果不好。
你的帽子还合脑袋吗?
头大成了这样子,恐怕不合适了吧。