Как архивировать несколько списков в 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?

16
задан Don Stewart 18 April 2011 в 23:00
поделиться

6 ответов

Обобщение 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 для получения дополнительной информации об аппликативной нотации.

34
ответ дан 30 November 2019 в 15:04
поделиться

Список списков можно транспонировать:

>>> import Data.List
>>> transpose [l1,l2,l3]
[[1,5,7],[2,6,4],[3,7,8]]
22
ответ дан 30 November 2019 в 15:04
поделиться

Это нетривиально, но выполнимо. См. это сообщение в блоге . Я не знаю, превратилось ли это в какую-то библиотеку.

Вот другая версия , которая проще. Его можно на самом деле вырезать и вставить здесь:

{-# 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, отложите понимание этого на некоторое время :)

3
ответ дан 30 November 2019 в 15:04
поделиться

Похоже, есть также zip3 ( документ ) и zip4 ( doc ) в Haskell. Но zipn кажется сложным из-за сильной системы шрифтов. Вот хорошее обсуждение , которое я нашел во время своего исследования.

9
ответ дан 30 November 2019 в 15:04
поделиться

Если все ваши данные одного типа, можно сделать так:

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]]
0
ответ дан 30 November 2019 в 15:04
поделиться

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

> 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)]

Это не общая функция, а шаблон, который вы можете применить к другому количеству списков.

1
ответ дан 30 November 2019 в 15:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: