Как закрепить несколько списков в Haskell?
В функции python zip
принимает произвольное количество списков и зашифровывает их вместе.
>>> l1 = [1,2,3]
>>> l2 = [5,6,7]
>>> l3 = [7,4,8]
>>> zip(l1,l2,l3)
[(1, 5, 7), (2, 6, 4), (3, 7, 8)]
>>>
Как я могу zip
объединить несколько списков в haskell?
Ответы
Ответ 1
Обобщение zip может быть достигнуто с помощью Аппликационная нотация. Это немного неприятно для использования из-за упаковки/распаковки newtype, но если вы делаете то, что не может быть сделано с помощью zipWithn
для достаточно маленького n, вы, вероятно, уже находитесь на достаточно высоком уровне абстракции, где обозначение боли все равно отсутствуют.
Тип ZipList a
, и его аппликативный экземпляр застегивает вместе списки. Например:
(+) <$> ZipList [1,2] <*> ZipList [3,4] == ZipList [4,6]
Это обобщает функции произвольной arity и типа с использованием частичного приложения:
(+) <$> ZipList [1,2] :: ZipList (Int -> Int)
Посмотрите, как (+) применяется здесь частично?
Если вам не нравится добавлять ZipList и getZipList всюду, вы можете легко воссоздать обозначение:
(<$>) :: (a -> b) -> [a] -> [b]
(<$>) = map
(<*>) :: [a -> b] -> [a] -> [b]
(<*>) = zipWith ($)
Тогда обозначение для zipWith f a b c d ...
:
f <$> a <*> b <*> c <*> d <*> ...
Аппликативная нотация - очень мощный и общий метод, который имеет гораздо более широкий охват, чем просто обобщенная застежка. Подробнее см. Typeclassopedia.
Ответ 2
Вы можете перенести список списков:
>>> import Data.List
>>> transpose [l1,l2,l3]
[[1,5,7],[2,6,4],[3,7,8]]
Ответ 3
Похоже, есть также zip3
(doc) и zip4
(doc) в Haskell. Но zipn кажется сложным из-за сильной системы типов. Вот хорошая дискуссия, которую я нашел во время моих исследований.
Ответ 4
GHC также поддерживает параллельные проверки списков:
{-# LANGUAGE ParallelListComp #-}
[(x,y) | x <- [1..3]
| y <- ['a'..'c']
]
==> [(1,'a'),(2,'b'),(3,'c')]
Я просто проверил его до 26 параллельных переменных, что должно быть достаточно для всех практических целей.
Это немного хакерский (и нестандартный), хотя, если вы пишете что-то серьезное, ZipList может быть лучшим способом.
Ответ 5
Я думаю, что это, вероятно, наименее изящное решение, но для полноты нужно добавить, что такие вещи должны быть возможны с Template Haskell.
Фактически это было отражено в том, что я считаю исходной бумагой шаблона Haskell (поиск zipn в тексте):
http://research.microsoft.com/en-us/um/people/simonpj/Papers/meta-haskell/meta-haskell.pdf
Но я думаю, что код никогда не работал, см. это:
http://www.haskell.org/pipermail/template-haskell/2003-July/000126.html
(фрагменты шаблона не реализованы).
Это не было реализовано в 2003 году, но оно все еще не реализовано сегодня:
http://www.haskell.org/ghc/docs/7.6.1/html/users_guide/template-haskell.html (фрагменты шаблона не поддерживаются)
Однако существует реализация zipWithN с использованием шаблона haskell:
http://www.haskell.org/haskellwiki/Template_Haskell#zipWithN
Я проверил, что он работает с этой тестовой программой:
{-# LANGUAGE TemplateHaskell #-}
import Zipn
main = do
let l1 = [1,2,3]
let l2 = [5,6,7]
let l3 = [7,4,8]
print $ $(zipWithN 3) (,,) l1 l2 l3
В модуле Zipn я вставил zipn, просто переименовал zipWithN для ясности (и не забудьте добавить прагма TemplateHaskell вверху). Заметим, что N фактически закодирован здесь дважды, потому что мне нужно было дать (,,)
как функцию "с". Вам нужно будет изменить количество запятых в зависимости от N.
(,,)
означает \a b c -> (a,b,c)
Я думаю, что кто-то с хорошими навыками Шаблона Хаскелла (который не мой случай в этой точке) мог бы сделать прямой zipN с использованием шаблона Haskell.
Ответ 6
Это нетривиально, но это выполнимо. См. этот пост в блоге. Я не знаю, было ли это сделано в какой-то библиотеке.
Вот другая версия, которая проще. На самом деле это можно было бы нарезать-n-вставить здесь:
{-# LANGUAGE MultiParamTypeClasses
, FunctionalDependencies
, FlexibleInstances
, UndecidableInstances
#-}
-- |
-- Module : Data.List.ZipWithN
-- Copyright : Copyright (c) 2009 wren ng thornton
-- License : BSD3
-- Maintainer : [email protected]
-- Stability : experimental
-- Portability : non-portable (MPTCs, FunDeps,...)
--
-- Provides a polyvariadic 'map'/'zipWith' like the @[email protected] in Scheme.
-- For more details on this style of type hackery, see:
--
-- * Chung-chieh Shan, /A polyvariadic function of a non-regular/
-- /type (Int->)^N ([]^N e)->.../
-- <http://okmij.org/ftp/Haskell/polyvariadic.html#polyvartype-fn>
----------------------------------------------------------------
module Data.List.ZipWithN (ZipWithN(), zipWithN) where
-- | This class provides the necessary polymorphism. It is only
-- exported for the sake of giving type signatures.
--
-- Because we can't do functor composition without a lot of noise
-- from newtype wrappers, we use @[email protected] and @[email protected] to precompose the
-- direct/list functor with the reader functor and the return type.
class ZipWithN a gr kr | kr -> gr a where
_zipWithN :: [a -> gr] -> [a] -> kr
instance ZipWithN a b [b] where
_zipWithN = zipWith ($)
instance ZipWithN b gr kr => ZipWithN a (b -> gr) ([b] -> kr) where
_zipWithN = (_zipWithN .) . zipWith ($)
-- | Polyadic version of 'map'/'zipWith'. The given type signature
-- isn't terribly helpful or intuitive. The /real/ type signature
-- is:
--
-- > zipWithN :: {forall a}^N. ({a->}^N r) -> ({[a]->}^N r)
--
-- Note that the @[email protected] type variables are meta and so are independent
-- from one another, despite being correlated in N across all
-- repetitions.
zipWithN :: (ZipWithN a gr kr) => (a -> gr) -> [a] -> kr
zipWithN = _zipWithN . repeat
Если вы только начинаете изучать Haskell, отложите его понимание в течение некоторого времени:)
Ответ 7
Обобщающий zipping на самом деле довольно прост. Вам просто нужно написать специализированные версии комбинаторов Applicative
для ZipList
:
z :: [a -> b] -> [a] -> [b]
z = zipWith ($)
infixl 4 `z`
Теперь вы можете скопировать столько списков, сколько хотите:
f <$> xs `z` ys `z` zs
или, альтернативно:
repeat f `z` xs `z` ys `z` zs
Ответ 8
Для определенного количества списков вы можете сделать что-то вроде этого:
> let l1 = [1,2,3]
> let l2 = "abc"
> let l3 = [10.0, 11.0, 12.0]
> let l4 = [True, False, False]
> [ (e1,e2,e3,e4) | (((e1,e2),e3),e4) <- zip (zip (zip l1 l2) l3) l4 ]
[(1,'a',10.0,True),(2,'b',11.0,False),(3,'c',12.0,False)]
Это не общая функция, а шаблон, который вы можете применить к другому количеству списков.
Ответ 9
Если все ваши данные одного типа, вы можете сделать:
import Data.List (transpose)
zipAllWith :: ([a] -> b) -> [[a]] -> [b]
zipAllWith _ [] = []
zipAllWith f xss = map f . transpose $ xss
zipAll = zipAllWith id
Пример:
> zipAll [[1, 2, 3], [4, 5, 6], [7, 8]]
[[1,4,7],[2,5,8],[3,6]]