【问题标题】:How to write recursive lambda expression in Haskell?如何在 Haskell 中编写递归 lambda 表达式?
【发布时间】:2011-10-31 18:11:16
【问题描述】:

我不确定这是否是一种好的编程习惯,但我想知道是否可以使用 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 定义来代替使用名称阶乘吗?

【问题讨论】:

  • 为什么不给它起个名字呢?它不必是全局名称,有letwhere
  • 顺便说一句,请注意,factorial 定义中使用的n+k 模式是removed from the language as of Haskell 2010。建议停止使用它们。
  • 附注我更喜欢基本情况 0:factorial 0 = 1,这将允许它为多个数字工作
  • @delnan:我猜你不会喜欢我的代码,因为几乎所有代码都导入了Data.Function (fix)

标签: haskell recursion lambda


【解决方案1】:

欧文很擅长修复功能

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

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

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

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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • let 仍然需要命名函数。
    【解决方案3】:

    是的,使用定点函数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中定义。

    【讨论】:

      【解决方案4】:

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

      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 来编写一个类型良好的定​​点组合器和功能应用。它可以通过使用辅助数据类型来完成某种类型的递归:

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

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

      【讨论】:

      • 虽然没有排版,但还是可以玩得开心! factorial, factorial 3 :)
      • 这里有理由使用data,还是newtype 也能正常工作(更好)?
      • @dfeuer:@dfeuer:newtype 可能会更好——我在写答案时并没有意识到该语言功能。
      猜你喜欢
      • 2014-06-22
      • 1970-01-01
      • 1970-01-01
      • 2016-03-19
      • 1970-01-01
      • 1970-01-01
      • 2023-03-29
      • 2014-04-08
      • 1970-01-01
      相关资源
      最近更新 更多