2010-03-18 81 views
16

Trong python zip chức năng chấp nhận số lượng danh sách tùy ý và nén chúng lại với nhau.Làm cách nào để nén nhiều danh sách trong Haskell?

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

Làm cách nào tôi có thể zip cùng nhau nhiều danh sách trong haskell?

+1

Đây là hàm zip3. –

+1

Có, 'zip3' là để nén 3 danh sách. –

Trả lời

33

Một tổng quát của zip có thể đạt được bằng Applicative Notation. Đó là một chút khó chịu để sử dụng vì gói mới/unwrapping, nhưng nếu bạn đang làm một cái gì đó mà không thể được thực hiện với một zipWithn cho n nhỏ hợp lý, bạn có thể đã ở mức độ cao trừu tượng, nơi các cơn đau hợp lý là vắng mặt.

Loại là ZipList a và phiên bản ứng dụng của nó nén các danh sách cùng nhau. Ví dụ:

(+) <$> ZipList [1,2] <*> ZipList [3,4] == ZipList [4,6] 

này khái quát chức năng của arity tùy ý và gõ sử dụng ứng dụng phần:

(+) <$> ZipList [1,2] :: ZipList (Int -> Int) 

Xem cách (+) là một phần áp dụng ở đây?

Nếu bạn không thích thêm ZipList và getZipList ở khắp mọi nơi, bạn có thể tái tạo các ký hiệu dễ dàng đủ:

(<$>) :: (a -> b) -> [a] -> [b] 
(<$>) = map 

(<*>) :: [a -> b] -> [a] -> [b] 
(<*>) = zipWith ($) 

Sau đó các ký hiệu cho zipWith f a b c d ... là:

f <$> a <*> b <*> c <*> d <*> ... 

ký hiệu applicative là rất kỹ thuật mạnh mẽ và tổng quát có phạm vi rộng hơn nhiều so với việc nén tổng quát. Xem Typeclassopedia để biết thêm về ký hiệu áp dụng.

+3

Để giúp giải quyết vấn đề gói/mở gói mới, hãy xem gói newtype http://hackage.haskell.org/package/newtype –

+0

Liên kết Typeclassopedia đang đưa ra 404. – sean

+0

@sean, fixed, thanks – luqui

9

Có vẻ như cũng có zip3 (doc) và chức năng zip4 (doc) trong Haskell. Nhưng zipn có vẻ phức tạp vì hệ thống kiểu mạnh. Here is a good discussion Tôi đã tìm thấy trong quá trình nghiên cứu của mình.

+0

Ví dụ tương ứng cho 'zip3': http://www.zvon.org/other/haskell/Outputprelude/zip3_f.html – kennytm

+0

Bạn có nghĩa là chỉ IMPOSSIBLE của nó trong haskell ?? –

+0

Tôi không biết. Có thể ai đó khác biết một mẹo. Nhưng tôi nghĩ rằng cơ hội thấp, bởi vì không có câu trả lời hay trong danh sách gửi thư và thư viện chuẩn đã triển khai tất cả các chức năng zip2, zip3 ... thay vào đó ... – tux21b

20

Bạn có thể transpose một danh sách liệt kê:

>>> import Data.List 
>>> transpose [l1,l2,l3] 
[[1,5,7],[2,6,4],[3,7,8]] 
+2

Chỉ hoạt động nếu tất cả các đối số có cùng loại và đó cũng là lý do tại sao có vẻ khó thực hiện một tệp zipN chung. – tux21b

+0

có sự khác biệt về hành vi: chuyển [[1,2,3], [4,5,6,7]] => [[1,4], [2,5], [3,6], [7] ]. zip không bao gồm phần tử cuối cùng đó. Nhưng appart từ đó gotcha nó là một người chiến thắng! Chắc chắn gia nhập hộp công cụ của tôi. –

+0

Tôi đã viết chức năng này để tránh các hành vi khác nhau trong trường hợp độ dài của danh sách không khớp: 'danh sách zipLists = takeWhile (\ x -> độ dài x == danh sách độ dài) $ transpose list' –

3

Đó là không tầm thường, nhưng nó là doable. Xem this blog post. Tôi không biết liệu điều này được thực hiện vào một số thư viện.

Đây là another version, đơn giản hơn. Cái này thực sự có thể được cắt-n-dán ở đây:

