非递归lambda演算因子函数

时间:2017-10-18 22:49:47

标签: lambda functional-programming lambda-calculus

如何在不使用lambda演算的递归的情况下编写阶乘函数?这意味着数学符号不能在任何特定的编程语言中实现。

2 个答案:

答案 0 :(得分:9)

我没有说出任何我不是的意思

通过"不使用递归" 你必须指"没有一个函数自称按名称&#34 ;

无论如何,让我们写一个因子

fact := λn. zero? n one (mult n (fact (dec n)))

现在,在这个例子中,我们并不特别关心zero?multdec,甚至one;您可以自己实现这些 - 我们只想删除粗体,按名称递归,

... fact (dec n)

最简单的方法是将lambda应用于自身 - 遇见 U组合器

U := λf. f f

现在,我们可以将原始表达式包装在lambda中,并应用U

fact := U (λf. λn. zero? n one (mult n (??? (dec n))))

但我们用什么代替fact ??? - 回想一下f是对外部lambda本身的引用,所以为了在下一个"迭代"中使它成为相同的情况,我们必须将f应用于自身,正如 U 所做的那样 - 实际上,你可以将 U 视为一种镜像,并且你的函数可以反射回到那个镜像中以便重现;只有这一次,没有使用名称^ _ ^

fact := U (λf. λn. zero? n one (mult n (f f (dec n))))

是的,是的,但更精明的是会注意到你也可以直接在lambda中使用镜子( U

fact := U (λf. λn. zero? n one (mult n (U f (dec n))))

在没有U的情况下展开

我们知道如何扩展内心 - 我们第一次就这样写了

fact := U (λf. λn. zero? n one (mult n (f f (dec n))))

现在扩展外部U

fact := (λf. λn. zero? n one (mult n (f f (dec n)))) (λf. λn. zero? n one (mult n (f f (dec n))))

有效吗?

所有教堂数字都表示为#N

fact := U λf. λn. zero? n #1 (mult n (U f (dec n)))

fact #4

U (λf. λn. zero? n #1 (mult n (U f (dec n))) #4

(λf. f f) (λf. λn. zero? n #1 (mult n (U f (dec n))) #4

(λf. λn. zero? n #1 (mult n (U f (dec n))) (λf. λn. zero? n #1 (mult n (U f (dec n))) #4

(λn. zero? n #1 (mult n (U (λf. λn. zero? n #1 (mult n (U f (dec n))) (dec n))) #4

zero? #4 #1 (mult #4 (U (λf. λn. zero? n #1 (mult n (U f (dec n))) (dec #4)))

zero? #4 #1 (mult #4 (U (λf. λn. zero? n #1 (mult n (U f (dec n))) (dec #4)))

// (zero? #4); false; returns second argument
(mult #4 (U (λf. λn. zero? n #1 (mult n (U f (dec n))) (dec #4)))

// which is #4 * ...
(U (λf. λn. zero? n #1 (mult n (U f (dec n))) (dec #4))

// which is ...
(U (λf. λn. zero? n #1 (mult n (U f (dec n))) #3)

// which is equivalent to...
fact #3

// so ...
(mult #4 (fact #3))

// repeating this pattern ...
(mult #4 (mult #3 (fact #2))
(mult #4 (mult #3 (mult #2 (fact #1)))
(mult #4 (mult #3 (mult #2 (mult #1 (fact #0))))
(mult #4 (mult #3 (mult #2 (mult #1 #1))))
(mult #4 (mult #3 (mult #2 #1)))
(mult #4 (mult #3 #2))
(mult #4 #6)
#24

在javascript中演示

继续用你自己的眼球观察U组合的力量!



const U =
  f => f (f)
  
const fact =
  U (f => n =>
    n === 0 ? 1 : n * U (f) (n - 1))
    
console.log (fact (4)) // 24




再次作为一个纯粹的lambda表达式



console.log (
  (f => n => n === 0 ? 1 : n * f (f) (n - 1))
    (f => n => n === 0 ? 1 : n * f (f) (n - 1))
      (4)
) // 24




相互递归

上面的代码段演示了此递归过程的一个非常重要的属性:它mutually recursive。正如您所看到的,实际上有两个(尽管是相同的)驱动递归的函数; A调用B,B调用A - 但是在U组合器的情况下,只有一个函数可以应用于它自己,所以它确实启用了direct recursion - lambda做了使用参数调用自己,而不是它的名字(lambdas没有名字可以调用)

Y?

Because I said so

U组合器有点麻烦,因为它希望用户反映"该函数是为了重复 - 如果我们可以为外部lambda提供一个镜像本身的函数呢?

U := λf. f f
Y := λg. U (λf. g (U f))

我会再次向你展示相同的定义,但是在两行上你才能看到"镜像"和他们的立场

          / g, user's function
         /
        /  / outer reflection
       /  /
Y := λg. U (λf.   ...   )
                g (U f)
                 \
                  \ call g with inner reflection

现在,无论何时将Y应用于任何lambda( g ),其参数都将成为计算lambda本身的函数 - 将fact更改为

fact := Y (λf. λn. zero? n one (mult n (f (dec n))))

扩展Y

Y := λg. U (λf. g (U f))             // expand inner U
Y := λg. U (λf. g (f f))             // expand outer U
Y := λg. (λf. g (f f)) (λf. g (f f))

这是你在维基百科中看到的定义;只是alpha重命名

我刚刚发现了一个深刻的发现

从上面得到Y,我看到了我以前从未见过的东西 - 隐藏的B

Y := λg. U (λf. g (U f))

B := λf. λg. λx. f (g x)

Y' := λg. U (B g U)

我见过的最美好事物之一 - and it works too;并不是说我们应该有任何理由认为它不会......

#lang lazy

(define B (λ (f)
            (λ (g)
              (λ (x)
                (f (g x))))))

(define U (λ (f)
            (f f)))

(define Y (λ (g)
            (U ((B g) U))))

(define fact (Y (λ (f)
                  (λ (n)
                    (if (= n 0)
                        1
                        (* n (f (sub1 n))))))))

(fact 4) ;; 24

<强> Haskellers

你见过Y表达过吗?

Y = U . (. U)
  where U f = f f

答案 1 :(得分:1)

如果通过&#34;不使用递归&#34;你的意思是没有一般的递归和 因此,如果没有固定点(或自我应用程序),我们可以简单地观察到阶乘函数是原始递归的(即,本质上是迭代的),并且通过迭代(由教会提供)对原始递归进行非常通用和简单的编码。数字)和对。 我将讨论一般非常有启发性的案例。 让&lt; M,N>是对的一些编码,让fst和snd成为关联的 预测。例如,您可以定义

<M,N> = λz. z M N
fst = λp. p (λxy.x)
snd = λp. p (λxy.y)

假设有一个原始的递归定义(没有参数, 为了简单起见)

f(0) = a
f(x+1) = h(x,f(x))

其中a和h已经定义。

一般的想法是使用辅助函数f&#39;,其中

                       f'(x) = <x,f(x)>

所以,f&#39;(0)=&lt; 0,a&gt;,并给出对p =&lt; x,f(x)> = f&#39;(x),我们建立了 下一对&lt; x + 1,f(x + 1)>通过计算第一个组件的后继者 并将h应用于这对论证(利用我们的 对的编码,简单地相当于将连续h作为对p)的输入传递。

总结,

next = λp.< succ (fst p), p h>

最后,为了计算f(n),我们需要迭代n次函数 从&lt;开始0,a&gt;,然后取第二个组件:

 f = λn. snd (n next <0,a>)