为了清楚起见,让我们使用一些省略括号的等式伪代码(因此,我们为调用 (f x y) 编写 f x y,这是明确的):
multirember&Co a lat col
= col [] [] , IF lat == []
= multirember&Co a (cdr lat)
( newlat seen =>
col newlat
(cons (car lat) seen) ) , IF (car lat) == a
= multirember&Co a (cdr lat)
( newlat seen =>
col (cons (car lat) newlat)
seen ) , OTHERWISE
这不就是不言自明的,这是做什么的? :) 还没有? :) 用想象中的模式匹配伪代码(带警卫)再次重写,我们有
multirember&Co = g where
g a [b, ...lat] col | b == a = g a lat ( n s => col n [b, ...s] )
| else = g a lat ( n s => col [b, ...n] s )
g a [] col = col [] []
模式匹配的语义应该很明显:[b, ...lat] 匹配 [1,2,3] 其中b = 1 和lat = [2,3]。因此,这只是一个三种情况的方程:
换句话说,我们正在处理从递归调用返回的两个结果,如果头部是 a,则将头部添加到第二个结果,或者如果不是,则将头部添加到第一个。
因此调用
(g 1 [ 2, 1, 3, 1, 4, 5 ] col)
与调用相同(将导致)
(col [ 2, ...[3, ...[4, ...[5, ...[]]]]]
[ 1, ...[1, ...[]] ])
即
(col [ 2, 3, 4, 5 ]
[ 1, 1 ])
另一种看待它的方式是,以下是另一种等效的表述:
multirember&Co a lat col = g a lat id id where
id x = x ; identity function
(f ∘ g) x = f (g x) ; function composition
g a [b, ...lat] c d
| b == a = g a lat c (d ∘ (x => cons b x)) ; (d ∘ {cons b})
| else = g a lat (c ∘ (x => cons b x)) d ; (c ∘ {cons b})
g a [] c d = col (c []) (d [])
因此
multirember&Co 1 [ 2, 1, 3, 1, 4, 5 ] col
=
col (((((id ∘ {cons 2}) ∘ {cons 3}) ∘ {cons 4}) ∘ {cons 5}) []) ; { } is for
( ( (id ∘ {cons 1}) ∘ {cons 1} ) []) ; partial application
=
col (id (cons 2 (cons 3 (cons 4 (cons 5 [])))))
(id (cons 1 (cons 1 []) ) )
这显然是一回事。
在另一个伪代码(带有列表推导)中,这表明它自己是
multirember&Co a lat col
= col [ b for b in lat if (b /= a) ]
[ b for b in lat if (b == a) ]
= ( ((n,s) => col n s) ∘ {partition {/= a}} ) lat
除了列表lat 的一次 遍历(在原始代码中),有效地构建模仿原始列表结构的嵌套 lambda 函数链;然后评估哪个链来创建两个结果,将它们传递给最顶层的收集器函数col。
所有这一切都向我们展示了Continuation-Passing Style(就是这样)的强大功能,它实际上创建了自己的函数调用协议,例如从每个递归函数传回两个结果调用,即使通常在 lambda 演算中,一个函数只能有 一个 结果(即使是一对)。