{-# 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 

Nếu bạn chỉ mới bắt đầu học Haskell, hoãn hiểu nó trong một thời gian :)

+0

Tôi không hiểu một từ. Tôi chỉ có kiến ​​thức rất cơ bản về Haskell. Bạn có thể trích xuất chức năng đó từ bài đăng đó và đăng nó ở đây không? –

0

Nếu tất cả các dữ liệu của bạn là cùng loại bạn có thể làm:

import Data.List (transpose) 

zipAllWith :: ([a] -> b) -> [[a]] -> [b] 
zipAllWith _ [] = [] 
zipAllWith f xss = map f . transpose $ xss 

zipAll = zipAllWith id 

Ví dụ:

> zipAll [[1, 2, 3], [4, 5, 6], [7, 8]] 
[[1,4,7],[2,5,8],[3,6]] 
1

Đối với một số cụ thể của danh sách, bạn có thể để một cái gì đó như thế này:

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

Đây không phải là chức năng chung, nhưng bạn có thể áp dụng mẫu cho một số danh sách khác.

6

GHC also supports parallel list comprehensions:

{-# LANGUAGE ParallelListComp #-} 

[(x,y) | x <- [1..3] 
     | y <- ['a'..'c'] 
     ] 

==> [(1,'a'),(2,'b'),(3,'c')] 

tôi chỉ thử nghiệm nó lên đến 26 biến song song, mà nên là đủ cho tất cả các mục đích thực tế.

Đó là một chút hacky (và không chuẩn) mặc dù, vì vậy trong trường hợp bạn đang viết một cái gì đó nghiêm trọng, ZipList có thể là cách tốt hơn để đi.

+0

Sự khác biệt chính giữa điều này và việc hiểu danh sách thông thường là x không còn nằm trong phạm vi của phần quyền đọc hiểu của y? – CMCDragonkai

3

Việc tổng hợp nén thực sự khá dễ dàng. Bạn chỉ cần viết các phiên bản đặc biệt của Applicative combinators cho ZipList:

z :: [a -> b] -> [a] -> [b] 
z = zipWith ($) 

infixl 4 `z` 

Bây giờ bạn có thể zip như nhiều danh sách như bạn muốn:

f <$> xs `z` ys `z` zs 

hoặc cách khác:

repeat f `z` xs `z` ys `z` zs 
4

tôi nghĩ rằng nó có lẽ là giải pháp ít thanh lịch nhất được đề xuất, nhưng vì lợi ích của sự hoàn chỉnh, nên thêm rằng những điều như vậy nên có thể với Templa te Haskell.

Đây là thực tế được đề cập trong những gì tôi nghĩ là giấy gốc Template Haskell (Tìm kiếm zipn trong văn bản): http://research.microsoft.com/en-us/um/people/simonpj/Papers/meta-haskell/meta-haskell.pdf

Nhưng tôi nghĩ rằng mã không bao giờ thực tế làm việc, thấy điều này: http://www.haskell.org/pipermail/template-haskell/2003-July/000126.html (lát mẫu không được triển khai).

đó đã không được thực hiện vào năm 2003, nhưng nó vẫn không được thực hiện ngày hôm nay: http://www.haskell.org/ghc/docs/7.6.1/html/users_guide/template-haskell.html (mẫu lát không được hỗ trợ)

Tuy nhiên có một thực hiện zipWithN sử dụng mẫu Haskell: http://www.haskell.org/haskellwiki/Template_Haskell#zipWithN

Tôi có đã xác minh rằng nó hoạt động với chương trình thử nghiệm này:

{-# 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 

Trong mô-đun Zipn, tôi đã dán zipn, chỉ cần đổi tên thành zipWithN cho c larity (và nhớ thêm TemplateHaskell pragma ở phía trên). Lưu ý rằng N thực tế bị bẻ khóa hai lần ở đây, bởi vì tôi đã phải cung cấp (,,) làm hàm "with". Bạn sẽ phải thay đổi số lượng dấu phẩy tùy thuộc vào N.

(,,) là viết tắt của \a b c -> (a,b,c)

Tôi đoán một ai đó với mẫu tốt kỹ năng Haskell (mà không phải là trường hợp của tôi vào thời điểm này) có thể làm cho một zipN thẳng sử dụng Template Haskell.

Các vấn đề liên quan