Если я правильно понимаю, что вы делаете, 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)))))
Вы абсолютно можете использовать это прямо в своей функции, определяя «внутреннюю» рекурсивную часть, но это невозможно сделать с помощью простого 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))))
Вы удалили эту ссылку на себя, сделав ее явной, но что вы передаете как 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))
Но вам не обойтись без определения символа для вашей рекурсивной функции*.
Что касается того, почему ваш пример не сработал, то он работает отлично, за исключением того, что он создает функцию, принимая в качестве аргумента функцию (которую я назвал cont
выше) и применяя ваша логика с учетом этой функции loop
. Поскольку у вас нет «цикла» для его передачи (поскольку вы его не связали), он возвращает эту функцию и ничего не делает (кроме того, в ваших возвращенных lambda
, low
и high
не определены).
* Это не совсем так, поскольку вы могли сделать это, используя комбинаторы на вашей лямбде, но это сделало бы его намного сложнее, чем следовало бы:
(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))
Или для еще более уродливой более чистой версии со встроенным комбинатором:
(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))
Которые работают как положено (благодаря слоям лямбда):
(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))
person
val
schedule
23.02.2016
(let ((x E1)) E2)
в((lambda (x) E2) E1)
. Это преобразование не применяется к именованному let. - person molbdnilo   schedule 23.02.2016fix
. :) - person Will Ness   schedule 23.02.2016let
вrec
+lambda
, ноrec
по-прежнему используетletrec
за кулисами. :-) - person Chris Jester-Young   schedule 23.02.2016