Как мне использовать сопоставление с шаблоном в определениях 'let'?

StackOverflow https://stackoverflow.com/questions/455571

  •  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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top