Лямбда в Ракетке Разъяснения

Я пытаюсь понять использование лямбды в ракетке, и я все еще неясен. Я понимаю, что они неназванные (анонимные) функции, но почему это так хорошо? Мне нужно получить доступ к моим функциям от других функций, так как бы я их назвал??? Пожалуйста, объясните небольшую программу ниже и почему лучше использовать лямбда? Спасибо.

; why is this better than below???
(define test
  (lambda (x)
    (lambda (y)
      (+ x y))))

(define add27
  (test 27))

; what wrong with this???
(define (addTest x)
  (+ x 27))

> (add27 2)
29
> (addTest 2)
29

Ответ 1

В Racket (и других языках функционального программирования) lambda очень полезны, если вы хотите передать встроенную функцию с одним выстрелом в качестве параметра, не определяя его в первую очередь. Например, предположим, что мы хотим скомпоновать список чисел. Мы можем пройти длинный путь и сначала определить функцию square, а затем использовать map:

(define (square x)
  (* x x))

(map square '(1 2 3 4 5))
=> '(1 4 9 16 25)

... Или мы можем просто передать lambda, например:

(map (lambda (x) (* x x))
     '(1 2 3 4 5))

=> '(1 4 9 16 25)

Как вы можете видеть, существуют случаи, когда нам не нужно ссылаться на имя функции. Конечно, если процедура, представленная lambda, будет повторно использована в нескольких частях или если она рекурсивна, тогда имеет смысл дать ей имя (так что оно уже не является анонимным):

(define square
  (lambda (x) (* x x)))

Приведенное выше эквивалентно первому определению square в начале. На самом деле, первое определение - это просто синтаксический сахар, чтобы определить функцию, но в конце все функции являются lambdas!

Теперь посмотрим на ваш пример. Здесь мы используем lambda несколько иначе, а также иллюстрируем, почему они полезны - мы не только определяем функцию, но также возвращаем функцию:

(define test
  (lambda (x)
    (lambda (y)
      (+ x y))))

Возможно, будет немного яснее, если мы напишем его так (это эквивалентно по причинам, упомянутым выше):

(define (test x)
  (lambda (y)
    (+ x y)))

Или даже короче - в Racket мы также можем использовать этот синтаксис для этой же цели:

(define ((test x) y)
  (+ x y))

Это не то, что это лучший (или худший) способ определить функцию - это другое дело! мы определяем процедуру с именем test, которая получает в качестве параметра x и возвращает в результате новую анонимную функцию, которая, в свою очередь, получит в качестве параметра y. Теперь в этих строках:

(define add27
  (test 27))

... мы вызываем test с x значением 27, который возвращает анонимную функцию, и мы называем эту функцию add27. Помните lambda, который был получен как параметр y? теперь lambda был назван add27 - и это пример currying. Подумайте об этом: test - это функция, которая используется для генерации функций, которые добавляют фиксированное значение x к заданному параметру y, что объясняет, почему это работает:

(add27 2)
=> 29

С другой стороны, эта функция всегда добавляет 27 к своему параметру без возможности ее изменить:

(define (addTest x)
  (+ x 27))

(addTest 2)
=> 29

Вы видите разницу? test позволяет нам генерировать новые функции, которые добавляют произвольное значение, тогда как addTest всегда добавляет фиксированное значение 27. Что, если вы хотите добавить say, 100? используя test, это просто:

(define add100 (test 100))

Но addTest нельзя изменить, нам нужно написать новую функцию:

(define (addTest100 x)
  (+ x 100))

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