如何在Haskell中编写递归的lambda表达式?

时间:2011-08-21 21:13:58

标签: haskell recursion lambda

我不确定这是不是很好的编程习惯,但我想知道是否可以使用lambda表达式定义递归函数。

这是我编写的一个人为例子:所以可以递归地定义Haskell中的阶乘函数,如下所示

factorial :: Integer -> Integer 
factorial 1 = 1
factorial (n + 1) = (n + 1) * factorial n

现在,我想要一个f函数f n = (factorial n) + 1。我没有使用factorial n的名称(即事先定义它),而是定义f,其中factorial nf的定义中被赋予lambda表达式。我可以在f中使用递归lambda定义来代替使用名称factorial吗?

4 个答案:

答案 0 :(得分:29)

是的,使用定点函数fix

fact :: Int -> Int
fact = fix (\f n -> if n == 0 then 1 else n * (f (n-1)))

基本上,它没有名称,因为它是一个lambda表达式,所以你将函数作为参数。 Fix将函数“无限地”应用于自身多次:

fix f = f (fix f)

并在Data.Function中定义。

答案 1 :(得分:28)

使用纯lambda表达式进行递归的规范方法是使用 fixpoint combinator ,这是一个具有属性的函数

fixpoint f x = f (fixpoint f) x

如果我们假设存在这样的组合子,我们可以将递归函数写为

factorial = fixpoint (\ff n -> if n == 1 then 1 else n * ff(n-1))

唯一的问题是fixpoint 本身仍然是递归的。在纯lambda演算中,有一些方法可以创建仅由lambda组成的固定点组合器,例如经典的“Y组合器”:

fixpoint f = (\x -> f (x x)) (\x -> f (x x))

但是我们仍然有问题,因为根据Haskell,这个定义并不是很好的类型 - 并且可以证明只有没有办法才能使用lambdas编写一个良好类型的fixpoint组合器和功能应用程序。可以通过使用辅助数据类型来完成某些类型的递归:

data Paradox a = Self (Paradox a -> a)
fixpoint f = let half (Self twin) = f (twin (Self twin))
             in half (Self half)

(请注意,如果删除了单例数据类型的注入和投影,那么这就是Y组合子!)

答案 2 :(得分:1)

为什么我们使用lambda而不是let in

Prelude> (let fib n = if n == 1 then 1 else n * fib(n-1) in fib ) 4
24

答案 3 :(得分:0)

Owen正在使用修复功能

Prelude> fix f = f (fix f)
Prelude> fix (\f n -> if n == 0 then 1 else n * f (n - 1)) 6
720

即使没有修复,无名的Lambda函数也会自动终止。 Owen的fix函数击败了Haskell中常用的修复函数

fix :: (a -> a) -> a
fix f = let {x = f x} in x

两者都允许匿名递归lambda函数