преобразовать пусть в лямбда в схеме

это исходная форма:

(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))))))

Я не знаю, как это исправить, поэтому, если бы кто-нибудь мог помочь, что я делаю неправильно, это было бы большим подспорьем.


person Fatemeh Rahimi    schedule 23.02.2016    source источник
comment
Я полагаю, вы думаете о преобразовании (let ((x E1)) E2) в ((lambda (x) E2) E1). Это преобразование не применяется к именованному let.   -  person molbdnilo    schedule 23.02.2016
comment
вы исправляете это с помощью fix. :)   -  person Will Ness    schedule 23.02.2016
comment
@molbdnilo Вы можете преобразовать именованный let в rec+lambda, но rec по-прежнему использует letrec за кулисами. :-)   -  person Chris Jester-Young    schedule 23.02.2016


Ответы (2)


Если я правильно понимаю, что вы делаете, 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

можно попробовать написать

(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 на letrec, и тогда это сработает, но это не то, о чем вы здесь спрашиваете).

Имя loop, определяемое let, не входит в область действия внутри выражения инициализации для него. В этом смысл let нерекурсивности. Его рекурсивный вариант, letrec, предусматривает, что определяемое имя должно быть в области видимости внутри выражения инициализации (просто его значение не может быть запрошено при вычислении значения инициализации) .

Однако есть простой трюк (своего рода Y-комбинатор для бедняков), который имитирует истинное "я" -ссылка через репликацию, которая достигается самостоятельным применением, как в

(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 -- имя loop, на которое ссылается тело лямбды, теперь является просто параметром лямбда, поэтому в области действия.

А поскольку 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)))))))
person Will Ness    schedule 23.02.2016