Что лучше в сопоставлении шаблонов OCaml, «когда» или » если-то-еще`?

#performance #if-statement #functional-programming #pattern-matching #ocaml

Вопрос:

Допустим, у нас есть тип под названием d:

 type d = D of int * int
 

И мы хотим провести некоторое сопоставление по шаблону, не лучше ли сделать это таким образом:

 let dcmp = function 
  | D (x, y) when x > y -> 1 
  | D (x, y) when x < y -> -1
  | _ -> 0
 

или

 let dcmp = function 
  | D (x, y) -> 
    if x > y then 1 else if x < y then -1 else 0
 

Просто в общем случае лучше сопоставлять шаблоны со многими случаями «когда» или сопоставлять один шаблон и вставлять в него «если-то-еще»?

И где я могу получить дополнительную информацию о таких вопросах, как передовая практика в OCaml, синтаксические сахара и тому подобное?

Комментарии:

1. С точки зрения функционального программиста Haskell, несколько предложений when делают вещи довольно легко читаемыми, поэтому я бы выбрал это.

2. @vanntile верно, на самом деле я больше ищу возможный эффект, который может оказать использование многих предложений when на время компиляции/выполнения, если есть какой-либо эффект и если он отличается от использования операторов if

3. Мой ответ сосредоточен на руководстве по стилю, для повышения производительности, будем надеяться, что более опытный специалист сможет помочь

4. Не ответ, а замечание. Ваша функция эквивалентна этой: let dcmp D(x,y) = compare x y .

5. @ghilesZ спасибо, но функция-это всего лишь пример, я ищу общий ответ на вопрос об эффективности «когда против утверждения if».

Ответ №1:

Оба подхода имеют свои плюсы и минусы, поэтому их следует использовать в соответствии с контекстом.

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

   let dcmp = function 
  | D (x, y) when x > y -> 1 
  | D (x, y) when x = y -> 0
  | _ -> -1
 

Конечно, то же самое верно и для if/then/else конструкции, просто сложнее случайно переставить ветви (например, во время рефакторинга) в if/then/else выражении и полностью изменить логику выражения.

Кроме того, when защита может помешать компилятору выполнить оптимизацию дерева решений 1 и запутать механизм опровержения 2.

Учитывая это, единственным преимуществом использования when вместо if в данном конкретном примере является то, что when синтаксис выглядит более привлекательным, поскольку он идеально выстроен, и человеческому мозгу легче найти, где находятся условия и их соответствующие значения, т. Е. Он больше похож на таблицу истинности. Однако, если мы напишем

 let dcmp (D (x,y)) = 
  if x = y then 0 else
  if x > y then 1 else -1
 

мы можем достичь такого же уровня читабельности.

Подводя итог, его лучше использовать when , когда невозможно или почти невозможно выразить одним и тем же кодом if/then/else . Для улучшения читабельности лучше включить вашу логику во вспомогательные функции с удобочитаемыми именами. Например, при dcmp наилучшем решении не следует использовать ни if то , ни when другое, например,

 let dcmp (D (x,y)) = compare x y
 

1)В данном конкретном случае компилятор сгенерирует один и тот же код для when и if/then/else . Но в более общих случаях защита может помешать соответствующему компилятору генерировать эффективный код, особенно когда ветви не пересекаются. В нашем случае компилятор просто заметил, что мы повторяем одну и ту же ветвь, объединил их в одну ветвь и превратил ее обратно в выражение if/then/else, например, вот вывод cmm функции с when защитой,

 (if (> x y) 3 (if (< x y) -1 1))
 

который в точности совпадает с кодом, сгенерированным версией функции if/then/else dcmp .

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

Комментарии:

1. Я предпочитаю if x = y then 0 /// else if x > y then 1 /// else -1 /// метапозицией для разрывов строк). таким образом, у вас есть три отдельные строки для трех альтернатив.

2. это также мой предпочтительный способ структурирования выражений if/then/else, я просто показываю, что можно связать несколько выражений if/then/else таким образом, чтобы это выглядело как таблица истинности. С учетом сказанного, я обычно стараюсь избегать цепочки if/then/else и предпочитаю по возможности включать их в логические предикаты. Это экономит мне много времени и нервов, когда я позже прочитаю свой код 🙂

Ответ №2:

Цитирование руководства OCaml по стилю ясности и изящества:

Код чаще читается, чем пишется — облегчите жизнь читателю

и

Чем меньше кода, тем лучше, а зашифрованный код хуже

Первое заставляет меня думать, что версия с несколькими when предложениями является лучшим выбором, так как она позволяет легко предсказать или оценить результат при чтении кода в зависимости от условий. Второй идет дальше, против первого if-then-else , потому что, даже если он короче, загадочен, если смотреть издалека.

Кроме того, в разделе Функции мы узнаем, что «Сопоставление шаблонов является предпочтительным способом определения функций».

С точки зрения функционального программиста Haskell.