Объяснение сопоставления с моделью

Я пытался объяснить разницу между операторами switch и сопоставлением с образцом (F#) нескольким людям, но я не смог объяснить это хорошо... большую часть времени они просто смотрят на меня и говорят: "Так почему? не просто использовать if..then..else ".

Как бы вы им это объяснили?

РЕДАКТИРОВАТЬ! Спасибо всем за отличные ответы, я действительно хотел бы отметить несколько правильных ответов.

9 ответов

Решение

Раньше я был одним из "тех людей", и я не знаю, что есть краткий способ подвести итог, почему сопоставление с образцом - это такое вкусное совершенство. Это опыт.

Когда я только что взглянул на сопоставление с образцом и подумал, что это было прославленное выражение switch, я думаю, что у меня не было опыта программирования с алгебраическими типами данных (кортежами и различимыми объединениями), и я не совсем понимал, что сопоставление с образцом было одновременно управляющая конструкция и связующая конструкция. Теперь, когда я программирую на F#, я наконец-то "понял". Прохладность сопоставления с образцом обусловлена ​​слиянием функций, присутствующих в функциональных языках программирования, и поэтому для стороннего наблюдателя нетривиально это оценить.

Я попытался обобщить один аспект того, почему сопоставление с образцом полезно во второй серии коротких блогов, состоящих из двух частей, по языку и дизайну API; проверить часть первая и часть вторая.

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

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

type expr =
    | Int of int
    | Var of string
    | Add of expr * expr
    | Mul of expr * expr;;

let rec d(f, x) =
    match f with
    | Var y when x=y -> Int 1
    | Int _ | Var _ -> Int 0
    | Add(f, g) -> Add(d(f, x), d(g, x))
    | Mul(f, g) -> Add(Mul(f, d(g, x)), Mul(g, d(f, x)));;

Кроме того, поскольку сопоставление с образцом является статической конструкцией для статических типов, компилятор может (i) проверить, что вы охватили все случаи (ii) обнаружить избыточные ветви, которые никогда не могут соответствовать ни одному значению (iii) обеспечить очень эффективную реализацию (с переходами и т. Д.).

Выдержка из этой статьи в блоге:

Сопоставление с образцом имеет несколько преимуществ по сравнению с операторами switch и диспетчеризацией методов:

  • Сопоставление с образцом может воздействовать на целые, поплавки, строки и другие типы, а также на объекты.
  • Сопоставление с образцом может воздействовать на несколько разных значений одновременно: параллельное сопоставление с образцом Метод отправки и переключения ограничен одним значением, например, "this".
  • Шаблоны могут быть вложенными, что позволяет распределять их по деревьям произвольной глубины. Отправление и переключение метода ограничены не вложенным случаем.
  • Шаблоны or позволяют использовать общие шаблоны. Диспетчеризация методов допускает совместное использование только тогда, когда методы из классов, которые совместно используют базовый класс. В противном случае вы должны вручную выделить общность в отдельную функцию (присвоив ей имя), а затем вручную вставить вызовы из всех подходящих мест в ненужную функцию.
  • Сопоставление с образцом обеспечивает проверку избыточности, которая фиксирует ошибки.
  • Вложенные и / или параллельные сопоставления с образцами оптимизируются для вас компилятором F#. Эквивалент ОО должен быть написан от руки и постоянно повторно оптимизирован вручную во время разработки, что непомерно утомительно и подвержено ошибкам, поэтому код ОО производственного качества имеет тенденцию быть чрезвычайно медленным по сравнению.
  • Активные шаблоны позволяют вводить пользовательскую семантику отправки.

С верхней части моей головы:

  1. Компилятор может сказать, не охватили ли вы все возможности в ваших матчах
  2. Вы можете использовать матч в качестве задания
  3. Если у вас есть дискриминационный союз, каждое совпадение может иметь свой "тип"

У кортежей есть ",", а у вариантов - аргументы Ctor. Это конструкторы, они создают вещи.

Шаблоны являются деструкторами, они разрывают их на части.

Это двойственные понятия.

Чтобы выразить это более убедительно: понятие кортежа или варианта не может быть описано просто его конструктором: необходим деструктор или значение, которое вы создали, бесполезно. Именно эти двойные описания определяют значение.

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

Параллелизм очевиден в таких операциях, как

(f * g) . (h * k) = (f . h * g . k) 

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

С этой точки зрения выражения представляют собой способы составления кортежей и вариантов для создания сложных структур данных (например, AST).

И сопоставления с образцом - это способы составить деструкторы (опять же, подумайте о AST).

Переключатель двух передних колес.

Сопоставление с образцом - это весь автомобиль.

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

  • исчерпывающий (ни один случай не пропущен)
  • не избыточный (нет случаев, которые никогда не могут быть обработаны, потому что они имеют приоритет перед предыдущим случаем)
  • звук (без шаблонов, которые невозможны, учитывая тип данных)

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

Операторы If-Else (или switch) предназначены для выбора различных способов обработки значения (ввода) в зависимости от свойств имеющегося значения.

Сопоставление с образцом - это определение способа обработки значения с учетом его структуры(также обратите внимание, что сопоставления с образцом в одном случае имеют смысл).

Таким образом, сопоставление с образцом больше связано с деконструкцией значений, чем с выбором, это делает их очень удобным механизмом определения (рекурсивных) функций на индуктивных структурах (рекурсивных типах объединения), что объясняет, почему они так широко используются в таких языках, как Ocaml и т. Д.

PS: Вы могли бы знать "шаблоны" сопоставления с образцом и If-Else по их специальному использованию в математике;

"если x имеет свойство A, то y еще z" (If-Else)

"некоторый член в p1..pn, где.... - простое разложение x.." ((единственный случай) совпадение с образцом)

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

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

Другие вопросы по тегам