отрицательные нумералы Чёрча
От: Кодт Россия  
Дата: 02.09.15 11:05
Оценка: 10 (1)
Нумерал Чёрча — это функция высшего порядка, применяющая один свой аргумент (произвольную функцию) N раз подряд к другому своему аргументу:
zero f x = x
one  f x = f x
two  f x = f (f x)

Это позволяет очень изящно определить полукольцо над натуральными числами с нулём.
(inc n)    f x = n f (f x)    -- тут я пишу скобки в объявлении, чтобы сделать карринг более наглядным и подчеркнуть суть:
(plus n m) f x = n f (m f x)  -- (inc n), (plus n m), (mult n m) - это нумералы, т.к. они принимают аргументы f и x
(mult n m) f x = n (m f) x    -- а (m f) - функция одного аргумента - тут без карринга пришлось бы тащить лямбды


Но вот с обратными операциями возникают понятные сложности.
  немножко писанины
Обычно декремент делается на сдвиге
(dec n) f x = unwrap (n (patch f) (wrap x))
где
  wrap x       = make-pair undefined x
  (patch f) ab = make-pair (get-second ab) (f (get-second ab))
  unwrap    ab = get-first ab

где, в свою очередь, работа с парой - это тоже функции высшего порядка
  (make-pair a b) q = q a b
  get-first  ab     = ab drop-second
  get-second ab     = ab drop-first
  drop-first  a b = b
  drop-second a b = a

после чего вычитание
(minus1 n) f x = (dec n) f x
(minus2 n) f x = (dec (dec n)) f x
(minus3 n) f x = (dec (dec (dec n))) f x

(minus n m) f x = (m pred n) f x -- m раз применяем декремент к нумералу n


Важный момент: нумералы — по-прежнему изоморфны неотрицательным числам, поэтому декремент нуля не определён.
Обычно там делают идемпотентную заглушку:
wrap x = make-pair x x

что приводит к
(dec zero) f x = unwrap (zero (patch f) (wrap x))
 = get-first (zero (patch f) (make-pair x x))
 = get-first (make-pair x x)
 = x

(dec zero) f x = x = zero f x


Предлагаю размять голову — написать минималистичную реализацию отрицательных нумералов. Что для этого понадобится изменить в дизайне?
Перекуём баги на фичи!
Re: отрицательные нумералы Чёрча
От: A13x США  
Дата: 03.09.15 00:43
Оценка: 15 (1)
Здравствуйте, Кодт, Вы писали:

К>...

К>Предлагаю размять голову — написать минималистичную реализацию отрицательных нумералов. Что для этого понадобится изменить в дизайне?

Нужно по другому определять сами числа, изначальное определение Черчевских нумералов работает только для объединения натуральных чисел и нуля и не имеет смысла для отрицательных чисел (говоря по простому и рассматривая частный случай — с операцией плюс один и нулем никак не получить отрицательного числа).

Можно определить вот так — на Clojure (def — определение нумерала, % — функция с одним аргументом):


;; Boolean logic

(def l-true (% a (% b a)))                ; logical 'true'
(def l-false (% a (% b b)))               ; logical 'false'

(def l-and (% p (% q p q p)))             ; logical 'and'
(def l-or (% p (% q p p q)))              ; logical 'or'
(def l-not (% p p l-false l-true))        ; logical 'not'
(def l-xor (% p (% q p (l-not q) q)))     ; logical 'xor'

(def l-if (% p (% a (% b p a b))))        ; if (p) then (a) else (b)

(def l-negative? (% n n (% x l-false) (% y l-true) l-false))

(def l-positive? (% n n (% x l-true) (% y l-false) l-false))


;;
;; Numerals
;;


;; Zero number
(def zero (% s (% p (% z z))))

;;Naive succ def - it won't work - (def succ (% n (% s (% p (% z s (((n s) p) z))))))

;; predecessor (decrement) for positive numbers only
;;    PRED := λn.λf.λb.λx.n (λg.λh.h (g f)) b (λu.x) (λu.u)
(def pos-pred (% n (% f (% b (% x n (% g (% h h (g f))) b (% u x) (% u u))))))
;; (numcall (pos-pred (succ one)))

;; successor (increment) for negative numbers only
(def neg-succ (% n (% b (% f (% x n b (% g (% h h (g f))) (% u x) (% u u))))))
;; (numcall (neg-succ (pred zero)))
;; (numcall (neg-succ (pred (pred zero))))

;; Successor operation (increment)
(def succ (% n
            (((l-if (l-negative? n)) (neg-succ n))
              (% s (% p (% z (s (((n s) p) z))))))))

;; Predecessor operation (decrement)
(def pred (% n
            (((l-if (l-positive? n)) (pos-pred n))
              (% s (% p (% z (p (((n s) p) z))))))))


Ключевой момент определения нумерала целого числа — включение примитива декремента в определение — обозначен как p вверху.
Это позволяет легко сворачивать нумералы в целые числа — так (ExtendedChurchNumeral inc dec 0) -> IntegerNumber

Для сравнения — для обычных нумералов доступна только свертка в неотрицательные числа — (OriginalChurchNumeral inc 0) -> ZeroOrPositiveIntegerNumber

Полный пример с тестами тут
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.