использование операторов с zipWithN
Скажем, мы можем написать что-то вроде этого:
zipWith (,) [1,2,3] [4,5,6]
Если мы хотим, чтобы кортеж 3 список, мы можем написать:
zipWith3 (,,) [1,2,3] [4,5,6] [7,8,9]
Мы также можем использовать zipWith4 (,,,) zipWith5(,,,,) и так далее.
Теперь я хочу сделать то же самое, но используя оператор добавления вместо запятой.
Есть ли способ определить его таким же кратким способом, не используя лямбды, как в
zipWith3 (a b c -> a + b + c) [1, 2, 3] [4, 5, 6] [7, 8, 9]
Заранее спасибо за любой ответ.
4 ответов:
Похоже, вам нужен код стиля "point free" для
\a b c -> a + b + c. Пусть будет известно, что, в общем,\a b c -> a + b + cчасто предпочтительнее указывать свободный код, потому что его гораздо легче прочитать через четыре недели, когда вы обнаружили ошибку.Есть Вики-статья о точечном свободном программировании ( source).
Вы также можете установить пакет
pointfree, который позволяет решить эти проблемы в командной строке. Например,$ pointfree '\x y z -> x + y + z' ((+) .) . (+)Таким образом,
((+) .) . (+)является бесплатной версией точки (x, y и z "точки", если вам интересно, и нет, это не имеет ничего общего с геометрией). Вы можете использовать это определение, если хотите, но большинство людей будут смотреть на ваш код и понятия не будут иметь, что должен делать этот забавно выглядящий кусок ASCII-искусства. Половина из них будет работать с карандашом и бумагой, но разве оригинал\x y z -> x + y + zне намного легче на глазах?Подсказка: если вам когда-нибудь понадобится выяснить, что делает некоторый точечный свободный код, посмотрите на Тип:
Prelude> :t ((+) .) . (+) ((+) .) . (+) :: (Num a) => a -> a -> a -> aИли вы можете установите пакет
pointful, который является приблизительно обратным пакетуpointfree.Добро пожаловать в мир точечного программирования, действуйте с осторожностью, чтобы ваш код не был нечитаемым.
Другой вариант: аппликативные функторы. По сути, контроль.Аппликатив содержит определение newtype ZipList (поскольку существует несколько возможных определений Аппликатива для типа списка), которые можно использовать следующим образом:
import Control.Applicative getZipList $ (,,) <$> ZipList [1,2,3] <*> ZipList [4,5,6] <*> ZipList [7,8,9]Или вот так (для пары (+) ' s):
getZipList $ (+) <$> ((+) <$> ZipList [1,2,3] <*> ZipList [4,5,6]) <*> ZipList [7,8,9]Хотя, вероятно, нет особого смысла использовать прикладные функторы для этой конкретной задачи, они тем не менее обеспечивают очень мощную абстракцию/механизм для решения подобного рода задач. задачи, поэтому они определенно заслуживают изучения (например, мы можем избавиться от zipWith3, zipWith4 ... прием).
Минуя zipWith3, вы можете сделать:
import Data.List (transpose) map sum $ transpose [[1,2,3],[4,5,6],[7,8,9]]В то время как использование zipWith3 сокращает выход до кратчайшего списка, это не делает, т. е. для [[1,2],[3]] он дает [4,2].
Я думаю, что вы не можете написать его без ламды. На самом деле
zipWith3требует в качестве первого параметра функцию, которая принимает 3 параметра, а(+)принимает только два. Итак, вам нужно определить "плюс-функцию, принимающую 3 параметра", то есть именно то, что делает ваша лямбда.Альтернативный вариант:
foldr1 (zipWith (+)) [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ]Я не знаю, является ли вышесказанное более кратким, чем
zipWith3 (\a b c -> a + b + c) [1, 2, 3] [4, 5, 6] [7, 8, 9]
Comments