【问题标题】:convert let into lambda in scheme在方案中将 let 转换为 lambda
【发布时间】:2016-02-23 08:50:42
【问题描述】:

这是原始形式:

(define (split-by l p k)
  (let loop ((low '())
             (high '())
             (l l))
    (cond ((null? l)
           (k low high))
          ((p (car l))
           (loop low (cons (car l) high) (cdr l)))
          (else
           (loop (cons (car l) low) high (cdr l))))))
 

我正在尝试转换 let,这是我尝试过的:

(define (split-by l p k)
  (lambda (loop)     
    (cond ((null? l) (k low high))
          ((p (car l)) 
           (loop low (cons (car l) high) (cdr l)))
          (else
           (loop (cons (car l) low) high (cdr l))
           ((low '()) (high '()) (l l))))))

我不知道如何解决这个问题,所以如果有人可以帮助我做错了什么,那将是一个很大的帮助。

【问题讨论】:

  • 我相信您正在考虑将(let ((x E1)) E2) 转换为((lambda (x) E2) E1)。该转换不适用于“命名的 let”。
  • 您可以使用 fix 解决此问题。 :)
  • @molbdnilo You can convert a named let into a rec+lambda,但rec 仍然在幕后使用letrec。 :-)

标签: lambda scheme let named letrec


【解决方案1】:

如果我正确理解你在做什么,p 是一个谓词,你根据这个拆分列表 l,用聚合函数 k 聚合你的两个结果列表;伪代码:

(split-by l p k) => (k {x in l | !p(x)} {x in l | p(x)})

替换let 时的问题是loop 函数是递归定义的。它的形式是:

(define (loop low high lst)
    (cond
        ((null? lst) <some value>)
        (<some predicate> (loop (cons (car lst) low) high (cdr lst)))
        (else (loop low (cons (car lst) high) (cdr lst)))))

您绝对可以在您的函数中直接使用它,定义“内部”递归部分,但是如果没有let,则无法使用简单的lambda:函数需要引用自身 (因为它是递归的),您只能通过为其分配名称来做到这一点。 define 会这样做,let 会让你这样做,但无论你如何转动它,你都需要自我参照。如果您很聪明并通过延续:

(lambda (low high lst cont)
    (cond
        ((null? lst) (agg high lst))
        ((pred? (car lst)) (cont low (cons (car lst) high) (cdr lst) cont))
        (else (cont (cons (car lst) low) high (cdr lst) cont))))

您已经通过显式删除了该自引用,但是您传递的 cont 是什么?好吧,如果你通过 let 分配它,你就会有一个符号来引用它:

(define (split-by2 lst pred? agg)
    (let ((f (lambda (low high lst cont)
                (cond
                    ((null? lst) (agg low high))
                    ((pred? (car lst)) (cont low (cons (car lst) high) (cdr lst) cont))
                    (else (cont (cons (car lst) low) high (cdr lst) cont))))))
        (f '() '() lst f)))

或者更简洁地使用define,它执行完全相同的操作(无需继续传递):

(define (split-by3 lst pred? agg)
    (define (f low high lst)
        (cond
            ((null? lst) (agg low high))
            ((pred? (car lst)) (f low (cons (car lst) high) (cdr lst)))
            (else (f (cons (car lst) low) high (cdr lst)))))
    (f '() '() lst))

所有的操作都类似:

(split-by '(1 2 3 4) (lambda (x) (> x 2)) list)
=> ((2 1) (4 3))   
(split-by2 '(1 2 3 4) (lambda (x) (> x 2)) list)
=> ((2 1) (4 3))   
(split-by3 '(1 2 3 4) (lambda (x) (> x 2)) list)
=> ((2 1) (4 3))

但是你不能为你的递归函数定义一个符号*。

至于您的示例为什么不起作用,它工作得非常好,只是它创建了一个 函数,将 function 作为参数(我称之为 @987654339 @ 以上)并应用您的逻辑给定该功能loop。由于您没有任何“循环”来传递它(因为您没有绑定它),它返回该函数并继续执行任何操作(此外,在您返回的lambdalow 和 @987654343 @ 未定义)。

* 这并不完全正确,因为您可以在您的 lambda 上使用combinators,但这会使它比应有的复杂得多

(define Y
  (lambda (h)
    ((lambda (x) (x x))
     (lambda (g)
       (h (lambda args (apply (g g) args)))))))

(define (split-ycomb lst pred? agg)
    ((Y 
        (lambda(f)
            (lambda (low high l)
                (cond
                    ((null? l) (agg low high))
                    ((pred? (car l)) (f low (cons (car l) high) (cdr l)))
                    (else (f (cons (car l) low) high (cdr l)))))))
    '() '() lst))

或者对于更纯的 uglier 版本,带有内联组合器:

(define (split-ycomb2 lst pred? agg)
    (((lambda (h)
        ((lambda (x) (x x))
            (lambda (g)
                (h (lambda args (apply (g g) args)))))) 
        (lambda(f)
            (lambda (low high l)
                (cond
                    ((null? l) (agg low high))
                    ((pred? (car l)) (f low (cons (car l) high) (cdr l)))
                    (else (f (cons (car l) low) high (cdr l)))))))
    '() '() lst))

按预期工作(感谢 lambda 层):

(split-ycomb '(1 2 3 4) (lambda (x) (> x 2)) list)
=> ((2 1) (4 3))
(split-ycomb2 '(1 2 3 4) (lambda (x) (> x 2)) list)
=> ((2 1) (4 3))

【讨论】:

    【解决方案2】:

    你可以试试写

    (define (split-by l p k)  
      (let ((loop 
              (lambda (low high l)
                 (cond 
                   ((null? l)
                      (k low high))
                   ((p (car l))
                      (loop low (cons (car l) high) (cdr l)))
                   (else
                      (loop (cons (car l) low) high (cdr l)))))))
        (loop '() '() l)))
    

    但问题是 lambda 的主体还不能引用 loop 名称,因为它正在被定义(您可以将 let 替换为 @987654321 @,然后它会起作用,但这不是你在这里问的)。

    let 定义的名称 loop 在它的 init 表达式中不在范围内。这就是let 非递归的含义。它的递归变体letrec 确实提供了被定义​​的名称,在init-expression 内在范围内(只是当init-value 为计算)。

    不过有一个简单的技巧(一种穷人的Y combinator),通过自我应用实现的复制模拟真正的自我引用,如

    (define (split-by l p k)  
      (let ((foo 
              (lambda (loop low high l)
                 (cond 
                   ((null? l)
                      (k low high))
                   ((p (car l))
                      (loop loop low (cons (car l) high) (cdr l)))
                   (else
                      (loop loop (cons (car l) low) high (cdr l)))))))
        (foo foo '() '() l)))
    

    在阳光下一切都好,即非递归 let -- 在 lambda 主体内引用的 loop 名称现在只是一个 lambda 参数,因此 在范围内.

    而且由于let 是普通的、非递归的,因此很容易用一个简单的lambda-application 重写它,因为

    (define (split-by l p k)  
      ((lambda (foo) (foo foo '() '() l))   ; (lambda (loop ...
       (lambda (loop low high l)            ;   is duplicated into the two foos
                 (cond 
                   ((null? l)
                      (k low high))
                   ((p (car l))
                      (loop loop low (cons (car l) high) (cdr l)))
                   (else
                      (loop loop (cons (car l) low) high (cdr l)))))))
    

    【讨论】:

      猜你喜欢
      • 2016-07-21
      • 2016-04-03
      • 1970-01-01
      • 1970-01-01
      • 2023-03-26
      • 2011-09-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多