Haskell列表迭代

时间:2013-01-24 22:20:06

标签: haskell functional-programming

我有一个列表[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中实现它?

谢谢, 萨姆。

4 个答案:

答案 0 :(得分:6)

你想要foldr :: (a -> b -> b) -> b -> [a] -> b。这是列表数据结构的一般折叠。可以把它想象为用提供的两个参数替换列表中的所有(:)[]构造函数。

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

答案 1 :(得分:3)

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

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

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

答案 2 :(得分: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 -> (t -> t1 -> t1) -> [t] -> t1。最高的结果是foldr,其类型为(a -> b -> b) -> b -> [a] -> 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

答案 3 :(得分:2)

右折看起来不错。

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

我发现在学习一门语言时,人们常常会有一个问题:“有更简单的方法吗?”答案几乎总是肯定的。