используя операторы с zipWithN - PullRequest
4 голосов
/ 19 июля 2011

Скажем, мы можем написать что-то вроде этого:

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 ]

12 голосов
/ 19 июля 2011

Звучит так, как будто вы хотите использовать стиль кода «без точек» для \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.

Резюме: Добро пожаловать в мир программирования без точек, действуйте с осторожностью, чтобы ваш код не читался.

7 голосов
/ 19 июля 2011

Другой вариант: аппликативные функторы.Фактически, Control.Applicative содержит определение нового типа ZipList (поскольку существует несколько возможных определений Applicative для типа списка), которые можно использовать так:

import Control.Applicative 

getZipList $ (,,) <$> ZipList [1,2,3] <*> ZipList [4,5,6] <*> ZipList [7,8,9]

или тому подобное (дляпара (+):

getZipList $ (+) <$> ((+) <$> ZipList [1,2,3] <*> ZipList [4,5,6]) <*> ZipList [7,8,9]

Несмотря на то, что использование аппликативных функторов для этой конкретной задачи, вероятно, не имеет особого смысла, тем не менее, они предоставляют очень мощную абстракцию / механизм для решения подобного родазадач, поэтому их определенно стоит изучить (например, мы можем избавиться от zipWith3, zipWith4 ... и т. д.).

4 голосов
/ 19 июля 2011

Обойдя zipWith3, вы можете сделать:

import Data.List (transpose)
map sum $ transpose [[1,2,3],[4,5,6],[7,8,9]]

При использовании zipWith3 сокращает вывод до кратчайшего списка, это не так, то есть для [[1,2], [3]] он дает [4,2].

2 голосов
/ 19 июля 2011

Я думаю, что вы не можете написать это без лямды. Фактически 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]
...