使用fold检查列表Haskell中的连续True
我是哈斯克尔的新手 对于项目Euler问题,我生成了一个布尔列表使用fold检查列表Haskell中的连续True,haskell,Haskell,我是哈斯克尔的新手 对于项目Euler问题,我生成了一个布尔列表 [True, False, False, True, False. . .True] 我想写一个函数,找到前四个连续的True,然后在列表中返回它们的索引 直觉上,我知道我应该通过折叠和模式匹配来实现这一点。你能帮我吗?像这样有褶皱的东西?(我不知道如何检索元素的索引。) 谢谢你的帮助 最简单的方法是拉上拉链,然后用特制的折页。首先查看每组四个连续元素,看看它们是否都是真的: import Data.List (zipWith4
[True, False, False, True, False. . .True]
我想写一个函数,找到前四个连续的True,然后在列表中返回它们的索引
直觉上,我知道我应该通过折叠和模式匹配来实现这一点。你能帮我吗?像这样有褶皱的东西?(我不知道如何检索元素的索引。)
谢谢你的帮助 最简单的方法是拉上拉链,然后用特制的折页。首先查看每组四个连续元素,看看它们是否都是真的:
import Data.List (zipWith4, findIndex)
consec4 :: [Bool] -> [Bool]
consec4 xs = zipWith4 (\x y z w -> x && y && z && w) xs (drop 1 xs) (drop 2 xs) (drop 3 xs)
现在你只需要
fstConsec4 :: [Bool] -> Maybe Int
fstConsec4 = findIndex id . consec4
现在这不太可能是你能做的最快的,而且它不能很好地推广到更大的窗口
相反,我们可以更直接地跳进褶皱中,采取不同的方法。请注意,此特定版本在GHC 7.10或更高版本中的表现可能比在早期版本中更好。为了清晰起见,我使用了bang模式,但您可以使用seq
或$代码>可移植性,如果您愿意
让我们首先定义一个在数据中明显缺失的函数。List
:
{-# INLINE foldrWithIndex #-}
foldrWithIndex :: (Int -> a -> b -> b) -> b -> [a] -> b
foldrWithIndex c n xs = foldr go (`seq` n) xs 0
where
go x r !i = c i x (r $ i + 1)
consec :: Int -> [Bool] -> [Int]
consec n = findIndices (isPrefixOf (replicate n True)) . tails
使用此函数,我们可以轻松定义一个函数,用于查找第一组n
连续True
值的开头索引
consec :: Int -> [Bool] -> Maybe Int
consec n xs = foldrWithIndex go (`seq` Nothing) xs n
where
go _ix False r !_ = r n
go ix True _r 1 = Just (ix - n + 1)
go _ix True r need = r (need - 1)
快速查看一下它产生的GHC核心表明它可能非常有效(这是使用GHC-O2-ddump simpl-dsuppress all-dno suppress类型签名产生的):
极端性能黑客攻击
从理论上讲,应该可以做得更好,特别是在查看长列表时。特别是,如果True
和False
值都很频繁,则Bool
值上的分支可能会导致许多错误预测的分支。在这种情况下,最好将值放入一个小的位向量中,然后对其进行一些小技巧。不幸的是,要说服GHC做这类事情可能很难,但我可能会稍后再试。tldr
findIndices(\x->(x==True))$map(\x->all(=True)x)$sliding 2 bs
定义一个与Scala中类似的滑动
函数
sliding :: Int -> [a] -> [[a]]
sliding size [] = []
sliding size ls@(x:xs) = if length ls >= size then (take size ls) : sliding size xs else sliding size xs
使用滑动2
和[真、假、假、真、真、假、真、假、真、假、真、真、真]
给出
[[True,False],[False,False],[False,True],[True,True],[True,False],[False,True],[True,False],[False,True],[True,True]
然后我们可以map
查看每个子列表是否包含all
True
值,并获取与findIndices
相关的索引
findIndices(\x->(x==True))$map(\x->all(=True)x)$sliding 2 bs
[3,8]这里是另一个解决方案:
consec :: Int -> [Bool] -> [Int]
consec n = findIndices and . foldr (zipWith (:)) (repeat []) . take n . tails
这是基于其他答案的想法<代码>取n。tails
返回一个矩阵,该矩阵包含长度为的连续元素列表是否使用了折叠
要求?
因为除此之外,它可以直接用Data.List中的标准函数表示:
{-# INLINE foldrWithIndex #-}
foldrWithIndex :: (Int -> a -> b -> b) -> b -> [a] -> b
foldrWithIndex c n xs = foldr go (`seq` n) xs 0
where
go x r !i = c i x (r $ i + 1)
consec :: Int -> [Bool] -> [Int]
consec n = findIndices (isPrefixOf (replicate n True)) . tails
只要把它读成英文:findIndices
的所有trail
的replicate
-edn
timesTrue
也许我们应该简单地计算连续的True
s,而不是把它们列成子列表
consec :: Int -> [Bool] -> [Int]
consec m = map (subtract m) . findIndices (>= m) . scanl (\ c x -> if x then c + 1 else 0) 0
ghci> consec 4 [False, True, True, True, True, True, False, True, True, True, True]
[1,2,7]
我真的很喜欢你的答案的可扩展性D即使我想要385个连续的假值,这也行@DanMaheshwari,我刚刚编辑了我的答案,以展示我的第二种方法如何扩展到其他数字,以及如何重构它。(==True)
只是id
,所以它是findIndices id$map和$sliding 2b
。在每次递归调用时重新计算length
是无效的,我要么定义safeTake::Int->[a]>Maybe[a]
,要么搜索更好的方法else滑动大小xs
是else[]
。在这里使用length
是一个非常糟糕的主意——它使算法成为O(n^2)。相反,为什么不滑动n=dropLast n。地图(以n为例)。尾部
,以及带有常量xs(下降n xs)的水滴n xs=zip
?这是O(n),更好的是,正如你所希望的那样懒惰/高效。另外,findIndices(\x->(x==True))。map(\x->all(=True)x)
的拼写可能更好findIndices and
@DanielWagner@user3237465感谢您的评论,尤其是id
和和
。什么包是dropWhile
in?