繁体   English   中英

如何在 Racket 中仅使用 lambda 进行递归?

[英]How to make recursion using only lambdas in Racket?

我需要一些帮助来试图弄清楚如何只使用 lambdas 使下面的代码递归。

(define (mklist2 bind pure args)
  (define (helper bnd pr ttl lst)
    (cond [(empty? lst) (pure ttl)]
          [else (define (func t) (helper bnd pr (append ttl (list t)) (rest lst)))
           (bind (first lst) func)])
    )
  (helper bind pure empty args))

给定一个样本fact口头程序 -

(define fact
  (lambda (n)
    (if (= n 0)
        1
        (* n (fact (- n 1)))))) ;; goal: remove reference to `fact`

(print (fact 7)) ; 5040

上面的fact(lambda (n)...) ,当我们调用fact时,我们要求的是这个 lambda,所以我们可以用新的 arguments 重新应用它。 lambda是无名的,如果我们不能使用顶级define绑定,绑定变量的唯一方法是使用 lambda 的参数 想象一下 -

(lambda (r)
  ; ...lambda body...
  ; call (r ...) to recur this lambda
)

我们只需要something来让我们的(lambda (r)...)表现得这样 -

(something (lambda (r)
  (print 1)
  (r)))

; 1
; 1
; 1
; ... forever

介绍你

something非常接近U组合器 -

(define u
  (lambda (f) (f f)))

(define fact
  (lambda (r)     ;; wrap in (lambda (r) ...)
    (lambda (n)
      (if (= n 0)
          1
          (* n ((r r) (- n 1))))))) ;; replace fact with (r r)

(print ((u fact) 7))

; => 5040

现在通过使用参数进行递归,我们可以有效地删除所有define绑定并仅使用lambda编写它 -

; ((u fact) 7)
(print (((lambda (f) (f f))  ; u
         (lambda (r)         ; fact
           (lambda (n)
             (if (= n 0)
                 1
                 (* n ((r r) (- n 1)))))))
        7))

; => 5040

当你可以 Y 时为什么要 U?

U 组合器很简单,但必须在 function 中调用((rr)...)很麻烦。 如果您可以直接调用(r...)来重现,那就太好了。 这正是 Y 组合器所做的 -

(define y
  (lambda (f)
    (f (lambda (x) ((y f) x))))) ;; pass (y f) to user lambda

(define fact
  (lambda (recur)
    (lambda (n)
      (if (= n 0)
          1
          (* n (recur (- n 1))))))) ;; recur directly

(print ((y fact) 7))

; => 5040

但是看看y如何有一个按名称递归定义? 我们可以使用u删除按名称引用并使用lambda参数来代替。 和我们上面做的一样——

(define u
  (lambda (f) (f f)))

(define y
  (lambda (r)      ;; wrap in (lambda (r) ...)
    (lambda (f)
      (f (lambda (x) (((r r) f) x)))))) ;; replace y with (r r)

(define fact
  (lambda (recur)
    (lambda (n)
      (if (= n 0)
          1
          (* n (recur (- n 1)))))))

(print (((u y) fact) 7)) ;; replace y with (u y)

; => 5040

我们现在可以只使用lambda来编写它 -

; (((u y) fact) 7)
(print ((((lambda (f) (f f))   ; u
          (lambda (r)          ; y
            (lambda (f)
              (f (lambda (x) (((r r) f) x))))))
         (lambda (recur)       ; fact
           (lambda (n)
             (if (= n 0)
                 1
                 (* n (recur (- n 1)))))))
        7))

; => 5040

需要更多参数?

通过使用柯里化,如果需要,我们可以扩展我们的函数以支持更多参数 -

(define range
  (lambda (r)
    (lambda (start)
      (lambda (end)
        (if (> start end)
            null
            (cons start ((r (add1 start)) end)))))))

(define map
  (lambda (r)
    (lambda (f)
      (lambda (l)
        (if (null? l)
            null
            (cons (f (car l))
                  ((r f) (cdr l))))))))

(define nums
  ((((u y) range) 3) 9))

(define squares
  ((((u y) map) (lambda (x) (* x x))) nums))

(print squares)
; '(9 16 25 36 49 64 81)

并作为单个lambda表达式 -

; ((((u y) map) (lambda (x) (* x x))) ((((u y) range) 3) 9))
(print (((((lambda (f) (f f)) ; u
           (lambda (r)        ; y
             (lambda (f)
               (f (lambda (x) (((r r) f) x))))))
          (lambda (r)         ; map
            (lambda (f)
              (lambda (l)
                (if (null? l)
                    null
                    (cons (f (car l))
                          ((r f) (cdr l))))))))
         (lambda (x) (* x x))) ; square
        (((((lambda (f) (f f)) ; u
            (lambda (r)        ; y
              (lambda (f)
                (f (lambda (x) (((r r) f) x))))))
           (lambda (r)         ; range
             (lambda (start)
               (lambda (end)
                 (if (> start end)
                     null
                     (cons start ((r (add1 start)) end)))))))
          3)   ; start
         9)))  ; end

; => '(9 16 25 36 49 64 81)

懒惰的

看看这些很酷的y使用惰性实现

#lang lazy

(define y
  (lambda (f)
    (f (y f))))
#lang lazy

(define y
  ((lambda (f) (f f)) ; u
   (lambda (r)
     (lambda (f)
       (f ((r r) f))))))
#lang lazy

(define y
  ((lambda (r)
    (lambda (f)
      (f ((r r) f))))
  (lambda (r)
    (lambda (f)
      (f ((r r) f))))))

为了回应@alinsoar 的回答,我只是想表明,如果您使用Rec类型放置正确的类型注释,Typed Racket 的类型系统可以表达 Y 组合子。

U 组合器的参数需要一个Rec类型:

(: u (All (a) (-> (Rec F (-> F a)) a)))
(define u
  (lambda (f) (f f)))

Y 组合器本身的类型不需要Rec

(: y (All (a b) (-> (-> (-> a b) (-> a b)) (-> a b))))

但是,Y 组合器的定义需要在其中使用的函数之一上使用Rec类型注释:

(: y (All (a b) (-> (-> (-> a b) (-> a b)) (-> a b))))
(define y
  (lambda (f)
    (u (lambda ([g : (Rec G (-> G (-> a b)))])
         (f (lambda (x) ((g g) x)))))))

仅使用 lambda 的递归可以使用定点组合器完成,最简单的是Ω

但是,请考虑到这样的组合器具有无限长的类型,因此如果您使用类型进行编程,则该类型是递归的并且具有无限长。 并非每个类型检查器都能够计算递归类型的类型。 Racket 的类型检查器我认为它是Hindley-Miller ,我记得键入的球拍它不能运行定点组合器,但不确定。 您必须禁用类型检查器才能使其正常工作。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM