2012-12-27 3 views
1

나는 ocaml 멍청이다. int에 대한 평범한 오래된 참조 또는 다른 간단한 내장 유형을 사용하여 지금까지 모든면에서 예상대로 작동합니다. 저는 ref가 튜플의 멤버 인 튜플의 컨텍스트에서 사용했습니다. 나는 그들 역 참조의 심판을 업데이트 할 수 있습니다 등입력 된 참조 셀?

# let e = 1, ref 1;; 
val e : int * int ref = (1, {contents = 1}) 
# snd e := 2;; 
- : unit =() 
# e;; 
- : int * int ref = (1, {contents = 2}) 
# !(snd e);; 
- : int = 2 

하지만 최대한 빨리 (또는 내장 간단한) 일이 일반적으로 심하게 이동, 입력 다른 집계에 "심판의"명명 된 형식을 선언으로 . 나는 그들이 "ref의"유형으로 선언되지 않았을 때 이전과 달리 참조를 더 이상 변경할 수 없다는 것을 발견했다. 그! and : = 연산자가 실패합니다.

그리고 의미가 이상하게 보이는 방식으로 바뀌는 것처럼 보입니다. 아래는 단지 하나의 예입니다. 아래의 첫 번째 코드 블록을 작성하는 것이 합법적 인 이유는 무엇입니까? 그러나 맨 위 루프에서 이와 유사한 작업을하는 것은 불법입니다 (아래 참조). 첫 번째 블록은 컴파일러에 의해 받아 들여지고, 생성 된 타입 인 ref와 일치 할 수 있고! 라인 (13) 및 (14)에 오퍼레이터 이것은 모든 순환 큐의 컨텍스트 내에 있고 #use하여 상부 루프 파일을로드 : 상부 루프 유사한 노력에서

type 'a element = 'a * 'a pointer 
and 'a pointer = Pointer of 'a element ref;; 
let next (_,n) = n;; 
type 'a queue = 'a element option ref;; 

let create() = None;; 
(*passes compiler and behaves well*) 
let enqueue queue x = 
    match !queue with 
     None -> 
    let rec elem = (x, Pointer (ref elem)) in 
    queue := Some elem; 
    | Some (_, Pointer last_newest_next) -> (*Insert between newest and oldest*) 
     let oldest = !last_newest_next in 
     let elem = (x, Pointer (ref oldest)) in 
     last_newest_next := elem; 
     queue := Some elem;; 

를 (이 따라 변동)하지 아래, 나는 또한 함수를 사용하여 튜플을 분해 한 후 같은 연산자를 호출 할 경우 같은 :

let rec elem = (1, Pointer (ref elem));; 
let last = !(next elem);; 
Characters 12-22: 
let last = !(next elem);; 
      ^^^^^^^^^^ 
Error: This expression has type int pointer 
    but an expression was expected of type 'a ref 

그래, 내가 사용하고 -rectypes을하지만 약식 재귀를 사용하지 않고 한 번이를 원했고 유형과 나는 그 후 모든 붙어있어.

let last = next elem;; 
val last : int pointer = (* ... *) 

그리고 첫 번째 코드 블록은 라인 (14)에 변경된 경우를 사용하지 : 그것은 동일하며 내가 정말 원하는 상단 루프에서 다음 작품을 참고하지만 확실하지 않다! 연산자, 중단됩니다.

(*compiles but fails - que only ever holds one item*) 
let enqueue queue x = 
    match !queue with 
     None -> 
    let rec elem = (x, Pointer (ref elem)) in 
    queue := Some elem; 
    | Some (_, Pointer last_newest_next) -> 
     let oldest = last_newest_next in 
     let elem = (x, Pointer oldest) in 
     last_newest_next := elem; 
     queue := Some elem;; 

그것은이없는 것을해야합니다 : 다시 쓰기는 (아래) 컴파일러를 전달했지만 무례한 행동을하는 대기열 기능을 원인! 연산자 (그리고 다른 몇 가지 변경 사항), 두 번째 행에서 마지막 행까지는 실제로 원래 의도 한대로 elem을 가리 키도록 다른 포인터 (일치 항목의 분해 된 요소 내에서)를 업데이트하는 대신 elem 포인터를 자체로 가리 킵니다. 그럼에도 불구하고 필자는 typed ref의 최상위 루프 튜플 분해와 ml 파일에서 동일한 작업을하는 것과 의미가 왜 일치하지 않는지 아직 볼 수 없다. 또는 패턴 매치에서 분해가 어떻게 든 함수를 통해 튜플을 분해하는 것과 같지 않습니까?

그리고 위의 기능의 동작을 테스트 할 디큐 기능을 사용 : 나는 기능적인 언어로 심판 세포를 피해야 할 이유

let dequeue queue = 
    match !queue with 
     None -> raise Not_found 
    | Some (_, Pointer oldest_ref) -> 
     let oldest = !oldest_ref in 
     let (x, Pointer next_ref) = oldest in 
     let next = !next_ref in 
     if next == oldest then 
    queue := None 
     else 
    oldest_ref := next; 
     x;; 

