4

나는 현재 스위프트 3에 Church Numerals을 구현하기 위해 시도하고, 내가 가진 :오류를 비는 이스케이프 3

return f(numToChurch(n: n - 1)(f)(x)) 
: 내 기능 numToChurch에서이 라인에서

func numToChurch(n: Int) -> ((Int) -> Int) -> Int { 

    return { (f: (Int) -> Int) -> (Int) -> Int in 
     return { (x : Int) -> Int in 
      return f(numToChurch(n: n - 1)(f)(x)) 
     } 
    } 
} 

func churchToNum(f: ((Int) -> Int) -> (Int)-> Int) -> Int { 
    return f({ (i : Int) -> Int in 
     return i + 1 
    })(0) 
} 

"이스케이프하지 않는 매개 변수 'f'의 종료로 인해 이스케이프가 허용되는 컴파일 타임 오류가 계속 발생합니다. 빠른 수정, 나는 @escaping을 포함하도록 권장 변경 가능 :

func numToChurch(n: Int) -> ((Int) -> Int) -> Int { 

    return { (f: @escaping (Int) -> Int) -> (Int) -> Int in 
     return { (x : Int) -> Int in 
      return f(numToChurch(n: n - 1)(f)(x)) 
     } 
    } 
} 

그러나 심지어 변경 한 후, 나는 같은 오류 말했다지고 계속하고 이후 다른 @escaping 추가 할 것을 권장합니다 "F를". 필자는 함수 매개 변수를 @escaping으로 표시하여 컴파일러에게 함수 프로그래밍을 위해 매개 변수를 저장하거나 캡처 할 수 있음을 알리는 것으로 이해합니다. 그러나 왜 나는이 오류가 계속 발생하는지 이해하지 못합니다.

원래 비 탈출 질문은

를 해결 스위프트의 계속에 대한 이해 교회 인코딩 도움말 : 당신은 다중 매개 변수 기능 무두질 사용하고

func zero(_f: Int) -> (Int) -> Int { 
    return { (x: Int) -> Int in 
     return x 
    } 
} 

func one(f: @escaping (Int) -> Int) -> (Int) -> Int { 
    return { (x: Int) in 
     return f(x) 
    } 
} 

func two(f: @escaping (Int) -> Int) -> (Int) -> Int { 
    return { (x: Int) in 
     return f(f(x)) 
    } 
} 

func succ(_ f: Int) -> (@escaping (Int) -> Int) -> (Int) -> Int { 
    return { (f : @escaping ((Int) -> Int)) -> Int in 
     return { (x : Int) -> Int in 
      return f(n(f)(x)) 
     } 
    } 
} 


func sum(m: @escaping ((Int) -> (Int) -> Int)) -> ((Int) -> (Int) -> Int) -> (Int) -> (Int) -> Int { 
    return { (n: @escaping ((Int) -> Int)) -> (Int) -> (Int) -> Int in 
     return { (f: Int) -> (Int) -> Int in 
      return { (x: Int) -> Int in 
       return m(f)(n(f)(x)) 
      } 
     } 
    } 
+1

스위프트의 FP 세계에 오신 것을 환영합니다. 또는 "스위프트의 안락한 구역 밖에 있습니다." 그러나 여전히 훌륭한 운동이 될 수 있습니다. 행운을 빈다. 클럽에 오신 것을 환영합니다! –

답변

7

. 스위프트에서 표현하는 것은 자연스러운 방법이 아니며 복잡한 일을 만듭니다. (Swift is not a functional programming language.)

링크 된 기사에 "모든 교회 숫자는 두 가지 매개 변수를 사용하는 함수입니다."라고되어 있습니다. 그렇게해라. 두 매개 변수 함수로 만듭니다.

typealias Church = (_ f: ((Int) -> Int), _ x: Int) -> Int 

이것은 함수와 인수의 두 매개 변수를 취하는 함수입니다.

// 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)) 
    } 
} 

그리고 다시 점점 단지 기능을 적용 :

이제 함수 N 시간의 인수를 마무리 할

func churchToNum(_ church: Church) -> Int { 
    return church({$0 + 1}, 0) 
} 

그냥이에 구축, 당신 그것을 카레로 만들 수 있습니다. (그리고 저는 @kennytm이 대답 한 내용을 말하고 있다고 생각합니다.) 커링은 스위프트 단지 약간 더 복잡하다 : 매우 합리적인 질문이있다

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 를 사용하여 추가 질문에


