2016-10-12 4 views
1

Скажем, я пишу этот код:OCaml: в качестве ключевого слова в сопоставлении с образцом ведет себя странно

# type t = A of int * int 
    let f = function A (i1, i2) -> print_int i1;; 
type t = A of int * int 
val f : t -> unit = <fun> 

совершенна, она работает.

Теперь, скажем, у меня есть эта замечательная функция:

# let print_couple (i1, i2) = print_int i1; print_int i2;; 
val print_couple : int * int -> unit = <fun> 

Так что, как вы ожидаете, я хотел бы написать следующее

# let f = function A (_ as c) -> print_couple c;; 

Ну, я не могу

Error: The constructor A expects 2 argument(s), 
     but is applied here to 1 argument(s) 

Я задавался вопросом, это из-за _ или круглых скобок (я серьезно сомневался в этом, но я хотел быть исчерпывающим)?

# let f = function A _ -> failwith "Fight me" 
    let g = function A (_) -> failwith "1o1";; 
val f : t -> 'a = <fun> 
val g : t -> 'a = <fun> 

Нет, это не ...

О, может быть, я должен показать компилятор, я знаю, у меня есть два аргумента:

# let f = function A ((_, _) as c) -> print_couple c;; 
Error: The constructor A expects 2 argument(s), 
     but is applied here to 1 argument(s) 

Но ... Если я пишу

# let f = function A (_, _) -> failwith "Puppey style";; 

Это работает. Тогда почему, поскольку компилятор знает, что я ожидаю пару, и я даже пытаюсь дать его ему, он продолжает терпеть неудачу? Это то, что написав A (_ as c) Я называю, независимо от того, первый аргумент? Странно, не так ли?

Я имею в виду, если я пишу

# let rec f = function 
    | [] ->() 
    | hd :: tl -> print_couple hd; f tl;; 
val f : (int * int) list -> unit = <fun> 

компилятор не будет беспокоить меня об этом списке, являющемся ассоциативном список или список целого? Тогда странно ли от меня ожидать такого же поведения от

# match a with 
    | A c | A (_ as c) | A ((_, _) as c) ->() 

?

ответ

2

Это причуда OCaml, что конструктор, подобный вашему определению A, берет то, что синтаксически выглядит как пара, но не является парой.

# type t = A of int * int;; 
type t = A of int * int 
# A (3, 4);; 
- : t = A (3, 4) 
# let z = (3, 4) in A z;; 
Error: The constructor A expects 2 argument(s), 
     but is applied here to 1 argument(s) 

Если определить конструктор, как на самом деле взять пару, вы можете поставить пару:

# type u = B of (int * int);; 
type u = B of (int * int) 
# B (3, 4);; 
- : u = B (3, 4) 
# let z = (3, 4) in B z;; 
- : u = B (3, 4) 

Это несколько сбивает с толку, но это именно так, как OCaml работает. Вам необходимо указать аргументы в квадратных скобках для конструктора типа A.

Это также немного удивительно, что вы не должны поставить четкие скобки при сопоставлении с _:

# let A _ = A (4, 5);; 
# 

(Это полезно только как часть более крупной модели, конечно.)

+0

Благодаря Джоффри. Как всегда, ясный и лаконичный. :-) – Lhooq

+0

О твоем редактировании, не правда ли, странно? Потому что, если я ожидаю два параметра, я ожидал бы (_, _). Я думаю, это просто синтаксический сахар, потому что вы часто хотите сопоставлять только конструктор, не принимая во внимание параметры, но вы не можете позволить что-то вроде (_ как c), потому что «c» не будет парой. – Lhooq

3

Строго говоря, когда вы объявляете A of int * int, A ожидает два int аргументов и не единственный аргумент, состоящий в паре int. _ является заполнителем только для одного аргумента, поэтому сообщение об ошибке вы видите, когда пишете A (_ as c) (или A _). Нет способа (если не использовать темную магию, которую я не буду здесь описывать), чтобы каким-то образом неявно преобразовать два аргумента A в пару: вам нужно прибегнуть к match x with A (x,y) -> x,y.

Альтернативой является объявление вашего конструктора таким образом, чтобы он брал пару, как в приведенном ниже коде. Имейте в виду, что это менее эффективно, хотя (вам нужно два indirections, чтобы получить доступ к аргументам A, один, чтобы получить пару, и один, чтобы извлечь его компоненты):

type t = A of (int * int) 
let print_couple (i1, i2) = print_int i1; print_int i2 
let f = function A c -> print_couple c 
+1

Upvote, но, к сожалению, я не могу принять два ответа. Спасибо за эффективность. ;-) – Lhooq