2010-10-15 71 views
5

我想在OCaml中定義一個接受列表元組作爲參數的異常。但是,這種情況不起作用?以元組爲參數定義異常

# exception Foo of string list * string list;; 
exception Foo of string list * string list 
# let bar = (["a"], ["b"; "c"; "d"]);; 
val bar : string list * string list = (["a"], ["b"; "c"; "d"]) 
# raise(Foo bar);; 
Error: The constructor Foo expects 2 argument(s), 
     but is applied here to 1 argument(s) 

但是,如果我這樣做,它的工作原理

# raise (Foo (["a"], ["b"; "c"; "d"]));; 
Exception: Foo (["a"], ["b"; "c"; "d"]). 

這是怎麼回事?謝謝!

回答

11

你看着這個錯誤(儘管我不會責怪你:起初它很令人驚訝)。在您看來,構造函數遵循語法Name of type,其中類型部分遵循正常類型語法(使其包含元組)。

在現實中,元組和建設者遵循完全相同的語法:構造僅僅是在它前面有一個名稱的元組:

tuple/constructor == [name of] type [* type] [* type] ... 

所以,在構造函數中定義的*不是的一部分元組語法,它們是構造函數語法的一部分。你從字面上定義一個構造函數爲這個名字,後面跟N個參數而不是這個名字,後面跟一個參數是一個元組

這種行爲細微差別背後的原因是表現之一。現在,元組和構造函數在內存中表示爲:

[TYPE] [POINTER] [POINTER] [POINTER] 

這是一個相當緊湊和高效的表示形式。如果構造函數的多個參數確實可以作爲一個元組來訪問,那麼這需要運行時獨立於構造函數來表示該元組(以便它可以獨立尋址),所以它看起來像這樣:

[TYPE] [POINTER] 
      | 
      v 
      [TYPE] [POINTER] [POINTER] [POINTER] 

這會使用更多的內存,在使用構造函數時需要兩倍的分配,並降低模式匹配元組的性能(因爲需要額外的解引用)。爲了保持最佳性能,name of type * type使用第一種模式表示,並且您需要明確鍵入name of (type * type)以從of中切斷*,並因此回退到第二種模式。

請注意,這兩種模式都通過相同的模式匹配和構造語法來訪問:name (arg,arg)。這意味着類型推斷不能根據使用情況推斷出模式。對於通常在類型定義中定義的普通構造函數來說,這是沒有問題的,但是它會導致變體(不需要初步定義)自動回退到第二個版本第二個版本。

here類型的內存表示的額外閱讀。

+0

哇!感謝冗長的解釋!你對OCaml的理解使你看起來像來自INRIA – axsuul 2010-10-15 09:05:34

4

Foo是具有2個參數的異常的構造函數。你必須分解元組並將每個部分傳遞給它。

# exception Foo of string list * string list;; 
exception Foo of string list * string list 
# let bar = (["a"], ["b"; "c"; "d"]);; 
val bar : string list * string list = (["a"], ["b"; "c"; "d"]) 
# let a, b = bar in raise (Foo (a, b));; 
Exception: Foo (["a"], ["b"; "c"; "d"]). 

如果您希望使用一個元組作爲單個參數,你必須用括號定義異常,並通過元組英寸

# exception Foo of (string list * string list);; 
exception Foo of (string list * string list) 
# let bar = (["a"], ["b"; "c"; "d"]);; 
val bar : string list * string list = (["a"], ["b"; "c"; "d"]) 
# raise (Foo bar);; 
Exception: Foo (["a"], ["b"; "c"; "d"]). 
+0

謝謝:)這是我想要的方式 – axsuul 2010-10-15 09:03:20

5

在這方面,OCaml中的異常構造就像普通構造函數:

Constr(1,2,3)是一個特殊的句法結構,其中不出現三元組。另一方面,在Constr((1,2,3))中發生三重。該實現與此行爲匹配,其中Constr(1,2,3)被分配爲單個塊,而Constr((1,2,3))爲兩個塊,其中一個包含指向另一個(三元組)的指針。在Constr(1,2,3)的運行時表示中,沒有三個指針指向一個指針,如果你需要指針,你必須分配一個新指針。

注:Constr(((1,2,3)))相當於Constr((1,2,3))。在Constr(((1,2,3)))中,中間括號被解釋爲圍繞表達式(1,2,3),並且在表達式周圍的括號被遺忘在抽象語法樹中。