Haskell: Предпочитаете сопоставление с образцом или членский доступ? - PullRequest
14 голосов
/ 02 августа 2011

Предположим, у меня есть Vector тип данных, определенный следующим образом:

data Vector = Vector { x :: Double
                     , y :: Double
                     , z :: Double
                     }

Было бы более привычным определять функции для него, используя доступ к элементу:

vecAddA v w
    = Vector (x v + x w)
             (y v + y w)
             (z v + z w)

Или используя шаблон-matching:

vecAddB (Vector vx vy vz) (Vector wx wy wz)
    = Vector (vx + wx)
             (vy + wy)
             (vz + wz)

(извиняюсь, если у меня неверная терминология).

Ответы [ 6 ]

12 голосов
/ 02 августа 2011

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

data Foo = A {a :: Int} | B {b :: String}

fun x = a x + 1

Если вы используете сопоставление с образцом для работы с типом Foo, вы в безопасности;невозможно получить доступ к члену, который не существует.Если вы используете функции доступа с другой стороны, некоторые операции, такие как вызов fun (B "hi!"), приведут к ошибке во время выполнения.

РЕДАКТИРОВАТЬ: хотя, конечно, вполне можно забыть сопоставить какой-либо конструктор, сопоставление с образцомдовольно ясно показывает, что то, что происходит, зависит от того, какой конструктор используется (вы также можете указать компилятору обнаруживать и предупреждать вас о неполных шаблонах), тогда как использование функции намекает больше, чем любой конструктор, IMO.

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

7 голосов
/ 02 августа 2011

Еще один второстепенный аргумент «реального мира»: в общем случае не рекомендуется иметь такие короткие имена записей, так как короткие имена, такие как x и y, часто используются для локальных переменных.

Таким образом, «честное» сравнение здесь будет:

vecAddA v w 
  = Vector (vecX v + vecX w) (vecY v + vecY w) (vecZ v + vecZ w)
vecAddB (Vector vx vy vz) (Vector wx wy wz) 
  = Vector (vx + wx) (vy + wy) (vz + wz)

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

  • Вам нужно только получить доступ (или изменить!) К одному или двум полям в большей записи
  • Вы хотите сохранять гибкость, чтобы изменить запись позже, например,добавить больше полей.
4 голосов
/ 03 августа 2011

(Оповещение, возможно, ошибаюсь. Я все еще новичок на Haskell, но вот мое понимание)

Одна вещь, которую другие люди не упомянули, это то, что сопоставление с образцом сделает функцию "строгой" в своем аргументе,(http://www.haskell.org/haskellwiki/Lazy_vs._non-strict)

Чтобы выбрать, какой шаблон использовать, программа должна уменьшить аргумент до WHNF до вызова функции, тогда как использование функции доступа к синтаксису записи оценило бы аргумент внутри функция.

Я не могу привести какие-либо конкретные примеры (все еще будучи новичком), но это может повлиять на производительность, когда огромные кучи "групп" могут накапливаться в рекурсивных, нестрогих функциях.(Это означает, что для простых функций, таких как извлечение значений, не должно быть разницы в производительности).

(конкретные примеры очень приветствуются)

Короче говоря

f (Just x) = x

на самом деле (используя BangPatterns)

f !jx = fromJust jx

Редактировать: Выше не хороший пример строгости, потому что оба на самом деле строгие из определения (f bottom = bottom),просто чтобы проиллюстрировать, что я имел в виду с точки зрения производительности.

4 голосов
/ 02 августа 2011

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

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

2 голосов
/ 03 августа 2011

Как указывало kizzx2 , существует строгая разница в строгости между vecAddA и vecAddB

vecAddA ⊥ ⊥ = Vector ⊥ ⊥ ⊥
vecAddB ⊥ ⊥ = ⊥

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

vecAddB' ~(Vector vx vy vz) ~(Vector wx wy wz)
    = Vector (vx + wx)
             (vy + wy)
             (vz + wz)

Однако в этом случае поля Vector должны быть, вероятно, строгими для эффективности:

data Vector = Vector { x :: !Double
                     , y :: !Double
                     , z :: !Double
                     }

При строгих полях vecAddA и vecAddB семантически эквивалентны.

0 голосов
/ 04 августа 2011

Пакет Hackage vect решает обе эти проблемы, позволяя сопоставлять как f (Vec3 xyz) и индексировать как:

get1 :: Vec3 -> Float
get1 v = _1 v

Поиск класса HasCoordinates.

http://hackage.haskell.org/packages/archive/vect/0.4.7/doc/html/Data-Vect-Float-Base.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...