Как мне использовать сопоставление с шаблоном в определениях 'let'?
-
19-08-2019 - |
Вопрос
Я только что заметил, что 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 ...
чтобы получить желаемые результаты, но я только что обнаружил еще один синтаксис для сопоставления с образцом, и он будет досаждать мне вечно, если я не пойму, как его использовать.
Как мне писать функции, используя альтернативный синтаксис сопоставления с шаблоном, показанный выше?
Решение
JaredPar прав, F # не имеет синтаксической формы, которую здесь использует Haskell.
Форма F # в основном полезна для разбиения разбираемых союзов с одним регистром или для определения функций с неполными совпадениями (например, ваш пример 'car', который завершается ошибкой в пустом списке).Это просто следствие того факта, что практически вся привязка имен в языке выполняется с помощью шаблонов;эта синтаксическая форма (определение функции с использованием шаблонов для аргументов) часто не слишком полезна на практике именно по той причине, которую вы описали.
Я думаю, что Haskell сделал ряд вещей лучше, чем ML, когда дело доходит до синтаксических форм, но корни F # находятся в ML.Преимущество заключается в том, что хорошее подмножество F # кросс-компилируется с OCaml (что помогло загрузить язык F # и сообщество пользователей);недостатком является то, что F # "застрял" с несколькими фрагментами уродливого / ограниченного синтаксиса.
Другие советы
AFAIK, в F # нет способа объявить несколько привязок let с одинаковым именем и разными сигнатурами соответствия шаблону.Я считаю, что самая близкая конструкция к тому, что вы ищете, - это выражение правил функции.
Возьмем этот пример для автомобиля
let car = function
| hd::tl -> hd
| [] -> failwith "empty list"
По-видимому, сопоставление шаблонов 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
's head, в случае, если список не пуст, или с дополнительным значением, указанным во втором значении 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
Как мне писать функции, используя альтернативный синтаксис сопоставления с шаблоном, показанный выше?
Как и у вас, но только тогда, когда один шаблон является исчерпывающим.Очевидные примеры, где это полезно, включают кортежи и записи, а также объединения в одном случае и активные шаблоны.
Вы используете эту языковую функцию всякий раз, когда делаете:
let f(a, b) = ...
Таким образом , это обобщается на:
let f(a, (b, c)) = ...
Вы даже можете использовать это для выбора между значением по умолчанию или Some value
:
let def(x, None | _, Some x) = x
Кстати, стиль, который вы предлагаете, использовался в SML до Haskell, а SML - это ML, так что это, очевидно, не имеет ничего общего с Haskell vs ML.На самом деле я предпочитаю стиль OCaml / F #, потому что он менее повторяющийся:
fun descriptiveFunctionName [] = true
fun descriptiveFunctionName (_::_) = false
против
let descriptiveFunctionName = function
| [] -> true
| _::_ -> false
Это лучше для неакадемического кода, где реальная ценность заключается в использовании самодокументирующихся идентификаторов.