Арифметика с церковными цифрами - PullRequest
18 голосов
/ 12 октября 2010

Я работаю через SICP, и проблема 2.6 поставила меня в затруднительное положение.При работе с церковными числами концепция кодирования нуля и 1 в качестве произвольных функций, которые удовлетворяют определенным аксиомам, кажется, имеет смысл.Кроме того, вывод прямой формулировки индивидуальных чисел с использованием определения нуля и функции add-1 имеет смысл.Я не понимаю, как может быть сформирован оператор плюс.

Пока у меня есть это.

(define zero (lambda (f) (lambda (x) x)))
(define (add-1 n)
  (lambda (f) (lambda (x) (f ((n f) x)))))

(define one (lambda (f) (lambda (x) (f x))))
(define two (lambda (f) (lambda (x) (f (f x)))))

Просматривая запись в википедии для лямбда-исчисление , я нашелчто определение плюс было PLUS: = λmnfx.mf (nfx).Используя это определение, я смог сформулировать следующую процедуру:

(define (plus n m)
  (lambda (f) (lambda (x) ((m f) ((n f) x)))))

Что я не понимаю, так это то, как эта процедура может быть получена напрямую, используя только информацию, предоставленную ранее полученными процедурами.Может ли кто-нибудь ответить на это в какой-то строгой форме доказательства?Интуитивно, я думаю, я понимаю, что происходит, но, как однажды сказал Ричард Фейнман: «Если я не могу это построить, я не могу этого понять ...»

Ответы [ 3 ]

14 голосов
/ 12 октября 2010

Это на самом деле довольно просто.Это, вероятно, будет рассматриваться как приманка для пламени, но из-за паренов сложнее это увидеть - лучший способ увидеть, что происходит, это либо представить, что вы на языке карри, либо просто использовать тот факт, что в Scheme есть функции с несколькими аргументами ипринять это ... Вот объяснение, которое использует лямбды и множественные аргументы, где это удобно:

  • Каждое число N кодируется как

    (lambda (f x) ...apply (f (f (f ... (f x)))) N times...)
    
  • Это означает, что кодировка N на самом деле

    (lambda (f x) (f^N x))
    

    , где f^N - это функциональное возведение в степень.

  • Более простой способ сказать это (при условии каррирования):число N кодируется как

    (lambda (f) f^N)
    

    , так что N на самом деле является "повышением до степени N" функция

  • Теперь возьмите свое выражение(заглядывая внутрь lambda с):

    ((m f) ((n f) x))
    

    , поскольку n - это кодировка числа, это возведение в степень, так что это на самом деле:

    ((m f) (f^n x))
    

    и то же самое для m:

    (f^m (f^n x))
    

    , а остальное должно быть очевидно ... Йоу вас есть m приложений f, примененных к n приложений f, примененных к x.

  • Наконец, чтобы оставить некоторые удовольствия- вот еще один способ определить plus:

    (define plus (lambda (m) (lambda (n) ((m add1) n))))
    

    (Ну, не слишком весело, так как этот, вероятно, более очевиден.)

3 голосов
/ 26 декабря 2014

(Убедитесь, что вы понимаете функции высшего порядка ) . В Алонзо-Черч * нетипизированное лямбда-исчисление функция является единственным примитивным типом данных.Здесь нет чисел, логических значений, списков или чего-либо еще, только функции.Функции могут иметь только 1 аргумент, но функции могут принимать и / или возвращать функции - не значения этих функций, а сами функции.Поэтому, чтобы представлять числа, логические значения, списки и другие типы данных, вы должны придумать способ, чтобы анонимные функции могли их поддерживать. Церковные цифры - это способ представления натуральных чисел .Три наиболее примитивных конструкции в нетипизированном лямбда-исчислении:

  1. λx.x, тождественная функция , принимает некоторую функцию и немедленно возвращает ее.
  2. λx.x x, self-application.
  3. λf.λx.f x, application application, принимает функцию и аргумент и применяет функцию к аргументу.

