Использование переменной в сопоставлении с образцом в Ocaml или F # - PullRequest
9 голосов
/ 05 марта 2010

У меня есть функция вида

'a -> ('a * int) list -> int

let rec getValue identifier bindings = 
  match bindings with
  | (identifier, value)::tail -> value
  | (_, _)::tail -> getValue identifier tail
  | [] -> -1

Я могу сказать, что identifier не привязывается так, как мне хотелось бы, и действует как новая переменная в выражении соответствия. Как мне получить identifier, чтобы быть то, что передается в функцию?

Ok! Я исправил это с помощью шаблона, то есть | (i, value)::tail when i = indentifier -> value но я нахожу это уродливым по сравнению с тем, как я изначально хотел это сделать (я использую только эти языки, потому что они симпатичные ...). Есть мысли?

Ответы [ 5 ]

11 голосов
/ 05 марта 2010

Вы можете использовать активные шаблоны F # для создания шаблона, который будет делать именно то, что вам нужно.F # поддерживает параметризованные активные шаблоны, которые принимают значение, которое вы соответствуете, но также принимают дополнительный параметр.

Вот довольно глупый пример, который завершается ошибкой, когда value равен нулю, и в противном случае завершается успешно и возвращает сложение.значения и указанного параметра:

let (|Test|_|) arg value = 
  if value = 0 then None else Some(value + arg)

Вы можете указать параметр в сопоставлении с образцом следующим образом:

match 1 with
| Test 100 res -> res // 'res' will be 101

Теперь мы можем легко определить активный шаблон, который будет сравниватьсопоставленное значение с входным аргументом активного шаблона.Активный шаблон возвращает unit option, что означает, что он не привязывает никакое новое значение (в приведенном выше примере он вернул некоторое значение, которое мы присвоили символу res):

let (|Equals|_|) arg x = 
  if (arg = x) then Some() else None

let foo x y = 
  match x with
  | Equals y -> "equal"
  | _ -> "not equal"

Youможно использовать его как вложенный шаблон, поэтому вы сможете переписать свой пример, используя Equals активный шаблон.

7 голосов
/ 05 марта 2010

Одна из красот функциональных языков - функции высшего порядка. Используя эти функции, мы вынимаем рекурсию и просто сосредотачиваемся на том, что вы действительно хотите сделать. Который должен получить значение первого кортежа, соответствующего вашему идентификатору, в противном случае возвращается -1:

let getValue identifier list = 
match List.tryFind (fun (x,y) -> x = identifier) list with
    | None      -> -1
    | Some(x,y) -> y

//val getValue : 'a -> (('a * int) list -> int) when 'a : equality

Эта бумага Грэма Хаттона - отличное введение в то, что вы можете делать с функциями высшего порядка.

5 голосов
/ 05 марта 2010

Это не прямой ответ на вопрос: как сопоставить шаблону значение переменной. Но это не совсем не связано.

Если вы хотите увидеть, насколько мощным может быть сопоставление с образцом в ML-подобном языке, подобном F # или OCaml, взгляните на Moca .

Вы также можете взглянуть на код, сгенерированный Moca :) (не то, чтобы что-то не так с компилятором, делающим много вещей для вас за вашей спиной. В некоторых случаях это даже желательно, но многим программистам нравится чувствовать, что они знают, сколько будут стоить операции, которые они пишут).

3 голосов
/ 05 марта 2010

Это распространенная жалоба, но я не думаю, что в общем есть хороший обходной путь; паттерн-охранник обычно лучший компромисс. Однако в некоторых конкретных случаях существуют альтернативы, такие как маркировка литералов атрибутом [<Literal>] в F #, чтобы их можно было сопоставлять.

2 голосов
/ 14 ноября 2011

То, что вы пытаетесь сделать, называется шаблоном равенства, и оно не предоставляется Objective Caml. Объективные паттерны Камла являются статичными и чисто структурными. То есть, соответствует ли значение шаблону, зависит исключительно от структуры значения и способом, который определяется во время компиляции. Например, (_, _)::tail - это шаблон, который соответствует любому непустому списку, чья голова является парой. (identifier, value)::tail соответствует точно таким же значениям; единственное отличие состоит в том, что последний связывает еще два имени identifier и value.

Хотя в некоторых языках существуют шаблоны равенства, существуют нетривиальные практические соображения, которые делают их проблематичными. Какое равенство? Физическое равенство (== в Окамле), структурное равенство (= в Окамле) или какое-то пользовательское равенство, зависящее от типа? Кроме того, в Ocaml есть четкое синтаксическое указание того, какие имена являются связующими и какие имена относятся к ранее связанным значениям: любой строчный идентификатор в шаблоне является связующим. Эти две причины объясняют, почему в Ocaml не используются шаблоны равенства. Идиоматический способ выражения шаблона равенства в Ocaml стоит на страже. Таким образом, сразу становится ясно, что сопоставление не является структурным, что identifier не связано этим сопоставлением с образцом и какое равенство используется. Что касается уродливости, это в глазах смотрящего - как обычный программист на Ocaml, я нахожу паттерны равенства некрасивыми (по причинам выше).

match bindings with
 | (id, value)::tail when id = identifier -> value
 | (_, _)::tail -> getValue identifier tail
 | [] -> -1

В F # у вас есть еще одна возможность: активные шаблоны , которые позволяют предварительно определять охрану, касающуюся одного сайта в шаблоне.

...