Как использовать сопоставление с образцом в определениях let? - PullRequest
10 голосов
/ 18 января 2009

Я только что заметил, что F # позволяет мне использовать привязки let с литералами и другими шаблонами следующим образом:

let fib 0 = 1
let exists item [] = false
let car (hd :: tl) = hd
let cdr (hd :: tl) = tl

F # правильно интерпретирует эти функции как своего рода сопоставление с образцом, потому что выдает мне следующие предупреждения:

Предупреждение 1 Неполные совпадения с образцом на этом выражении. Например, значение '1' не будет соответствует

Предупреждение 2 Неполные совпадения с образцом на этом выражении. Например, значение '[_]' не будет соответствует

и т.д.

Эти функции работают должным образом, но я хочу определить функцию в этом стиле с полными совпадениями с образцом, однако я не могу найти ничего об этом альтернативном синтаксисе сопоставления с образцом в руководстве по F #.

Я знаю, что могу использовать let whatever = function ... и let whatever x = match x with ... для получения желаемых результатов, но я только что обнаружил еще один синтаксис для сопоставления с образцом, и это навлечет на меня боль, если я не пойму, как использовать его.

Как написать функции, используя альтернативный синтаксис сопоставления с образцом, показанный выше?

Ответы [ 4 ]

12 голосов
/ 18 января 2009

AFAIK, в F # нет способа объявить несколько привязок let с одним и тем же именем и разными сигнатурами сопоставления с образцом. Я считаю, что наиболее близкой конструкцией к тому, что вы ищете, является выражение правил функции.

Возьмите этот пример для автомобиля

let car = function
    | hd::tl -> hd
    | [] -> failwith "empty list"
9 голосов
/ 18 января 2009

JaredPar прав, F # не имеет синтаксической формы, которую Хаскель делает здесь.

Форма F # в основном полезна для разрыва открытых однозначных различающихся объединений или для определения функций с неполными совпадениями (например, ваш пример 'car', который не выполняется в пустом списке). Это просто следствие того факта, что практически все привязки имен в языке выполняются с помощью шаблонов; эта синтаксическая форма (определение функции с использованием шаблонов на аргументах) не слишком полезна на практике по той причине, которую вы описали.

Я думаю, что Haskell сделал несколько вещей лучше, чем ML, когда дело доходит до синтаксических форм, но корни F # в ML. Преимущество заключается в том, что хорошее подмножество F # кросс-компилируется с помощью OCaml (что помогло запустить язык F # и сообщество пользователей); недостаток в том, что F # «застрял» с несколькими битами уродливого / ограниченного синтаксиса.

7 голосов
/ 22 декабря 2012

Очевидно, что сопоставление с образцом в F # намного мощнее, чем мы используем в обычной разработке.

Во-первых, вы можете связать несколько значений одновременно. Как правило, вы будете делать это с List.partition:

let data = [7;0;0;0;1;0;1;1;1;1;0;0;1;1;0]
let ones, others = data |> List.partition ((=) 1) // bind two values

В качестве примечания вы можете привязать несколько идентификаторов к одному значению:

let (a & b) = 42 // a = 42; b = 42

Давайте начнем с простой привязки let для простоты.

let hd::tl = data

предупреждение FS0025 : для этого выражения найден неполный образец. Например, значение «[]» может указывать на случай, не охватываемый шаблоном (ами).

Чтобы смягчить это, мы должны добавить еще один случай для пустого списка:

let (hd::tl) | [] = data

ошибка FS0018 : две стороны этого шаблона 'или' связывают разные наборы переменных

И это правда; в случае пустого списка hd и tl остаются несвязанными. tl легко связать с тем же пустым списком:

let (hd::tl) | ([] as tl) = data

Однако ошибка Ошибка FS0018 не исчезает. В самом деле, мы также должны предоставить некоторое значение по умолчанию для hd.
Следующий подвох сделает это.

let (hd::tl, _) | ([] as tl , hd) = data, 42

Строка выше свяжет hd с головой data, в случае, если список не пуст, или с дополнительным значением, указанным во втором значении tuple .

Примечание Я не нашел способа встроить 42 в конструкцию let.

Наконец, то же самое для car функции:

let car ((hd::tl, _) | ([] as tl, hd)) = hd
let foo = car(data, 42) // foo = 7
let bar = car([], 42)   // bar = 42
1 голос
/ 16 декабря 2012

Как мне написать функции, используя альтернативный синтаксис сопоставления с образцом, показанный выше?

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

Вы используете эту языковую функцию, когда делаете:

let f(a, b) = ...

Итак, это обобщает до:

let f(a, (b, c)) = ...

Вы даже можете использовать это для выбора значения по умолчанию или Some value:

let def(x, None | _, Some x) = x

Кстати, предлагаемый вами стиль использовался в SML до того, как Haskell и SML стали ML, так что это, очевидно, не имеет ничего общего с Haskell против ML. Я на самом деле предпочитаю стиль OCaml / F #, потому что он менее повторяется:

fun descriptiveFunctionName [] = true
fun descriptiveFunctionName (_::_) = false

против

let descriptiveFunctionName = function
  | [] -> true
  | _::_ -> false

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

...