В 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?
Обобщение zip может быть достигнуто с помощью аппликативной нотации . Немного неприятно использовать из-за оборачивания / разворачивания нового типа, но если вы делаете что-то, что невозможно сделать с zipWithn
для достаточно малых n, вы, вероятно, уже на достаточно высоком уровне абстракция, в которой все равно отсутствуют условные обозначения.
Тип - ZipList a
, и его аппликативный экземпляр объединяет списки.Например:
(+) <$> ZipList [1,2] <*> ZipList [3,4] == ZipList [4,6]
Это обобщается на функции произвольной арности и типа с использованием частичного применения:
(+) <$> 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 для получения дополнительной информации об аппликативной нотации.
Список списков можно транспонировать:
>>> import Data.List
>>> transpose [l1,l2,l3]
[[1,5,7],[2,6,4],[3,7,8]]
Это нетривиально, но выполнимо. См. это сообщение в блоге . Я не знаю, превратилось ли это в какую-то библиотеку.
Вот другая версия , которая проще. Его можно на самом деле вырезать и вставить здесь:
{-# LANGUAGE MultiParamTypeClasses
, FunctionalDependencies
, FlexibleInstances
, UndecidableInstances
#-}
-- |
-- Module : Data.List.ZipWithN
-- Copyright : Copyright (c) 2009 wren ng thornton
-- License : BSD3
-- Maintainer : wren@community.haskell.org
-- Stability : experimental
-- Portability : non-portable (MPTCs, FunDeps,...)
--
-- Provides a polyvariadic 'map'/'zipWith' like the @map@ 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 @gr@ and @kr@ 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 @a@ 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, отложите понимание этого на некоторое время :)
Похоже, есть также zip3
( документ ) и zip4
( doc ) в Haskell. Но zipn кажется сложным из-за сильной системы шрифтов. Вот хорошее обсуждение , которое я нашел во время своего исследования.
Если все ваши данные одного типа, можно сделать так:
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]]
Для определенного количества списков вы можете сделать что-то вроде этого:
> 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)]
Это не общая функция, а шаблон, который вы можете применить к другому количеству списков.