내가 이해할 수 있지만 사용하는 방법을 내가 알 필요가 그것은 필수적 일 때 (말장난 의도 없음).

답변

5

당신이 쓰는 것에 특정 질문을 찾는 것이 어렵습니다. 그러나 OCaml은 일관성이 없거나 비논리적이지 않습니다. 이것은 "현대"FP 언어의 아름다움 중 하나입니다. 유형 시스템은 건전한 수학에 기반합니다. 지금은 제일 먼저 할것 위해 당신은 작동하지 않는 보여 그냥 next elem이 무엇인지를 보면 그것은 문제처럼 보인다

# let rec elem = (1, Pointer (ref elem));; 
# let last = !(next elem);; ## TYPE ERROR HERE 

꽤 분명하다. elem의 정의에서 알 수 있듯이 next elemPointer (ref elem)입니다. 이것은 참조가 아닙니다. 생성자는 Pointer입니다.따라서 연산자에 ! 연산자를 적용하는 것은 의미가 없으므로 형식 오류가 사용자에게 알려줍니다. elemnext elem에서 다시 가져 오려면 Pointer 생성자를 구조화해야합니다.

# let unpointer (Pointer x) = x;; 
# let last = !(unpointer (next elem));; 
# last == elem;; 
- : bool = true 
# 

편집 : 그것은 가치가 무엇인지에 대한, 당신의 원형 목록 유형은 나에게 뒤얽힌 조금 보인다. 순환 목록이 실제로 필요한 경우에는 OCaml Batteries Included에 표시된 이중 연결 목록 구현을 살펴볼 수 있습니다 : BatDllist. 이것은 C로 작성하는 것과 매우 흡사 한 간단한 저수준 구현입니다. 내장 된 OCaml리스트를 사용하는 것이 더 좋을 것입니다! 나는 오랜 세월 OCaml 코딩 (단 하나의 데이터 포인트)에서 순환리스트를 사용할 필요성을 느껴 보지 못했습니다.

+0

회상 질문에 대한 사과; 그들이 내 이해를 대단히 도왔을 때 모든 해답 덕분에. – paul

4

프로그래밍 언어의 의미에 대해 매우 낮은 수준의 이해가있는 것 같습니다. 이 경우 낮은 것을 생각한다고 겉으로보기에 당신을 잘못 이끌었습니다. OCaml에서 참조의 의미는 일관성이 있으며 여기에서 관찰되는 이상한 모든 것은 언어 의미론이 아니라 사용자의 실수로 인한 것입니다.

당신을 도울 수 있다면, 여기 구현 수준이 낮은 것으로 생각하는 사람에게 OCaml의 의미를 설명하는 방법이 있습니다. 그것은 내가 일반적으로 초보자에게 설명 할 방법이 아니다,하지만 당신은 포인터, 집계 및 포인터의 관점에서 생각하고 주장하는 경우 : OCaml의에서

  • 값을 정수로 표현할 수있는 불변 중 하나이며, 그대로 통과, 또는 일부 힙 블록에 대한 포인터; 이 설정에서 값의/기준 구별에 의한 언어에서 명시 적으로 해체되거나 재구성되지 않는 한 모든 것이 공유된다는 사실을이 설정에서 추론하는 것이 더 간단합니다. type 'a ref = {mutable contents: 'a};의 레코드로 정의 된 개념은 파생 개념입니다. 이것은 값을 가리키는 간단한 상자에 해당하며 값이 가리키는 값을 변경할 수 있습니다. 그것은 다형성이며 일관된 방식으로 행동합니다. 특히, 입력 오류는 코드 오류로 인해 발생합니다.

  • 유형 type foo = Foo of tt과 구별됩니다. v : tFoo v으로 foo으로 변환하고 v : foot으로 (match v with (Foo x) -> x)으로 변환 할 수 있습니다.

나는 당신이 너무 많은 어려움을 동시에 섞어 가고 있다고 생각합니다. -rectypes을 놓고 코드 작업을 한 다음 코드를 밝게 할 수 있는지 다시 추가하는 방법을 고려해 볼 수 있습니다. -rectypes이 기본적으로 활성화되어 있지 않은 이유는 확실히 오류 (let sum x = x (* + *) x) 인 일부 코드가 허용되어 나중에 사용하는 경우에만 암시 적 오류 메시지가 나타납니다. 언어의 다른 측면에 불편하다면 그런 일이 일어나기를 원하지 않을 것입니다.

+0

모든 유형이 일부 힙 블록에 대한 포인터 인 것처럼 가장한다면 더 쉽게 추론 할 수 있습니다. float와 같은 방식으로 정수를 변경할 수 없으므로 정수에는 차이가 없습니다. – newacct

+0

사실,하지만 저는 "이것은 대단히 비효율적입니다"라는 걱정을하지 않으려합니다. 제안이 있다면 포인터와 물건을 호출하는 것과 같은 추상적 인 방식으로 극한의 공유를 설명하는 방법에 관심이 있습니다. – gasche