факториальная функция с просто лямбда-выражением - PullRequest
5 голосов
/ 05 марта 2011

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

Один из моих учеников взял урок Scheme в Беркли, и ему была дана дополнительная кредитная задача создания факториальной функции только с лямбда-выражениями (без определения, допуска или других процедур питания). Мне потребовалось некоторое время, чтобы решить, и было сложно и некрасиво.

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

Ответы [ 6 ]

8 голосов
/ 05 марта 2011

Вот одна (карри) версия:

((lambda (x) (x x))
 (lambda (fact-gen)
   (lambda (n)
     (if (zero? n)
         1
         (* n ((fact-gen fact-gen) (sub1 n)))))))

Хвостовая рекурсивная версия:

(let ((fact-gen
       (lambda (fact-gen n acc)
         (if (zero? n)
             acc
             (fact-gen fact-gen (sub1 n) (* n acc))))))
  (lambda (n) (fact-gen fact-gen n 1)))

На церковных цифрах:

(let* ((one (lambda (s z) (s z)))
       (add1 (lambda (n) (lambda (s z) (s (n s z)))))
       (* (lambda (a b) (lambda (s z) (a (lambda (z2) (b s z2)) z))))
       (cons (lambda (a b) (lambda (f) (f a b)))))
  (lambda (n)
    ((n (lambda (p)
          (p (lambda (count acc)
               (cons (add1 count) (* count acc)))))
        (cons one one))
     (lambda (a b) b))))
1 голос
/ 06 марта 2011

Вот самая простая хвостовая рекурсивная версия, которую я могу придумать:

(lambda (n)
  (((lambda (!) (! !))
    (lambda (!)
      (lambda (n acc)
        (if (zero? n)
            acc
            ((! !) (sub1 n) (* n acc))))))
   n 1))

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

Вместо этого мое решение и Иеремия скрывают самоприменение в одной ветви короткого замыкания Схемы if, давая необходимую рекурсию с гораздо меньшим количеством символов.

0 голосов
/ 24 марта 2013

Я впервые написал решение в нетипизированном лямбда-исчислении, используя определения верхнего уровня для таких вещей, как ноль? , true , false и т. Д., Определеноиспользуя церковные кодировки.В этой реализации предполагается, что функции с несколькими аргументами каррируются и функции частично применяются (например, Haskell).

Церковное кодирование натуральных чисел выглядит следующим образом:

(define 0  λf x. x)
(define 1  λf x. f x)
(define 2  λf x. f (f x))
(define 3  λf x. f (f (f x)))

Церковные логические значения true и false определены ниже

(define const  λx y. x)
(define U      λf. f f)
(define true   λt f. t)
(define false  λt f. f)
(define succ   λn f x. f (n f x))
(define 0      λf x. x)
(define *      λm n f x. m (n f) x)
(define zero?  λn. n (const false) true)
(define pred   λn f x. n (λg h. h (g f)) (const x) id)

После того, как эти предварительные условия определены, теперь мы определяем факториальную функцию, используя самоприменение для рекурсии.Это определение является хвостово-рекурсивным.

(define !
  U (lambda loop acc n.
      zero? n -- branches wrapped in lambdas
              -- to accomodate call-by-value
       (lambda _. acc)
       (lambda _. (loop loop (* n acc) (pred n))))
       n) -- dummy argument to evaluate selected branch
    1)

Отсюда я обманул и выполнил обычную оценку порядка на !;это по сути частичная оценка.Чтобы это работало, мне пришлось удалить определение U, чтобы предотвратить расхождение, а затем добавить его обратно после.

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

(λx. x x)             -- self application
(λloop acc n.
  n (λy t f. f)       -- const false
    (λt f. t)         -- true
    (λ_. acc)         -- zero? branch
    (λ_. loop loop    -- other branch
      (λf. n (acc f))
      (λf x. n (λg h. h (g f)) (λy. x) (λx. x)))  -- pred
    n)  -- dummy argument
(λf. f) -- 1

Умножение может бытьтрудно заметить, но это есть.Теперь, чтобы проверить это, я оценил термин, примененный к 3, или (λf x. f (f (f x))).Гибридная аппликативная и гибридная нормальная оценка приводят к нормальному сроку без отклонения, приводя к λf x. f (f (f (f (f (f x))))) или 6. Другие стратегии сокращения либо расходятся (из-за самостоятельного применения), либо не приводят к нормальной форме.

0 голосов
/ 24 марта 2013

Вот один, который я сделал некоторое время назад

 (define fibs
  (lambda (idx)
    ((((lambda (f)
            ((lambda (x) (x x))
             (lambda (y) (f (lambda (a)
                              (lambda (b) (((y y) a) b)))))))
       (lambda (f) (lambda (s)
                     (lambda (idx)
                       (if (= idx 0)
                           ((lambda (p)
                              (p (lambda (h) (lambda (t) h)))) s)
                           ((f (((lambda (p)
                                   (p (lambda (h) (lambda (t) t)))) s)))
                            (- idx 1)))))))
      ((((lambda (f)
            ((lambda (x) (x x))
             (lambda (y) (f (lambda (a)
                              (lambda (b) (((y y) a) b)))))))
         (lambda (f) 
           (lambda (a)
             (lambda (b)
               (((lambda (h)
                   (lambda (t) (lambda (a) ((a h) t)))) a)
                (lambda () ((f b) (+ a b)))))))) 0) 1))
     idx)))  

Он определяет все числа Фибоначчи (конечно, через бесконечный список церковных пар)

Я пошел еще дальше, избавляясь от if, 0, 1, + и -, но, в конце концов, они были необходимы для преобразования туда и обратно от церковных цифр в любом случае.И в этот момент это становилось смешным.

0 голосов
/ 06 марта 2011

Вот мой код, который я кодировал раньше, когда оборачивал голову вокруг Y-Combinator.

[λ (n) 
    ;; Y combinator (specialized to two arguments)
    (([λ (rec-func)
        ([λ (procedure)
           (rec-func [λ (arg1 arg2) ((procedure procedure) arg1 arg2)])]
         [λ (procedure)
           (rec-func [λ (arg1 arg2) ((procedure procedure) arg1 arg2)])])]
    ;; The factorial function (tail recursive)
     [λ (func-arg)
           [λ (n tot)
             (if (zero? n)
                 tot
                 (func-arg (- n 1) (* n tot)))]]) 
     n 1)]
0 голосов
/ 06 марта 2011

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

(lambda (n)
  ((lambda (fact) (fact fact 1 n))
   (lambda (f P n) (if (<= n 1) P (f f (* n P) (- n 1))))))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...