以元组为参数定义异常

问题描述:

我想在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"]). 

这是怎么回事?谢谢!

你看着这个错误(尽管我不会责怪你:起初它很令人惊讶)。在您看来,构造函数遵循语法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

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

在这方面,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),并且在表达式周围的括号被遗忘在抽象语法树中。