Как вы кодируете 0, 1,2 как ничего кроме функций?Нам как-то нужно встроить понятие количество в систему.У нас есть только функции, каждая функция может быть применена только к 1 аргументу.Где мы можем увидеть что-то похожее на количество?Эй, мы можем применить функцию к параметру несколько раз!Очевидно, что в трех повторяющихся вызовах функции есть ощущение количества: f (f (f x)).Итак, давайте закодируем его в лямбда-исчислении:

  • 0 = λf.λx.x
  • 1 = λf.λx.f x
  • 2 = λf.λx.f (f x)
  • 3 = λf.λx.f (f (f x))

И так далее.Но как вы идете от 0 до 1 или от 1 до 2?Как бы вы написали функцию, которая, учитывая число, вернула бы число, увеличенное на 1?Мы видим шаблон в церковных цифрах, что термин всегда начинается с λf.λx., и после того, как у вас есть конечное повторное применение f , поэтому нам нужно каким-то образом попасть в тело λf.λx. и обернуть его вдругой f.Как изменить тело абстракции без сокращения?Ну, вы можете применить функцию, обернуть тело в функцию, а затем обернуть новое тело в старую лямбда-абстракцию.Но вы не хотите, чтобы аргументы менялись, поэтому вы применяете абстракции к значениям с тем же именем: ((λf.λx.f x) f) x → f x, но ((λf.λx.f x) a) b) → a b, а это не то, что нам нужно.

Вот почему add1 - этоλn.λf.λx.f ((n f) x): вы применяете n к f, а затем x, чтобы уменьшить выражение для тела, затем применяете f к этому телу, затем снова абстрагируете его с помощью λf.λx.. Упражнение: также убедитесь, что это правда, быстро выучите β-уменьшение и уменьшите (λn.λf.λx.f ((n f) x)) (λf.λx.f (f x)) до шага 2 на 1.

Теперь вы понимаете интуицию, лежащую в основе обертывания телав другой вызов функции, как мы реализуем сложение 2 чисел?Нам нужна функция, которая, учитывая λf.λx.f (f x) (2) и λf.λx.f (f (f x)) (3), вернет λf.λx.f (f (f (f (f x)))) (5).Посмотрите на 2. Что если бы вы могли заменить его x телом 3, то есть f (f (f x))?Очевидно, чтобы получить тело из 3, просто примените его к f, а затем x.Теперь примените 2 к f, но затем примените его к телу 3, а не к x.Затем снова оберните его в λf.λx.: λa.λb.λf.λx.a f (b f x).

Заключение: Чтобы сложить вместе 2 числа a и b, оба из которых представлены как церковные цифры, выхотите заменить x в a телом b, чтобы f (f x) + f (f (f x)) = f (f (f (f (f x)))).Чтобы это произошло, примените a к f, затем к b f x.

2 голосов
/ 13 мая 2014

Ответ Илии технически верен, но, поскольку на момент, когда этот вопрос задают, процедура #apply не была введена, я не думаю, что авторы предполагали, что ученик знал об этом или о таких понятиях, как карри чтобы иметь возможность ответить на этот вопрос.

Они в значительной степени направляют одного к ответу, предлагая применить метод подстановки, а затем следует заметить, что эффект сложения - это сложение одного числа с другим. Композиция - это понятие, введенное в упражнении 1.42; и это все, что требуется для понимания того, как аддитивная процедура может работать в этой системе.

; The effect of procedure #add-1 on `one`, and `two` was the composition of `f` 
; onto `one` and `f` onto `two`.
;
; one   : (λ (f) (λ (x) (f x)))
; two   : (λ (f) (λ (x) (f (f x))))
; three : (λ (f) (λ (x) (f (f (f x)))))
;
; Thus one may surmise from this that an additive procedure in this system would
; work by composing one number onto the other.
;
; From exercise 1.42 you should already have a procedure called #compose.
;
; With a little trial and error (or just plain be able to see it) you get the
; following solution.

(define (adder n m)
  (λ (f)
    (let ((nf (n f))
          (mf (m f)))
      (compose nf mf))))
...