我有一个列表[a,b,c,d,e]
和一个初始值u
(显然a,b,c,d,e
代表值)。我想将一个函数应用于e
和u
,让我们说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中实现它?
谢谢, 萨姆。
答案 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
我发现在学习一门语言时,人们常常会有一个问题:“有更简单的方法吗?”答案几乎总是肯定的。