【问题标题】:haskell, map two lists adding each element of one with each of the other listhaskell,映射两个列表,将一个列表的每个元素与另一个列表中的每个元素相加
【发布时间】:2014-04-21 09:51:39
【问题描述】:

有一个列表理解风格的函数

f1 :: [Int] -> [Int] -> [Int]
f1 xs ys = [ x+y | x <- xs, y <-ys ]

这并不复杂。 示例输出:

*Main> f2 [1,2,3,4] [5,6,7,8]
[6,7,8,9,7,8,9,10,8,9,10,11,9,10,11,12]

任务是转换这个和其他几个函数,只使用mapfilterconcat。 其他功能没有问题,但我不知道如何在这里计算嵌套的map。 第一个列表的每个元素都将添加到另一个列表中的每个元素。

【问题讨论】:

    标签: haskell list-comprehension higher-order-functions


    【解决方案1】:

    我不会直接给出这个作业的解决方案,而是用更笼统的术语来解释它。您现在可能不了解其中的大部分内容,但至少应该能够挑选出您需要的内容!

    map 只是 Functor 将函数“提升”为函子函数的方法的专用版本(用于列表函子):

    class Functor f where
      fmap :: (a->b) -> f a->f b
    

    现在,列表是一种特别强大的函子:applicative 函子,甚至是 monad。 Applicative 让许多程序员感到困惑,但您的示例非常适合:

    前奏曲> :m +Control.Applicative
    Prelude Control.Applicative> let f1 xs ys = (+) xs ys
    Prelude Control.Applicative> :t f1
    f1 :: (Num b, Applicative f) => f b -> f b -> f b
    Prelude Control.Applicative> f1 [1,2,3,4] [5,6,7,8]
    [6,7,8,9,7,8,9,10,8,9,10,11,9,10,11,12]

    当然你也可以简单写

    Prelude Control.Applicative> (+) [1,2,3,4] [5,6,7,8]
    [6,7,8,9,7,8,9,10,8,9,10,11,9,10,11,12]

    看起来很简单,但它是如何工作的? Applicative 类的令人困惑之处在于,您不断地将 curried 函数插入函子中,在其中部分评估它们,并以某种方式从外部检索结果。使用 monad 时,您不会这样做,因此它们更容易掌握。只使用Monad类型的类,也可以做例子(MonadApplicative严格)。

    Haskell 列表推导式实际上只是 monad 操作的语法糖!

                       [ x+y | x <- xs, y <- ys ]
    

    是糖

                       xs >>= \x -> ys >>= \y -> return (x+y)
    

    或带有额外的括号:

                       xs >>= ( \x -> ( ys >>= ( \y -> return (x+y) ) ) )
    

    好吧,您可以使用以下规则将其转换为只有 mapconcat 的东西:

    • ys &gt;&gt;= (\y -&gt; return (f y))map f ys 相同。
    • zs &gt;&gt;= (\z -&gt; g z)concat (map g zs) 相同。

    【讨论】:

      【解决方案2】:

      一种方法是:

      f1 xs ys = concat $ map (\x -> map (\y -> x + y) ys) xs
      

      ghci 中的演示:

      λ> f1 [1,2,3,4] [5,6,7,8]
      [6,7,8,9,7,8,9,10,8,9,10,11,9,10,11,12]
      

      【讨论】:

      • 你也可以把它减少到concat $ map (\x -&gt; map (+x) ys) xs
      【解决方案3】:
      [ x+y | x <- xs, y <-ys ]
      

      相同
      do {x <- xs; y <- ys; return (x + y)}
      

      相同
      xs >>= \x -> ys >>= \y -> return (x + y)
      

      &gt;&gt;= 称为绑定。这里使用的是著名的列表单子。对于列表,return 定义为return x = [x],bind 定义为xs &gt;&gt;= f = concatMap f xs,而concatMap,顾名思义,就是concatmap 的简单组合。

      list monad 背后的想法是,使用x &lt;- xs,您可以不确定地选择列表中的某个任意元素,然后对其进行一些计算,最后,您会得到一个包含所有可能结果的列表。所以在你的例子中,

      do {x <- xs; y <- ys; return (x + y)}
      

      分别从xsys 中非确定性地选择xy,您将获得所有可能结果的列表。

      我现在将让您使用这些知识将您的列表理解转换为仅使用 mapconcat 的表达式。

      至于filter,列表推导中的条件b被转换为列表monad中的guard b,如果你想要一个非单子表达式,可以用filter表示。

      【讨论】:

        【解决方案4】:

        通用列表推导式具有以下形式

        [ e | qual1 , qual2 , ... , qualN ]
        

        其中e 是一个表达式,qual 是限定符。限定符可以是三种形式:生成器 (x&lt;-list)、布尔值守卫 (x/=0) 和本地声明 (let x = ...)。由于您只使用生成器,因此我将重点介绍这些。

        根据Haskell definition,列表理解可以翻译如下

        [ e | x<-l , quals ] = concat $ map (\x -> [ e | quals ]) l
        [ e | ]              = [ e ]  -- singleton list
        

        因此您可以将这些应用到[ x+y | x &lt;- xs, y &lt;-ys ] 并获得解决方案(这基本上是 Sibi 的答案)。

        【讨论】:

          猜你喜欢
          • 2023-03-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-01-19
          • 2021-09-11
          相关资源
          最近更新 更多