这是原始形式:
(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))))))
我不知道如何解决这个问题,所以如果有人能帮助我做错了会有很大的帮助。
答案 0 :(得分:3)
如果我正确理解您正在做什么,p
是一个谓词,您根据此分割列表l
,使用聚合函数{{1}聚合您的两个结果列表};在伪代码中:
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)))))
而不是lambda
:函数需要引用自身(因为它是递归的),并且你只能通过为其指定名称。 let
会这样做,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))))
传递了什么?好吧,如果你通过let分配了它,你有一个符号引用它:
cont
或者更简洁地使用(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))
)并在给定函数cont
的情况下应用逻辑。因为你没有任何“循环”。为了传递它(因为你没有绑定它),它返回该函数并继续不执行任何操作(此外,在您返回的loop
中,lambda
和low
不是定义的)。
*这不完全正确,因为你可以在你的lambda上使用combinators来做这件事,但这会使它很多比它应该更复杂 :
high
或者对于偶数 uglier 更纯的版本,使用内联组合器:
(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))
按预期工作(感谢lambda的层次):
(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))
答案 1 :(得分:1)
你可以尝试写
(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
名称,正在定义letrec
取代let
,然后它就可以了,但这不是你在这里问的问题。
由loop
定义的名称let
不在其范围内的init表达式中。这就是let
非递归的含义。它的递归变体letrec
确实提供了被定义的名称,在init-expression中的范围内(只是当它的值不被允许查询时)初始值计算完毕。)
虽然有一个简单的技巧(一种穷人的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)))
并且所有人都在阳光下再次正确,即在lambda体内引用的非递归let
- loop
名称,现在只是一个lambda参数,因此范围。
由于let
是普通的,非递归的,因此很容易用简单的lambda
- 应用程序重写它,因为
(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)))))))