Более эффективный код точки или просто терпение?
Я написал следующий код, который берет кучу точек и рисует их на экране с помощью библиотеки глянца.
let s = blocks pes
pts = map (map mkPt) s {- stitches to points-}
lines = map Line pts {-points to lines -}
pict = Pictures lines {- lines to a picture -}
in do displayInWindow "My Window" (200, 200) (10, 10) white pict
Он отлично работает, но мне приходит в голову, что существует повторяющийся шаблон: цепочка вызовов функций, результат каждого из которых подается в последний аргумент следующего.
Поэтому я реорганизую, удаляя промежуточные переменные, меняя порядок и цепляя функции с композицией функции ( "." ) Следующим образом:
let pict = Pictures . (map Line) . (map $ map $ mkPt) . blocks $ pes
in do displayInWindow "My Window" (200, 200) (10, 10) white pict
К счастью, это тоже отлично. Но мне интересно, не напрягаю ли удобочитаемость, или если я просто не привык к чтению и написанию кода стиля без кода. Кроме того, как мне объяснить этот код? Является ли вторая версия более эффективной или просто сложной? Есть ли что-нибудь, что я могу сделать стилистически, чтобы сделать его более ясным?
Ответы
Ответ 1
Несколько быстрых предложений:
let pict = Pictures . (map Line) . (map $ map $ mkPt) . blocks $ pes
in do displayInWindow "My Window" (200, 200) (10, 10) white pict
У вас есть лишние вещи, которые можно удалить прямо:
let pict = Pictures . map Line . (map $ map mkPt) . blocks $ pes
in do displayInWindow "My Window" (200, 200) (10, 10) white pict
В любом случае вы не избегаете круглых скобок с термином map (map mkPt)
, поэтому избавляйтесь от $
:
let pict = Pictures . map Line . map (map mkPt) . blocks $ pes
in do displayInWindow "My Window" (200, 200) (10, 10) white pict
Для ясности можно написать цепочку композиций на нескольких строках:
let pict = Pictures
. map Line
. map (map mkPt)
. blocks $ pes
in do displayInWindow "My Window" (200, 200) (10, 10) white pict
Блок do
лишний, поскольку он имеет только один оператор, и вы можете перенести окончательное приложение за пределы определения:
let displayPict = displayInWindow "My Window" (200, 200) (10, 10) white
. Pictures
. map Line
. map (map mkPt)
. blocks
in displayPict pes
Вы можете объединить два map
s:
let displayPict = displayInWindow "My Window" (200, 200) (10, 10) white
. Pictures
. map (Line . map mkPt)
. blocks
in displayPict pes
Иногда это также более читаемо для длинных цепей, чтобы использовать оператор обратной композиции из Control.Arrow
:
let displayPict = blocks
>>> map (Line . map mkPt)
>>> Pictures
>>> displayInWindow "My Window" (200, 200) (10, 10) white
in displayPict pes
Но все это необязательно; введите свой код по вкусу.
Что касается эффективности, я не вижу причин думать, что они будут разными, как только GHC-оптимизатор будет работать с кодом.
Ответ 2
Точечный стиль может быть хорошим, чтобы очень четко показать, что функция представляет собой просто последовательность преобразований на входе. Очень легко читать что-то вроде:
foo = map show . doThis . doThat . etc
потому что он выглядит как обычный точечный код, поэтому знакомый с ним человек сможет точно видеть, что важно, без шума. Сравните это с:
foo x = d
where
a = etc x
c = doThis b
b = doThat a
d = map show c
Очевидно, что это немного надуманно, но идея состоит в том, что дополнительные определения нужно читать и внимательно следить за тем, чтобы понять, что действительно делает foo
. Используется ли a
в качестве аргумента для более чем одной вспомогательной функции? Что насчет b
? Произошли ли данные через эти вспомогательные функции таким образом, что это было неожиданным? Точный свободный стиль в этом случае говорит: "Вещи просто трансформируются в конвейер, нет никаких странностей, о которых вам нужно подумать"
В других случаях стиль без точек может действительно запутывать вещи. Вообще-то, когда вы выходите из дам и wheres. Таким образом, терпение - не единственная цель, важно также уменьшить умственную нагрузку. (Как прокомментировали другие, точечный стиль не должен иметь разницу в производительности в оптимизированном коде).