2012-02-17 4 views
29

가능한 중복 :
Why are functions in Ocaml/F# not recursive by default?왜 ocaml은 "let"과 "let rec"를 모두 필요로합니까?

OCaml의 재귀있는 함수를 정의 let rec를 새로운 함수를 정의 let 사용하거나. 왜 두 가지가 모두 필요합니까? let을 모든 용도에 사용할 수 없습니까?

예를 들어, 하스켈 (GHCI)에서 내가 쓸 수있는 반면에 내가

let succ n = n + 1;; 

let rec fact n = 
    if n = 0 then 1 else n * fact (n-1);; 

을 작성할 수 있습니다합니다 (OCaml의 인터프리터에서 실제로) OCaml의에서 비 재귀 후계자 기능과 순환 요인을 정의

let succ n = n + 1 

let fact n = 
    if n == 0 then 1 else n * fact (n-1) 

왜 OCaml은 letlet rec을 구별합니까? 그것은 성능 문제, 또는 뭔가 더 미묘한가?

+6

투표가 닫히는 이유는 무엇입니까? 합리적인 질문처럼 보입니다. – Sean

+0

참조 @stackoverflow.com/questions/900585/why-are-functions-in-ocaml-f-not-recursive-by-default –

+0

@Sean : 이것은 매우 자주 묻는 질문입니다. – Ashe

답변

27

글쎄, 단 하나 대신 둘 다 사용할 수있게되면 프로그래머는 범위를 엄격하게 제어 할 수 있습니다. let x = e1 in e2의 경우, 바인딩은 e2의 환경에만 존재하며, let rec x = e1 in e2의 바인딩은 e1e2의 환경에 모두 존재합니다.

(편집 : 나는 것을 강조하고 싶다 하지 전혀 차이가없는 성능 문제.)

여기 가진이 비 재귀 바인딩 유용 두 가지 상황은 다음과 같습니다

  • 기존 바인딩을 사용하는 상세 검색으로 기존 정의를 섀도 잉합니다. 뭔가 : let f x = (let x = sanitize x in ...), 여기서 sanitize은 입력에 몇 가지 바람직한 속성이 있음을 보장하는 함수입니다 (예 : 정규화되지 않은 벡터 등의 표준을 취함). 이것은 어떤 경우에 매우 유용합니다.

  • 매크로 프로그래밍과 같은 메타 프로그래밍. 어떤 표현식이든 에 대해 let x = foo in x * x에있는 매크로 SQUARE(foo)을 정의하고 싶다고 상상해보십시오. 출력에서 코드 중복을 피하기 위해이 바인딩이 필요합니다 (factorial n을 두 번 계산하려면 SQUARE(factorial n) 필요 없음). let 바인딩이 반복적이지 않으면 위생적입니다. 그렇지 않으면 let x = 2 in SQUARE(x)을 쓰고 올바른 결과를 얻을 수 없습니다.

따라서 필자는 실제로 재귀 적 바인딩과 비 재귀 적 바인딩을 모두 사용하는 것이 매우 중요하다고 주장합니다. 자, 기본적으로 let-binding의 동작은 관례입니다. let x = ...은 재귀 적이며, 비 재귀 바인더를 얻으려면 let nonrec x = ...을 사용해야한다고 말할 수 있습니다. 하나의 기본값 또는 다른 하나를 선택하는 것은 어떤 프로그래밍 스타일로 부탁하고 어떤 선택을해야하는지에 대한 좋은 이유가 있습니다. 하스켈은이 비 재귀 적 모드를 사용할 수 없기 때문에 ¹ 고통을 겪고 OCaml은 유형 수준에서 정확히 동일한 결함을 가지고 있습니다 : type foo = ...은 재귀 적이며 사용할 수있는 재귀 적 옵션은 없습니다 - this blog post을 참조하십시오.

¹ : Google 코드 검색을 사용할 수있을 때이를 사용하여 Haskell 코드에서 let x' = sanitize x in ... 패턴을 검색했습니다. 이것은 재귀 적 바인딩을 사용할 수없는 경우의 일반적인 해결 방법이지만 나중에 실수로 x' 대신 x을 쓰는 위험이 있으므로 안전하지 않습니다. 두 가지를 모두 사용하고자하는 경우가 있기 때문에 자발적으로 다른 이름을 사용할 수 있습니다 . 좋은 관용구는 unsanitized_x과 같이 첫 번째 x에 더 긴 변수 이름을 사용하는 것입니다. 어쨌든, 그냥 x'을 찾고 (다른 변수 이름 없음) x1은 많은 결과를 냈습니다. Erlang (그리고 가변 음영 처리를 어렵게 만드는 모든 언어 : Coffeescript 등)은 이런 종류의 더 심각한 문제를 가지고 있습니다.

즉, 기본적으로 (재귀 적이 아닌) 재귀 적 바인딩을 갖는 Haskell 바인딩을 선택하는 것은 기본적으로 지연 평가와 일치하므로 확실히 재귀 값을 만드는 것이 쉽습니다. 엄격한 기본 언어는 재귀 적 정의가 더 많은 제약을 갖습니다.

+2

멋진 포스트이지만, 나는 "메타 프로그래밍"에 대해 확신하지 못합니다. 하나는'square'를 함수로 쓰고, 컴파일러가 인라이닝 (inlining) 기능을 가지고있을 때, 이것은 알파 - 이름 바꾸기 등으로 매크로처럼 행동 할 것입니다. – Ingo

+3

또한 섀도 잉 문제는 다음과 같은 숙어로 가장 쉽게 수행 할 수 있습니다.'foo x = work (sanitized x) where work x = ....' – Ingo

+0

@Ingo, 메타 프로그래밍은 일반적인 함수를 확장하는 것 이상의 의미가 있습니다. –

관련 문제