Вы используете выделки для функций нескольких параметров. Это не очень естественный способ выразить вещи в Свифте, и это усложняет ситуацию. (Swift is not a functional programming language.)
Как указано в вашей статье, «все церковные цифры являются функциями, которые принимают два параметра». Так и сделайте это. Сделайте это двумя параметрами.
typealias Church = (_ f: ((Int) -> Int), _ x: Int) -> Int
Это функция, которая принимает два параметра, функцию и ее аргумент.
Теперь вы хотите, чтобы обернуть аргумент в функции N раз:
// You could probably write this iteratively, but it is pretty elegant recursively
func numToChurch(_ n: Int) -> Church {
// Church(0) does not apply the function
guard n > 0 else { return { (_, n) in n } }
// Otherwise, recursively apply the function
return { (f, x) in
numToChurch(n - 1)(f, f(x))
}
}
И получить обратно только применяя функцию:
func churchToNum(_ church: Church) -> Int {
return church({$0 + 1}, 0)
}
Просто строить на этом, вы может карри его (и я думаю, что я просто говорю, что @kennytm также ответил). Карринг лишь немного сложнее в Swift:
typealias Church = (@escaping (Int) -> Int) -> (Int) -> Int
func numToChurch(_ n: Int) -> Church {
// Church(0) does not apply the function
guard n > 0 else { return { _ in { n in n } } }
return { f in { x in
numToChurch(n - 1)(f)(f(x))
}
}
}
func churchToNum(_ church: Church) -> Int {
return church({$0 + 1})(0)
}
Там очень резонный вопрос: «Зачем мне нужно во втором случае @escaping
, но не в первую очередь?» Ответ заключается в том, что когда вы передаете функцию в кортеже, вы уже избегаете ее (сохраняя ее в другой структуре данных), поэтому вам не нужно снова отмечать ее @escaping
.
Для ваших дальнейших вопросов, используя typealias резко упрощает эту проблему и поможет вам продумать ваши типы гораздо более четко.
Итак, каковы параметры нуля? Ничего. Это постоянный. Итак, какова должна быть его подпись?
func zero() -> Church
Как это реализовать? Мы применяем f
ноль раз
func zero() -> Church {
return { f in { x in
x
} }
}
Один и два почти идентичны:
func one() -> Church {
return { f in { x in
f(x)
} }
}
func two() -> Church {
return { f in { x in
f(f(x))
} }
}
Что такое подпись succ
? Он принимает Церковь и возвращает Церковь:
func succ(_ n: @escaping Church) -> Church {
Потому что это Свифт, нам нужно немного подтолкнуть, добавив @escaping
и _
, чтобы сделать вещи более естественно. (Swift не является функциональным языком, он разлагает проблемы по-разному. Составляющие функции не являются его естественным состоянием, поэтому избыточность синтаксиса не должна шокировать нас.) Как реализовать? Нанесите еще один f
к n
:
func succ(_ n: @escaping Church) -> Church {
return { f in { x in
let nValue = n(f)(x)
return f(nValue)
} }
}
И опять же, какова природа sum
? Ну, у нас настроение, и это означает, что это функция, которая берет Церковь и возвращает функцию, которая берет Церковь и возвращает Церковь.
func sum(_ n: @escaping Church) -> (@escaping Church) -> Church
Опять же, необходим дополнительный синтаксис, потому что Swift. (И, как описано выше, я добавил дополнительный Пусть связывающую только, чтобы кусочки немного более ясно.)
func sum(_ n: @escaping Church) -> (@escaping Church) -> Church {
return { m in { f in { x in
let nValue = n(f)(x)
return m(f)(nValue)
} } }
}
Глубокий урок здесь власть Church
typealias. Когда вы пытаетесь думать о числах Церкви как о «функциях, которые бла-бла-бла», вы быстро теряетесь в карри и синтаксисе. Вместо этого отрисуйте их как «церковные числа» и просто подумайте о том, что каждая функция должна принимать и возвращать. Помните, что номер церкви всегда функция, которая принимает Int и возвращает Int. Он никогда не растет и не сжимается от этого, независимо от того, сколько раз он был вложен.
Это стоит принимать этот пример в несколько других направлениях, потому что мы можем играть некоторые более глубокие идеи FP, а также как Swift действительно должно быть написана (не то же самое ....)
Во-первых, написание церковных номеров в остром стиле ... неэлегантное. Это просто плохо. Церковные номера определяются с точки зрения функционального состава, а не приложения, поэтому их следует писать в стиле без ИМО. В принципе, в любом месте вы видите { f in { x in ...} }
, это просто уродливо и чрезмерно синтаксически. Поэтому нам нужна функциональная композиция. Хорошо, мы можем углубиться в некоторые экспериментальные stdlib features и получить, что
infix operator ∘ : CompositionPrecedence
precedencegroup CompositionPrecedence {
associativity: left
higherThan: TernaryPrecedence
}
public func ∘<T, U, V>(g: @escaping (U) -> V, f: @escaping (T) -> U) -> ((T) -> V) {
return { g(f($0)) }
}
Теперь, что это делает для нас?
func numToChurch(_ n: Int) -> Church {
// Church(0) does not apply the function
guard n > 0 else { return zero() }
return { f in f ∘ numToChurch(n - 1)(f) }
}
func succ(_ n: @escaping Church) -> Church {
return { f in f ∘ n(f) }
}
func sum(_ n: @escaping Church) -> (@escaping Church) -> Church {
return { m in { f in
n(f) ∘ m(f)
} }
}
Таким образом, мы не должны говорить о x
больше. И мы более глубоко фиксируем сущность церковных чисел, ИМО. Суммирование их эквивалентно функциональному составу.
Но все, что сказал, ИМО это не очень быстро. Swift хочет структуру и методы, а не функции. Это определенно не хочет функцию верхнего уровня, которая называется zero()
. Это ужасно Свифт. Итак, как мы реализуем церковные числа в Свифте?Подъем в тип.
struct Church {
typealias F = (@escaping (Int) -> Int) -> (Int) -> Int
let applying: F
static let zero: Church = Church{ _ in { $0 } }
func successor() -> Church {
return Church{ f in f ∘ self.applying(f) }
}
static func + (lhs: Church, rhs: Church) -> Church {
return Church{ f in lhs.applying(f) ∘ rhs.applying(f) }
}
}
extension Church {
init(_ n: Int) {
if n <= 0 { self = .zero }
else { applying = { f in f ∘ Church(n - 1).applying(f) } }
}
}
extension Int {
init(_ church: Church) {
self = church.applying{ $0 + 1 }(0)
}
}
Int(Church(3) + Church(7).successor() + Church.zero) // 11
Добро пожаловать в (часто разочаровывающий) мир FP в Свифт, или, как нам нравится называть его: «Хорошо в зоне комфорта Swift». : D Но все же, это может быть отличное упражнение; удачи и добро пожаловать в клуб! –