Я читаю о функциональном программировании и заметил, что сопоставление с образцом упоминается во многих статьях как одна из основных функций функциональных языков.
Может ли кто-нибудь объяснить разработчику Java / C ++ / JavaScript, что это означает?
Ответы:
Для понимания сопоставления с образцом необходимо пояснить три части:
Вкратце об алгебраических типах данных
Функциональные языки, подобные ML, позволяют определять простые типы данных, называемые «непересекающимися объединениями» или «алгебраическими типами данных». Эти структуры данных являются простыми контейнерами и могут быть определены рекурсивно. Например:
определяет структуру данных, подобную стеку. Думайте об этом как об эквиваленте этого C #:
Таким образом,
Cons
иNil
идентификаторы определяют простой простой класс, в которомof x * y * z * ...
определяет конструктор и некоторые типы данных. Параметры конструктора не имеют имени, они идентифицируются по положению и типу данных.Вы создаете экземпляры своего
a list
класса как таковые:Это то же самое, что:
В двух словах о сопоставлении с образцом
Сопоставление с образцом - это разновидность проверки типов. Итак, допустим, мы создали объект стека, подобный приведенному выше, мы можем реализовать методы для просмотра и извлечения стека следующим образом:
Приведенные выше методы эквивалентны (хотя и не реализованы как таковые) следующему C #:
(Почти всегда языки ML реализуют сопоставление с образцом без типовых тестов или приведений во время выполнения, поэтому код C # несколько обманчив. Давайте отбросим детали реализации, помахав рукой :))
В двух словах о декомпозиции структуры данных
Хорошо, вернемся к методу просмотра:
Хитрость заключается в понимании , что
hd
иtl
идентификаторы являются переменными (errm ... так как они неизменны, они на самом деле не «переменные», а «ценности»;)). Еслиs
имеет типCons
, мы собираемся извлечь его значения из конструктора и привязать их к переменным с именемhd
иtl
.Сопоставление с образцом полезно, потому что оно позволяет нам разложить структуру данных по форме, а не по содержимому . Итак, представьте, что мы определяем двоичное дерево следующим образом:
Мы можем определить некоторые вращения дерева следующим образом:
(
let rotateRight = function
Конструктор является синтаксическим сахаром дляlet rotateRight s = match s with ...
.)Таким образом, помимо привязки структуры данных к переменным, мы также можем углубиться в нее. Допустим, у нас есть узел
let x = Node(Nil, 1, Nil)
. Если мы вызываемrotateLeft x
, мы проверяемx
первый шаблон, который не соответствует, потому что правый дочерний элемент имеет типNil
вместоNode
. Он перейдет к следующему шаблону,x -> x
который будет соответствовать любому входу и вернет его без изменений.Для сравнения мы бы написали приведенные выше методы на C # как:
Для серьезно.
Сопоставление с образцом - это здорово
Вы можете реализовать что-то похожее на сопоставление с образцом в C #, используя образец посетителя , но это не так гибко, потому что вы не можете эффективно разложить сложные структуры данных. Более того, если вы используете сопоставление с образцом, компилятор сообщит вам, если вы пропустили регистр . Как это круто?
Подумайте, как бы вы реализовали аналогичные функции на C # или языках без сопоставления с образцом. Подумайте, как бы вы это сделали без тестовых тестов и приведений во время выполнения. Это конечно не сложно , просто громоздко и громоздко. И у вас нет проверки компилятора, чтобы убедиться, что вы охватили все случаи.
Таким образом, сопоставление с образцом помогает вам разлагать структуры данных и перемещаться по ним в очень удобном компактном синтаксисе, это позволяет компилятору хотя бы немного проверить логику вашего кода. Это на самом деле является особенностью убийцы.
источник
Краткий ответ: сопоставление с образцом возникает из-за того, что функциональные языки рассматривают знак равенства как утверждение эквивалентности вместо присваивания.
Длинный ответ: сопоставление с образцом - это форма отправки, основанная на «форме» присвоенного значения. На функциональном языке типы данных, которые вы определяете, обычно называются дискриминируемыми объединениями или алгебраическими типами данных. Например, что такое (связанный) список? Связанный список
List
вещей некоторого типаa
- это либо пустой список,Nil
либо некоторый элемент типаa
Cons
ed вList a
(списокa
s). В Haskell (функциональном языке, с которым я наиболее знаком) мы пишем этоВсе дискриминируемые объединения определяются следующим образом: один тип имеет фиксированное количество различных способов его создания; создатели, вроде
Nil
иCons
здесь, называются конструкторами. Это означает, что значение типаList a
могло быть создано двумя разными конструкторами - оно могло иметь две разные формы. Итак, предположим, мы хотим написатьhead
функцию для получения первого элемента списка. В Haskell мы бы написали это какпоскольку
List a
значения могут быть двух разных типов, нам нужно обрабатывать каждое из них отдельно; это сопоставление с образцом. Вhead x
случаеx
совпадения с шаблономNil
мы запускаем первый случай; если он соответствует шаблонуCons h _
, запускаем вторую.Краткий ответ с пояснением: я думаю, что один из лучших способов подумать об этом поведении - это изменить свое отношение к знаку равенства. В языках с фигурными скобками, по большому
=
счету , означает присвоение:a = b
означает «превратитьa
вb
». Во многих функциональных языках, однако,=
означает утверждение равенства:let Cons a (Cons b Nil) = frob x
утверждает, что вещь слева,Cons a (Cons b Nil)
эквивалентна вещи справа,frob x
,; кроме того, все переменные, используемые слева, становятся видимыми. То же самое происходит и с аргументами функции: мы утверждаем, что первый аргумент выглядит такNil
, а если нет, мы продолжаем проверку.источник
Cons
значит?Cons
есть минусы tructor , что строит (связанный) список из подпора (a
) и хвост (List a
). Название происходит от Лиспа. В Haskell для встроенного типа списка это:
оператор (который по-прежнему произносится как «cons»).Это означает, что вместо того, чтобы писать
Ты можешь написать
Эй, C ++ тоже поддерживает сопоставление с образцом.
источник
Сопоставление с образцом похоже на перегруженные методы на стероидах. Самый простой случай будет примерно таким же, как и в java, аргументы - это список типов с именами. Правильный метод для вызова основан на переданных аргументах и дублируется как назначение этих аргументов имени параметра.
Шаблоны просто идут еще дальше и могут еще больше разрушить переданные аргументы. Он также может потенциально использовать охранников для фактического сопоставления на основе значения аргумента. Для демонстрации я сделаю вид, будто в JavaScript есть сопоставление с образцом.
В foo2 он ожидает, что a будет массивом, он разбивает второй аргумент, ожидая объект с двумя реквизитами (prop1, prop2) и присваивает значения этих свойств переменным d и e, а затем ожидает, что третий аргумент будет 35.
В отличие от JavaScript, языки с сопоставлением с образцом обычно позволяют использовать несколько функций с одним и тем же именем, но с разными образцами. В этом смысле это похоже на перегрузку метода. Приведу пример на erlang:
Немного размыте глаза, и вы сможете представить это на javascript. Что-то вроде этого может быть:
Дело в том, что когда вы вызываете fibo, реализация, которую он использует, основана на аргументах, но там, где Java ограничивается типами как единственным средством перегрузки, сопоставление с образцом может сделать больше.
Помимо перегрузки функций, как показано здесь, тот же принцип может быть применен и в других местах, например, в операторах case или в задачах деструктуризации. В JavaScript это есть даже в версии 1.7 .
источник
Сопоставление с образцом позволяет сопоставить значение (или объект) с некоторыми образцами для выбора ветви кода. С точки зрения C ++ это может звучать немного похоже на
switch
утверждение. В функциональных языках сопоставление с образцом может использоваться для сопоставления стандартных примитивных значений, таких как целые числа. Однако это более полезно для составных типов.Во-первых, давайте продемонстрируем сопоставление с образцом для примитивных значений (с использованием расширенного псевдо-C ++
switch
):Второе использование связано с функциональными типами данных, такими как кортежи (которые позволяют хранить несколько объектов в одном значении) и размеченные объединения, которые позволяют создавать тип, который может содержать один из нескольких вариантов. Это немного похоже на то,
enum
за исключением того, что каждая метка также может нести некоторые значения. В синтаксисе псевдо-C ++:Значение типа
Shape
теперь может содержать либоRectangle
все координаты, либо aCircle
с центром и радиусом. Сопоставление с образцом позволяет написать функцию для работы сShape
типом:Наконец, вы также можете использовать вложенные шаблоны , сочетающие в себе обе функции. Например, вы можете использовать
Circle(0, 0, radius)
для сопоставления всех форм, центр которых находится в точке [0, 0], и любой радиус (значение радиуса будет присвоено новой переменнойradius
).Это может показаться немного незнакомым с точки зрения C ++, но я надеюсь, что мой псевдо-C ++ прояснит объяснение. Функциональное программирование основано на совершенно иных концепциях, поэтому лучше понимать его на функциональном языке!
источник
Сопоставление с образцом - это когда интерпретатор вашего языка выбирает конкретную функцию на основе структуры и содержания аргументов, которые вы ему даете.
Это не только функциональная языковая функция, она доступна для многих разных языков.
Впервые я столкнулся с этой идеей, когда выучил пролог, где он действительно важен для языка.
например
Приведенный выше код даст последний элемент списка. Входной аргумент является первым, а результат - вторым.
Если в списке есть только один элемент, интерпретатор выберет первую версию, а второй аргумент будет установлен равным первому, т.е. результату будет присвоено значение.
Если в списке есть и голова, и хвост, интерпретатор выберет вторую версию и будет выполнять рекурсию до тех пор, пока в списке не останется только один элемент.
источник
Многим людям легче освоить новую концепцию, если будут представлены несколько простых примеров, так что приступим:
Допустим, у вас есть список из трех целых чисел, и вы хотите добавить первый и третий элементы. Без сопоставления с образцом вы могли бы сделать это так (примеры в Haskell):
Теперь, хотя это игрушечный пример, представьте, что мы хотели бы связать первое и третье целое число с переменными и просуммировать их:
Это извлечение значений из структуры данных и есть то, что делает сопоставление с образцом. Вы в основном "зеркалируете" структуру чего-либо, предоставляя переменные для привязки к интересующим местам:
Когда вы вызываете эту функцию с [1,2,3] в качестве аргумента, [1,2,3] будет объединен с [first
_
,, third], привязывая сначала к 1, третье к 3 и отбрасывая 2 (_
это заполнитель для вещей, которые вам не интересны).Теперь, если вы хотите сопоставить списки только с 2 в качестве второго элемента, вы можете сделать это следующим образом:
Это будет работать только для списков с 2 в качестве второго элемента и вызовет исключение в противном случае, потому что для несовпадающих списков не дается определение addFirstAndThird.
До сих пор мы использовали сопоставление с образцом только для деструктурирования привязки. Выше вы можете дать несколько определений одной и той же функции, где используется первое определение соответствия, таким образом, сопоставление с образцом немного похоже на «оператор переключения на стереоиде»:
addFirstAndThird с радостью добавит первый и третий элементы списков с 2 в качестве второго элемента, иначе «провалится» и «вернет» 0. Эта «похожая на переключатель» функциональность может использоваться не только в определениях функций, например:
Кроме того, он не ограничивается списками, но может использоваться и с другими типами, например, сопоставление конструкторов значений Just и Nothing типа Maybe, чтобы «развернуть» значение:
Конечно, это были просто игрушечные примеры, и я даже не пытался дать формальное или исчерпывающее объяснение, но их должно быть достаточно, чтобы понять основную концепцию.
источник
Вам следует начать со страницы Википедии, которая дает довольно хорошее объяснение. Затем прочтите соответствующую главу вики-книги Haskell .
Это хорошее определение из приведенной выше вики-книги:
источник
Вот действительно короткий пример, демонстрирующий полезность сопоставления с образцом:
Допустим, вы хотите отсортировать элемент в списке:
в (я отсортировал "Нью-Йорк")
на более повелительном языке вы бы написали:
Вместо этого на функциональном языке вы должны написать:
Как вы можете видеть, решение с сопоставлением с шаблоном имеет меньше шума, вы можете ясно увидеть, какие существуют различные случаи и насколько легко путешествовать и деструктурировать наш список.
Я написал более подробный пост об этом здесь .
источник