【问题标题】:factorial function with just lambda expression只有 lambda 表达式的阶乘函数
【发布时间】:2011-03-05 19:35:19
【问题描述】:

仅使用 lambda 表达式,您可以自己创建的最透明、最优雅的阶乘函数是什么?

我的一个学生在伯克利上了一堂 Scheme 课,并获得了这个额外的学分问题,即仅使用 lambda 表达式(没有定义、让或其他幂过程)创建阶乘函数。我花了一段时间才解决,又复杂又丑。

几年后,我现在正在教 Scheme,我意识到我要把它作为对自己的挑战,并认为其他人也会喜欢它。

【问题讨论】:

  • 你可以使用数字吗?布尔值(ifcond)?
  • 是否允许将let 和/或let* 用作((lambda 成语的缩写?这不会简化问题,但会使生成的代码更清晰。
  • 好问题。必须允许“if”和“cond”,否则在停止条件下不会停止。同意 let 和 let* 是 lambda 语法糖。我正在寻找一个优雅的尾递归简约解决方案。你总是可以同时做这两个;我想我也会。命名为 let 和 letrec 肯定已经淘汰了。
  • 您是否正在寻找一个将lets 和defines 转换为lambdas 的普通阶乘函数?
  • 这里有一个更难的挑战:不使用 if、cond 或数字。将数字 0 表示为 (lambda (f x) x),数字 1 表示为 (lambda (f x) (f x)),数字 2 表示为 (lambda (f x) (f (f x))),通常数字 ' n'作为消耗'f'和'x'并将'f'应用于'x''n'次的函数。这些被称为“教会数字”。在这个系统中,您可以在没有任何 if、cond、数字等的情况下实现“阶乘”。只需 lambda、函数调用和变量 refs。如果您已经熟悉这个(一组)问题,我们深表歉意。

标签: lambda scheme


【解决方案1】:

这是一个(咖喱)版本:

((lambda (x) (x x))
 (lambda (fact-gen)
   (lambda (n)
     (if (zero? n)
         1
         (* n ((fact-gen fact-gen) (sub1 n)))))))

尾递归版本:

(let ((fact-gen
       (lambda (fact-gen n acc)
         (if (zero? n)
             acc
             (fact-gen fact-gen (sub1 n) (* n acc))))))
  (lambda (n) (fact-gen fact-gen n 1)))

关于教堂数字:

(let* ((one (lambda (s z) (s z)))
       (add1 (lambda (n) (lambda (s z) (s (n s z)))))
       (* (lambda (a b) (lambda (s z) (a (lambda (z2) (b s z2)) z))))
       (cons (lambda (a b) (lambda (f) (f a b)))))
  (lambda (n)
    ((n (lambda (p)
          (p (lambda (count acc)
               (cons (add1 count) (* count acc)))))
        (cons one one))
     (lambda (a b) b))))

【讨论】:

  • 柯里化解决方案违反了问题的“自行”部分。对于这个问题,我预计解决方案的旅程将是最有趣的部分。
  • @Tom:我认为我的很重要;我使用我记得的技术来编写函数,而无需查找它。
  • 那你赢了。做得好。我和包括你在内的其他人提出的尾递归和进一步简化的想法会很有趣
  • @Tom:我添加了其他两个版本(尾递归和教堂数字)。
  • 当我在 SICP 中第一次看到教堂数字时,我头疼。
【解决方案2】:

这是我能想到的最简单的尾递归版本:

(lambda (n)
  (((lambda (!) (! !))
    (lambda (!)
      (lambda (n acc)
        (if (zero? n)
            acc
            ((! !) (sub1 n) (* n acc))))))
   n 1))

很难在更小的空间内获得递归。自应用程序必须在某个地方发生,并且像 Scheme 这样的按值调用语言中的大多数独立固定点都必须引入额外的 lambdas 以避免在自应用程序处出现失控的递归。

相反,我的解决方案和 Jeremiah 将自我应用程序隐藏在 Scheme 的短路 if 的一个分支中,从而以更少的字符提供必要的递归。

【讨论】:

  • 非常好。你的代码和解释做得很好。我怀疑你对这个最小化是正确的,但我会用更少的 lambdas(稍后)。我会提高你的答案,但还不能
【解决方案3】:

我几年前做的那个有两倍多的台词,而且更难理解。

(lambda (n)
  ((lambda (fact) (fact fact 1 n))
   (lambda (f P n) (if (<= n 1) P (f f (* n P) (- n 1))))))

【讨论】:

    【解决方案4】:

    这是我之前在绕着 Y-Combinator 时编写的代码。

    [λ (n) 
        ;; Y combinator (specialized to two arguments)
        (([λ (rec-func)
            ([λ (procedure)
               (rec-func [λ (arg1 arg2) ((procedure procedure) arg1 arg2)])]
             [λ (procedure)
               (rec-func [λ (arg1 arg2) ((procedure procedure) arg1 arg2)])])]
        ;; The factorial function (tail recursive)
         [λ (func-arg)
               [λ (n tot)
                 (if (zero? n)
                     tot
                     (func-arg (- n 1) (* n tot)))]]) 
         n 1)]
    

    【讨论】:

      【解决方案5】:

      我首先在无类型的 lambda 演算中编写了解决方案,使用诸如 zero?truefalse 之类的顶级定义,等,使用 Church 编码定义。这个实现假设多参数函数被柯里化并且函数被部分应用(如 Haskell)。

      Church 编码自然数如下:

      (define 0  λf x. x)
      (define 1  λf x. f x)
      (define 2  λf x. f (f x))
      (define 3  λf x. f (f (f x)))
      

      Church 布尔值 truefalse 定义如下

      (define const  λx y. x)
      (define U      λf. f f)
      (define true   λt f. t)
      (define false  λt f. f)
      (define succ   λn f x. f (n f x))
      (define 0      λf x. x)
      (define *      λm n f x. m (n f) x)
      (define zero?  λn. n (const false) true)
      (define pred   λn f x. n (λg h. h (g f)) (const x) id)
      

      定义了这些先决条件后,现在我们使用自应用递归定义阶乘函数。这个定义是尾递归的。

      (define !
        U (lambda loop acc n.
            zero? n -- branches wrapped in lambdas
                    -- to accomodate call-by-value
             (lambda _. acc)
             (lambda _. (loop loop (* n acc) (pred n))))
             n) -- dummy argument to evaluate selected branch
          1)
      

      从这里开始,我在!上作弊并进行了正常的订单评估;这基本上是部分评估。为此,我必须删除 U 的定义以防止分歧,然后将其添加回来。

      这是生成的术语。它相当神秘(尽管如果没有解释器,我很难手写这么短的东西)所以我添加了 cmets 来识别我仍然可以识别的部分。

      (λx. x x)             -- self application
      (λloop acc n.
        n (λy t f. f)       -- const false
          (λt f. t)         -- true
          (λ_. acc)         -- zero? branch
          (λ_. loop loop    -- other branch
            (λf. n (acc f))
            (λf x. n (λg h. h (g f)) (λy. x) (λx. x)))  -- pred
          n)  -- dummy argument
      (λf. f) -- 1
      

      乘法可能很难发现,但它就在那里。现在,为了测试它,我评估了应用于 3 或 (λf x. f (f (f x))) 的术语。混合应用和混合正态评估都归约到一个正态项而不发散,产生λf x. f (f (f (f (f (f x))))),或 6。其他归约策略要么发散(由于自我应用),要么不归约到范式。

      【讨论】:

        【解决方案6】:

        这是我不久前做过的一个

         (define fibs
          (lambda (idx)
            ((((lambda (f)
                    ((lambda (x) (x x))
                     (lambda (y) (f (lambda (a)
                                      (lambda (b) (((y y) a) b)))))))
               (lambda (f) (lambda (s)
                             (lambda (idx)
                               (if (= idx 0)
                                   ((lambda (p)
                                      (p (lambda (h) (lambda (t) h)))) s)
                                   ((f (((lambda (p)
                                           (p (lambda (h) (lambda (t) t)))) s)))
                                    (- idx 1)))))))
              ((((lambda (f)
                    ((lambda (x) (x x))
                     (lambda (y) (f (lambda (a)
                                      (lambda (b) (((y y) a) b)))))))
                 (lambda (f) 
                   (lambda (a)
                     (lambda (b)
                       (((lambda (h)
                           (lambda (t) (lambda (a) ((a h) t)))) a)
                        (lambda () ((f b) (+ a b)))))))) 0) 1))
             idx)))  
        

        它定义了所有的斐波那契数(当然是通过无限的教堂对列表)

        我确实更进一步,去掉了 if、0、1、+ 和 -,但最终还是需要这些来从教堂数字来回转换。那时它变得越来越荒谬。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-31
          • 1970-01-01
          • 2011-06-11
          相关资源
          最近更新 更多