У меня есть функция формы
'a -> ('a * int) list -> int
let rec getValue identifier bindings =
match bindings with
| (identifier, value)::tail -> value
| (_, _)::tail -> getValue identifier tail
| [] -> -1
Я могу сказать это identifier
не связывается путем, я хотел бы его к и действую как новая переменная в рамках выражения соответствия. Как я добираюсь identifier
быть, что передается в функцию?
Хорошо! Я зафиксировал его с защитой шаблона, т.е. | (i, value)::tail when i = indentifier -> value
но я нахожу это ужасным по сравнению со способом, которым я первоначально хотел сделать это (я только использую эти языки, потому что они довольно...). Какие-либо мысли?
Вы можете использовать активные шаблоны F # для создания шаблона, который будет делать именно то, что вам нужно. F # поддерживает параметризованные активные шаблоны, которые принимают совпадающее значение, но также принимают дополнительный параметр.
Вот довольно глупый пример, который терпит неудачу, когда значение
равно нулю, и в противном случае завершается успешно и возвращает сложение значения и указанного параметра:
let (|Test|_|) arg value =
if value = 0 then None else Some(value + arg)
Вы можете указать параметр в сопоставлении с образцом следующим образом :
match 1 with
| Test 100 res -> res // 'res' will be 101
Теперь мы можем легко определить активный шаблон, который будет сравнивать совпадающее значение с входным аргументом активного шаблона. Активный шаблон возвращает параметр единицы
, что означает, что он не связывает какое-либо новое значение (в приведенном выше примере он вернул некоторое значение, которое мы присвоили символу res
):
let (|Equals|_|) arg x =
if (arg = x) then Some() else None
let foo x y =
match x with
| Equals y -> "equal"
| _ -> "not equal"
Вы можете использовать это как вложенный шаблон, поэтому вы сможете переписать свой пример, используя активный шаблон Equals
.
Это не прямой ответ на вопрос: как сопоставить значение переменной с образцом. Но это тоже не совсем не связано.
Если вы хотите увидеть, насколько мощным может быть сопоставление с образцом в ML-подобном языке, подобном F # или OCaml, взгляните на Moca .
Вы также можете взглянуть на код, сгенерированный Moca :) (не то чтобы что-то не так с компилятором, который много чего делает за вас. чтобы чувствовать, что они знают, во что обойдутся операции, которые они пишут).
Одна из прелестей функциональных языков - функции высшего порядка. Используя эти функции, мы убираем рекурсию и просто сосредотачиваемся на том, что вы действительно хотите делать. Чтобы получить значение первого кортежа, который соответствует вашему идентификатору, в противном случае верните -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
Эта статья Грэма Хаттона - отличное введение в то, что вы можете делать с функциями более высокого порядка.
Это распространенная жалоба, но я не думаю, что есть хороший обходной путь в целом; защита шаблона обычно является лучшим компромиссом. Однако в некоторых конкретных случаях есть альтернативы, например, пометить литералы атрибутом [
в F#, чтобы их можно было сопоставить.