은 극적으로이 문제를 단순화하고 당신이 훨씬 더 명확하게 유형을 통해 생각하는 데 도움이됩니다.

그래서 0의 매개 변수는 무엇입니까? 아무것도. 그것은 상수입니다. 그러면 서명은 무엇이되어야합니까?

func zero() -> Church 

어떻게 구현합니까? 우리는 적용 f 제로 시간

func zero() -> Church { 
    return { f in { x in 
     x 
     } } 
} 

한 2 개는 거의 동일합니다 :

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는 기능적 언어가 아니며 문제를 다르게 분해합니다. 함수 작성은 자연 상태가 아니므로 구문의 과도한 사용으로 인해 우리에게 충격을주지 않아야합니다.) 구현 방법? 한 번 더 fn에 적용 :

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의 생각과도 스위프트가 정말로 (.... 동일하지있는) 작성 방법

을 재생할 수 있기 때문에 다른 방향의 몇 예를 복용 가치

첫째, 지적 숫자로 교회 번호를 쓰는 것은 우아하지 않습니다. 기분이 좋지 않습니다. 교회 번호는 적용이 아닌 기능적 구성의 측면에서 정의되므로 포인트가없는 스타일의 IMO로 작성해야합니다. 기본적으로 어디서나 { f in { x in ...} }을 볼 수 있습니다. 단지 추악하고 지나치게 구문이 복잡합니다. 그래서 우리는 기능적 구성을 원합니다. OK, 우리는 몇 가지 실험 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에 대해 이야기 할 필요가 없습니다. 그리고 우리는 훨씬 더 강력하게 교회 번호의 본질을 포착합니다, IMO. 그것들을 더하는 것은 기능적 구성과 같습니다.

그러나 모든 것이 IMO는 훌륭한 스위프트가 아닙니다. 스위프트는 기능이 아니라 구조와 방법을 원합니다. 확실히 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 
+0

정말 고마워요. 스위프트에서 교회 숫자를 이해하려고 애쓰며 실망했습니다. 이것은 매우 도움이되었습니다. – fayche

3

@escaping 인수 유형의 일부입니다, 그래서 당신은 그것을 좋아합니까해야합니다

func numToChurch(n: Int) -> (@escaping (Int) -> Int) -> (Int) -> Int { 
//       ^~~~~~~~~ 

전체, 작업 코드 :

func numToChurch(n: Int) -> (@escaping (Int) -> Int) -> (Int) -> Int { 
//       ^~~~~~~~~      ^~~~~~ 
    return { (f: @escaping (Int) -> Int) -> (Int) -> Int in 
//    ^~~~~~~~~ 
     if n == 0 { 
      return { x in x } 
     } else { 
      return { (x : Int) -> Int in 
       return f(numToChurch(n: n - 1)(f)(x)) 
      } 
     } 
    } 
} 

func churchToNum(f: (@escaping (Int) -> Int) -> (Int) -> Int) -> Int { 
//     ^~~~~~~~~ 
    return f({ (i : Int) -> Int in 
     return i + 1 
    })(0) 
} 

let church = numToChurch(n: 4) 
let num = churchToNum(f: church) 

참고 :

  1. 귀하의 반품 종류는 0123입니다. @escaping 부분이 없어도은 잘못되었습니다. -> Int이 누락되었습니다.

  2. 나는 에 기본 n == 0 케이스를 추가했습니다. 그렇지 않으면 무한 재귀가됩니다.

  3. numToChurch의 결과에는 이스케이프 처리 종료가 있기 때문에 churchToNum에도 동일한 주석을 추가해야합니다.

+0

귀하의 의견을 듣기 위해 @ rob-napier님께 고마워요. 귀하의 수정 사항은 탈출하지 못한 문제를 해결하는 데 도움이되었습니다. 왜 그런 일이 발생했는지 알 수 있습니다. 또한 후계자와 같은 FP 함수를 구현하는 방법을 이해하려고합니다. 원래 게시물에 더 많은 코드를 포함 시켰습니다. 피드백을 제공하고 계속 진행하는 방법에 대한 지침을 제공하면 정말 감사하겠습니다! – fayche

관련 문제