2012-12-27 80 views
1

我是一個ocaml noob。到目前爲止,使用普通的舊引用int或其他簡單的內置類型,對我來說,在所有方面都能像預期的那樣工作。我已經在refs是元組成員的元組上下文中使用它們。我可以更新裁判,取消引用他們,等鍵入的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」類型。 !!和:=運算符失敗。

而語義似乎改變奇怪顯然不一致的方式。以下只是一個例子。爲什麼寫下面的第一個代碼塊是合法的,但在頂部循環(在下面)做類似的事情似乎是非法的?第一個塊被編譯器接受,並且其中我們可以匹配一個構造類型的ref並使用!訪問它的值。在線路13和14操作者這是所有循環隊列的範圍內,並使用從#使用在頂循環中的文件中加載:

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。無論如何,我仍然不明白爲什麼語義看起來在類型化引用的頂級循環元組分解和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並不矛盾或不合邏輯。這是「現代」計劃生育語言的美麗之一 - 他們的類型系統是基於健全的數學。現在,我將重點介紹的第一件事就是告訴你不工作:

​​

好像這個問題是很清楚的,如果你只是看看next elem是。從您的elem的定義中可以看出,next elemPointer (ref elem)。這不是一個參考。它的構造函數是Pointer。因此,將!運算符應用於它是沒有意義的,這是類型錯誤告訴您的。如果您想從next elem中取回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 foo = Foo of t型不同於t;您可以將v : t轉換爲fooFoo v,並將v : foo轉換爲t(match v with (Foo x) -> x)

我想你同時混合了太多的困難。刪除-rectypes並讓你的代碼工作,然後你可以考慮加回來看它是否可以減輕代碼。 -rectypes默認情況下未啓用的原因是,某些代碼肯定是錯誤的(let sum x = x (* + *) x),但只有在稍後使用它時纔會導致出現隱蔽錯誤消息。如果您對該語言的其他方面感到不舒服,則不希望發生這種情況。

+0

如果你假裝所有類型都是指向某個堆塊的指針,那麼理由會更加容易。它對整數沒有什麼不同,因爲你會認爲它們是不可變的,就像float一樣。 – newacct

+0

確實,但我不想製造「這是非常低效率」的擔憂,因此我的區別。如果您有任何建議,我會對以一種更抽象的方式描述極端共享的方式感興趣,這種方式可以調用指針和內容。 – gasche