Y 或 Z 组合器
尽管 Julia 不是一种纯函数式语言,但它完全支持函数式编程的许多基石:一流函数 ,词法范围和闭包 。
定点组合子是在功能的编程的关键组合子。因为朱莉娅有着急切的评价语义(正如许多函数式语言,包括 Julia 深受启发的 Scheme),Curry 的原始 Y-combinator 不会开箱即用:
Y(f) = (x -> f(x(x)))(x -> f(x(x)))
然而,Y 组合子 Z-combinator 的近亲确实会起作用:
Z(f) = x -> f(Z(f), x)
这个组合器接受一个函数并返回一个函数,当用参数 x
调用时,它将自己传递给 x
。为什么函数自身传递会有用?这允许递归而不实际引用函数的名称!
fact(f, x) = x == 0 ? 1 : x * f(x)
因此,Z(fact)
成为阶乘函数的递归实现,尽管在此函数定义中没有可见的递归。 (当然,递归在 Z
组合器的定义中很明显,但这在一种急切的语言中是不可避免的。)我们可以验证我们的函数确实有效:
julia> Z(fact)(10)
3628800
不仅如此,它的速度与我们对递归实现的预期一样快。LLVM 代码演示了将结果编译为普通旧分支,减去,调用和乘法:
julia> @code_llvm Z(fact)(10)
define i64 @"julia_#1_70252"(i64) #0 {
top:
%1 = icmp eq i64 %0, 0
br i1 %1, label %L11, label %L8
L8: ; preds = %top
%2 = add i64 %0, -1
%3 = call i64 @"julia_#1_70060"(i64 %2) #0
%4 = mul i64 %3, %0
br label %L11
L11: ; preds = %top, %L8
%"#temp#.0" = phi i64 [ %4, %L8 ], [ 1, %top ]
ret i64 %"#temp#.0"
}