我不确定这是不是很好的编程习惯,但我想知道是否可以使用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 n
在f
的定义中被赋予lambda表达式。我可以在f
中使用递归lambda定义来代替使用名称factorial吗?
答案 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函数