如果没有手工编写的递归,如何在Haskell中突破纯循环?
我想写一个函数,通过一个列表更新累加器,直到累加器达到某个条件,或者我到达列表的末尾。例如,累加器一达到零就停止的乘积函数 我知道如何手工编写递归代码:如果没有手工编写的递归,如何在Haskell中突破纯循环?,haskell,recursion,fold,Haskell,Recursion,Fold,我想写一个函数,通过一个列表更新累加器,直到累加器达到某个条件,或者我到达列表的末尾。例如,累加器一达到零就停止的乘积函数 我知道如何手工编写递归代码: {-# LANGUAGE BangPatterns #-} prod :: [Integer] -> Integer prod xs = go 1 xs where go 0 _ = 0 go !acc [] = acc go !acc (x:xs) = go (acc * x
{-# LANGUAGE BangPatterns #-}
prod :: [Integer] -> Integer
prod xs =
go 1 xs
where
go 0 _ = 0
go !acc [] = acc
go !acc (x:xs) = go (acc * x) xs
但是有没有办法用折叠和其他高阶函数来编码呢
我想到的一件事是定义
mult 0 _ = 0
mult x y = x * y
然后使用foldl’。但是,这种情况不会提前发生,因此有点浪费性能
我们不能使用foldr,因为它以错误的顺序遍历列表,并且它“提前爆发”的方式是查看列表中的元素,而不是查看累加器(如果累加器的类型与列表元素不同,这将很重要).看来,
scanl
是最简单的列表组合器,可以为您提供所需的内容。例如,这不会计算第二个列表的1
到10
Prelude> let takeUntil _ [] = []; takeUntil p (x:xs) = if p x then [x] else (x: takeUntil p xs)
Prelude> (last . takeUntil (==0) . scanl (*) 1) ([1..10] ++ [0..10])
0
takeUntil
似乎不存在于标准库中。这就像是takeWhile
,但也会给出第一个失败的元素
如果您想正确地执行此操作,您应该注意最后的部分。
如果您想要一个功能强大的通用解决方案,我想mapAccumL
是一个不错的选择。一个简单的方法是在允许提前退出的monad中进行计算,例如或者可能
:
{-# LANGUAGE BangPatterns #-}
import Data.Functor ((<$))
import Data.Maybe (fromMaybe)
import Control.Monad
prod :: [Integer] -> Integer
prod = fromMaybe 0 . prodM
-- The type could be generalized to any MonadPlus Integer
prodM :: [Integer] -> Maybe Integer
prodM = foldM (\ !acc x -> (acc * x) <$ guard (acc /= 0)) 1
您可以使用Monoids
和mconcat
@viorior:这在累加器和列表元素具有不同类型的一般情况下不起作用。您可以在本例中使用foldr
,因为foldr
和mult
运算符一样是非严格的,所以foldr1 mult$[1,2]+[0..]
非常快速地为您提供0
。也许你最好举一个加法的例子,当累加器为零时,你就停止,这样你就不能从数据中判断它是什么:如果你想让它在累加器上短路,你想请求mult\u0=0
而不是相反,如foldr:(a->b->b)->b->[a]->b
-第二个参数是累加器。@大卫杨:在我的实际问题中,唯一的方法是查看累加器,因为函数不是像*
那样的可交换和关联函数。我想我不应该选择“产品”作为例子。顺便说一句,你不需要bang模式!acc
因为您在它上的模式与0
匹配。这将迫使它自己,完全正确scanl
为我们提供了从左到右的流程,而foldr
是一个早期突破:takeUntil p=foldr(\x r->head([[x]| px]+[r])[]last
在这里不是局部的,因为scanl
总是生成一个非空列表。:)当然,在scanl
适合的地方,iterate
和unbover
也是如此<代码>展开器甚至会自动停止。最后。takeUntil p
可以重写为头。takeWhile(not.p)
也有同样的效果。@WillNess:确实这里没有提到对last
的偏爱,但理想的写作方式应该是不使用last
,并且通过构造可以明显看出整体性。@chunksOf50你的意思是dropWhile(not.p)>>head
。。。但这也不正确(如果p
不成立怎么办?)。
> prod ([1..5] ++ [0..])
0