如果我有这样的清单:
lst = [4,4,4,5,3,3,9]
我可以使用group
创建:[[4,4,4],[5],[3,3],[9]]
。
如果我有一个指向列表的索引,例如,i = 4
,它指向第一个3
元素,我怎样才能最好地编写一个列表的函数和索引,并返回索引指向的组的大小(在这种情况下,2,组的长度为3)?
我知道我可以非常强制地写出来,而且我也可以和map length . group
一起敲打一些东西并计算我找到索引时经过多少组,但在我看来,必须有一个更好的方式来做到这一点。欢迎所有建议。
答案 0 :(得分:5)
这是一个非常明确的解决方案,所以我可以解释每一步。
制作分组列表:
lst = [4,4,4,5,3,3,9]
lst1 = group lst
接下来,用长度替换组:
lst2 = map length lst1
这给了我们lst2 = [3,1,2,1]
现在用长度列表替换长度,这些列表也包含每个位置的长度:
lst3 = map (\l -> replicate l l) lst2
现在我们有lst3 = [[3,3,3],[1],[2,2],[1]]
,这几乎是我们需要的,但我们不想要内部结构:
lst4 = concat lst3
现在我们有一些与原始列表相同的长度,但每个位置的组长度而不是原始值。所以lst4!!4 = 2
。
此解决方案使用评论中的想法简化了我的原始解决方案。
正如该评论中所指出的,还可以进行进一步的调整 - 将两个map
和concat
作为concatMap
,并使用join
(->) r
的{{1}}实例“简化”Monad
,为整个管道提供此内容:
\l -> replicate l l
答案 1 :(得分:1)
如果您有计算累积总和的函数,那么您可以使其相当简单:
cumsum :: Num a => [a] -> [a]
cumsum xs = go xs 0
where
go [] _ = []
go (x:xs) i = let n = x + i in n : go xs n
groupSizeAt :: [[a]] -> Int -> Int
groupSizeAt xs i =
let groups = zip xs $ cumsum $ map length xs
targetGroup = head $ dropWhile ((<= i) . snd) groups
in length $ fst targetGroup
这背后的逻辑是计算组长度的累积和,然后丢弃组,直到这个累积和大于你要搜索的索引,然后抓住第一个,丢弃累积总和,再次计算长度。有一些冗余,但对于小组来说没有什么是太贵的(如果您的组大小在100或1000的数量级,您可能想要修改它以避免必须计算目标组的长度两次)
答案 2 :(得分:1)
如果您实际上不需要这些组,只需要您的结果大小,请查看:
help :: Int -> [Int] -> Int
help i xs = let val = xs !! i in
(length . takeWhile (val==) . dropWhile (val/=)) xs
size :: Int -> [Int] -> Int
size i xs = if ( i>0 && xs !! (i-1) == xs !! i)
then size (i-1) xs
else help i xs