использование операторов с 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]


Заранее спасибо за любой ответ.

712   4  

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

    Ничего не найдено.