具有lambda表达式的阶乘函数

时间:2011-03-05 19:35:20

标签: lambda scheme

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

我的一个学生在伯克利学习了一个Scheme课程,并且给了这个额外的学分问题,即只用lambda表达式创建阶乘函数(没有定义,让或其他权力程序)。我花了一段时间才解决,而且复杂而丑陋。

我现在正在教授Scheme,几年之后,我意识到我将把它作为对自己的挑战,并认为其他人也可能会欣赏它。

6 个答案:

答案 0 :(得分:8)

这是一个(咖喱)版本:

((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))))

答案 1 :(得分:1)

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

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

很难在较小的空间内获得递归。自我应用程序必须在某个地方发生,而像Scheme这样的按值调用语言的大多数独立修复点必须引入额外的lambdas以避免在自我应用程序中失控递归。

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

答案 2 :(得分:0)

我几年前所做的那一行有两倍的线条,而且更难以遵循。

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

答案 3 :(得分:0)

这是我在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)]

答案 4 :(得分:0)

这是我之前做过的一次

 (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,+和 - ,但最后还需要来自教堂数字的来回转换。那时候变得荒谬可笑。

答案 5 :(得分:0)

我首先在无类型lambda演算中编写解决方案,使用 zero? true false 之类的顶级定义,等,使用教会编码定义。这个实现假定多参数函数是curry,并且部分应用了函数(如Haskell)。

教会编码自然数如下:

(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)))

教会布尔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的定义以防止分歧,然后将其添加回来。

这是由此产生的术语。这是相当神秘的(虽然我很难用手写这么短的东西,没有翻译)所以我添加了一些注释来识别我仍然能识别的部分。

(λ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.其他减少策略要么偏离(由于自我应用),要么不会降低到正常形式。