【问题标题】:Rewriting zipWith function using list comprehension使用列表推导重写 zipWith 函数
【发布时间】:2015-11-13 00:54:31
【问题描述】:

我已经使用递归重写了 zipWith 函数,现在我正在尝试使用列表推导来重写它。我遇到了很多绑定错误,我知道我的第二行不正确。这是我使用递归的功能,就像 zipWith 一样:

zipW :: (a -> b -> c) -> [a] -> [b] -> [c] 
zipW _ [] _ = []  
zipW _ _ [] = []  
zipW f (x:xs) (y:ys) = f x y : zipW f xs ys

这是我尝试将其重写为列表理解:

zipW2 :: (a -> b -> c) -> [a] -> [b] -> [c]
zipW2 f xs ys = [f x y | (x, y) <- zipW2 f xs ys]

我不确定如何更正第二条语句,使其像 zipWith 一样工作,并允许我选择运算符。

【问题讨论】:

    标签: list haskell zip list-comprehension winghci


    【解决方案1】:

    您需要Parallel List Comprehensions 分机:

    {-# LANGUAGE ParallelListComp #-}
    
    zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
    zipWith' f xs ys = [f x y | x <- xs | y <- ys] 
    

    【讨论】:

    • 谢谢,我知道这行得通;我还不知道并行列表理解。但是,我正在尝试导入 ParallelListComp 语句,以便它可以在我的 Haskell 窗口中工作,我使用记事本并将文件加载到 WinCHCi 中。如果不破坏该导入语句,我无法让它加载文件。
    • @T-Bird in ghci 你可以通过:set -XParallelListComp激活扩展
    【解决方案2】:

    原来zipWith有三种情况:

    1. 当第一个列表为空时
    2. 当第二个列表为空时
    3. 当两个列表都不为空时

    第三种情况在参数尾部递归调用zipWith,再次进行情况分析。

    在您的定义中,您只有一种情况 - 列表推导,因此任何递归调用都将返回到该情况。如果没有案例分析,您可能会在这里永远循环:

    >>> let myZipWith f xs ys = [ f x y | (x,y) <- myZipWith f xs ys ]
    >>> myZipWith (,) [] []
    ^CInterrupted.
    

    此外,因为您在递归调用中使用 f,但要求递归输出是一对,所以您隐含要求 f x y 产生一对:

    >>> :t myZipWith 
    myZipWith :: (t2 -> t3 -> (t2, t3)) -> t -> t1 -> [(t2, t3)]
    

    解决方案是不递归,而是直接考虑每一对。

    你可以use behzad.nouri's solution of enabling the ParallelListComp language extension:

    >>> :set -XParallelListComp
    >>> let myZipWith f xs ys = [ f x y | x <- xs | y <- ys ]
    >>> myZipWith (+) [1,2,4] [0,10,20]
    [1,12,24]
    

    ParallelListComp 使列表理解中的第二个(及以后的)竖线字符 (|) 成为合法语法,与之前的列表并行(类似 zip)遍历这些列表。

    很高兴知道这与普通列表推导有何不同,在普通列表推导中,您使用逗号分隔从中提取的每个列表。使用逗号进行嵌套迭代,在结果列表中被展平:

    >>> let notZipWith f xs ys = [ f x y | x <- xs, y <- ys ]
    >>> notZipWith (+) [1,2,4] [0,10,20]
    [1,11,21,2,12,22,4,14,24]
    

    Using the ParallelListComp extension is really just syntatical sugar for the original zipWith,所以你可能会认为它作弊。

    你也可以只依赖原来的zip

    >>> let myZipWith f xs ys = [ f x y | (x,y) <- zip xs ys ]
    >>> myZipWith (+) [1,2,4] [0,10,20]
    [1,12,24]
    

    但既然zip被定义为zipWith (,),那也可能是作弊。

    另一种方法是使用索引:

    >>> let myZipWith f xs ys = [ f x y | i <- [0..min (length xs) (length ys) - 1], let x = xs !! i, let y = ys !! i ]
    >>> myZipWith (+) [1,2,4] [0,10,20]
    [1,12,24]
    

    但这将非常低效,因为!! 是线性时间运算,使得myZipWith 是二次的,而zipWith 是线性的:

    >>> :set +s
    >>> last $ myZipWith (+) (replicate 10000000 1) (replicate 10000000 2)
    3
    (4.80 secs, 3282337752 bytes)
    >>> last $ zipWith (+) (replicate 10000000 1) (replicate 10000000 2)
    3
    (0.40 secs, 2161935928 bytes)
    

    我确信还有其他不好的方法可以通过列表理解创建与 zipWith 等效的方法,但我并不十分相信有一种好的方法,即使是上面的方法。

    【讨论】:

      猜你喜欢
      • 2011-07-24
      • 1970-01-01
      • 2018-08-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多