【问题标题】:Haskell list iterationHaskell 列表迭代
【发布时间】:2013-01-26 17:18:20
【问题描述】:

我有一个列表[a,b,c,d,e] 和一个初始值u(显然a,b,c,d,e 代表值)。我想对eu 应用一个函数,比如说f(e,u)。然后我想应用函数f(d, f(e, u)) 然后f(c, f(d, f(e, u))) 等。我看过“迭代”,但我不知道如何将迭代应用于列表中的每个元素。

我的清单:

a = take 101 (0 : concat [[(1%1),(2*k%1),(1%1)] | k <- [1..40]])

我将如何在 Haskell 中实现这一点?

谢谢, 山姆。

【问题讨论】:

  • 你能澄清一下你的问题吗?我认为mapfold 在这里可能会有所帮助...
  • 我明天晚上会坐下来,尝试为未来的读者澄清我的问题。不幸的是,我写的时候措辞有问题。
  • 计算 e 是我们吗? :-)

标签: haskell functional-programming


【解决方案1】:

你想要foldr :: (a -&gt; b -&gt; b) -&gt; b -&gt; [a] -&gt; b。这是列表数据结构的一般折叠。将其视为用两个提供的参数替换列表中的所有 (:)[] 构造函数。

例如,如果我们要将列表[1, 2, 3] 的数字相加,构造为1 : (2 : (3 : [])),我们可以找到(:)[]替换,例如@ 987654328@和0,即1 + (2 + (3 + 0))。因此,我们可以将sum :: Num a =&gt; [a] -&gt; a 实现为foldr (+) 0

【讨论】:

    【解决方案2】:

    您描述的函数称为“fold”,在本例中为“右折叠”,因为它是从右向左应用的。它在 Prelude 中作为foldr 函数实现。

    例如,如果我们使用连接两个字符串的函数(++),并将其应用于初始元素和字符串列表:

    Prelude> foldr (++) "u" ["a", "b", "c", "d", "e"]
    "abcdeu"
    

    【讨论】:

    • foldr 不能从右到左工作,它是 right-associative fold
    • 谢谢,这正是我想要的! Haskell 的功能继续让我惊叹。
    【解决方案3】:

    干得好,您自己发现了foldr! (我希望这听起来没有嘲讽之类的意思,不是那个意思;大多数人都觉得褶皱不自然,并且必须非常努力地思考才能理解它!)

    我建议你处理这些情况的方法是尝试自己编写你想要的函数,并弄清楚类型,然后在Hoogle中搜索该类型,看看是否已经存在这样的函数。

    在这种情况下,您可以尝试以这种方式编写函数。我们称之为foo

    -- If we see an empty list the result should be u
    foo u f [] = u
    -- If we're given a a non-empty list we recurse down the list to get a partial 
    -- result, then "add on" to it:
    foo u f (x:xs) = f x (foo u f xs)
    

    一旦你定义了这个函数,你就可以将它加载到ghci 并使用它的:t 命令来查找它的类型:

    *Main> :load "../src/scratch.hs"
    [1 of 1] Compiling Main             ( ../src/scratch.hs, interpreted )
    Ok, modules loaded: Main.
    *Main> :t foo
    foo :: t1 -> (t -> t1 -> t1) -> [t] -> t1
    

    现在我们可以search Hoogle for the type t1 -&gt; (t -&gt; t1 -&gt; t1) -&gt; [t] -&gt; t1。最上面的结果是foldr,其类型为(a -&gt; b -&gt; b) -&gt; b -&gt; [a] -&gt; b——与foo 相同,但变量重命名并且参数顺序颠倒了。搜索结果还告诉我们该函数在 Prelude 模块中——Haskell 默认加载的模块。你可以点击结果找到its definition in the documentation,它用这个等式描述:

    foldr f z [x1, x2, ..., xn] == x1 `f` (x2 `f` ... (xn `f` z)...)
    

    他们使用f 函数作为中缀运算符,我希望这不会让您感到困惑,但以防万一我们可以将其重写为:

    foldr f z [x1, x2, ..., xn] == f x1 (f x2 ... (f xn z) ...)
    

    这正是你想要的行为。


    为什么我对上面的foldr 做了这么大的事情?因为foldr 实际上是拆分列表的最基本功能。这样看类型:

    foldr :: (a -> b -> b)  -- ^ What to do with a list node
          -> b              -- ^ What to do with the empty list
          -> [a]            -- ^ A list
          -> b              -- ^ The final result of "taking the list apart."
    

    原来很多列表函数都可以用foldr来写:

    map f = foldr step []
        where step x rest = (f x):rest
    
    -- | Append two lists
    xs (++) ys = foldr (:) ys xs
    
    -- | Filter a list, keeping only elements that satisfy the predicate.
    filter pred = foldr step []
        where step x rest | pred x    = x:rest
                          | otherwise = rest
    
    -- | Find the first element of a list that satisfies the predicate.
    find pred = foldr step Nothing
        where step x r | pred x    = Just x
                       | otherwise = r
    

    【讨论】:

      【解决方案4】:

      右折看起来不错。

      foo :: (a -> b -> b) -> b -> [a] -> b
      foo f u xs = foldr (\x acc -> f x acc) u xs
      

      我发现在学习一门语言时,人们经常会问“有没有更简单的方法可以做到这一点?”答案几乎总是肯定的。

      【讨论】:

      • \x acc -&gt; f x acc 与简单的f 相同。 (不过,我可以理解您为什么会出于演示目的进行扩展)
      • 和我输入 xs 的原因一样 :)
      猜你喜欢
      • 2011-01-15
      • 2021-08-06
      • 1970-01-01
      • 2017-01-04
      • 2012-10-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-05
      相关资源
      最近更新 更多