Смущает преобразование let в лямбда

В настоящее время я прохожу этот отличная статья о Y-комбинаторе от Майк Ванье. В пояснении опущена следующая строка:

It turns out that any let expression can be converted into an equivalent lambda expression using this equation:

(let ((x <expr1>)) <expr2>) ==> ((lambda (x) <expr2>) <expr1>)

Статья иллюстрирует это утверждение путем преобразования:

(define (part-factorial self)
  (let ((f (self self)))
    (lambda (n)
      (if (= n 0)
        1
        (* n (f (- n 1)))))))

к:

(define (part-factorial self)
  ((lambda (f)
    (lambda (n)
      (if (= n 0)
        1
        (* n (f (- n 1))))))
  (self self)))

Теперь я понимаю, как и почему два приведенных выше фрагмента кода идентичны, хотя я не могу понять тот факт, что общее уравнение для преобразования let в lambda:

(let ((x <expr1>)) <expr2>)
==> ((lambda (x) <expr2>) <expr1>)

Буду признателен за подробное объяснение.

Связанный.
user202729 21.03.2018 11:48

re: эпиграф статьи, по-видимому, это Джон фон Нейман сказал: «В математике ничего не понимаешь. Просто привыкаешь».

Will Ness 21.03.2018 18:19
Сортировка hashmap по значениям
Сортировка hashmap по значениям
На Leetcode я решал задачу с хэшмапой и подумал, что мне нужно отсортировать хэшмапу по значениям.
3
2
1 490
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Я сам в этом разобрался =).

То, что мне не хватало, это то, что в:

(let ((x <expr1>)) <expr2>)
==> ((lambda (x) <expr2>) <expr1>)

x присутствует где-то внутри <expr2>, так что это больше похоже на:

(let ((x <expr1>)) <expr-containing-x>)
==> ((lambda (x) <expr-containing-x>) <expr1>)

С учетом сказанного, если мы заменим x на f в:

(define (part-factorial self)
  (let ((f (self self)))
    (lambda (n)
      (if (= n 0)
        1
        (* n (f (- n 1)))))))

а также в:

(define (part-factorial self)
  ((lambda (f)
    (lambda (n)
      (if (= n 0)
        1
        (* n (f (- n 1))))))
  (self self)))

и выделит x, <expr1> и <expr2> разными цветами, тогда формула преобразования должна стать понятной:

x, <expr1> and <expr2> highlighted

x не нужно нигде использовать - (let ((x 0)) 99) такой же, как ((lambda (x) 99) 0).
molbdnilo 21.03.2018 16:32

для меня все наоборот: (x => ...x...) val"средства"let {x = val} in ...x... , который не требует пояснений («открыть новый фрейм окружения; создать в нем место с именем x; поместить в это место значение val; продолжить ...»).

Will Ness 22.03.2018 20:26

В примерах следует использовать более значимые имена. Не могло бы вас смутить это: ((lambda (param) body ...) arg) <--> (let ((param arg)) body ...)?

Kaz 16.04.2018 02:40

Вы должны представить себе, что у вас очень мало шепелявого языка, в котором есть lambda, но нет let. Ты хочешь сделать:

(let ((nsq (square n)))
  (+ nsq nsq))

Вы знаете, что nsq - это новая переменная, и что тело let можно сделать функцией:

(lambda (nsq) (+ nsq nsq))

Затем вам нужно использовать это, чтобы получить то же значение:

((lambda (nsq) (+ nsq nsq)) (square n))

Представьте, что ваша простая схема содержит макросы и, следовательно, вы реализуете ее как let:

(define-syntax let
  (syntax-rules ()
    [(let ((binding value) ...)
       body ...)
     ((lambda (binding ...)
        body ...)
      value ...)]))

Обратите внимание, что во многих реализациях это действительно происходит именно так.

Ответ принят как подходящий

let позволяет вам открыть новую среду, в которой доступны переменные. В терминах языка программирования мы говорим, что это «открывает новый фрейм».

Когда вы пишете (let ((x 42)) <body>), вы создаете фрейм, в котором x доступен внутри <body>, и присваиваете ему значение 42.

Что ж, есть еще один инструмент, который позволяет открывать новые кадры. Фактически, это обычно основной кирпич, из которого вы можете создавать более абстрактные конструкции: он называется lambda.

lambda открывает новый фрейм, в котором его аргументы доступны его телу. Когда вы пишете (lambda (x) <body>), вы делаете x доступным для <body> функции.

Единственное различие между lambda и let состоит в том, что let немедленно присваивает значение x, тогда как lambda ожидает значение в качестве аргумента.

Следовательно, если вы хотите обернуть <body> напрямую присвоенным значением с помощью lambda, вам просто нужно передать это значение!

((lambda (x) <body>) 42)

Что делает его в точности эквивалентным:

(let ((x 42)) <body>)

Другие вопросы по теме