我有一个双打(myList)列表,我想添加到一个新的List(someList),但是一旦新列表达到设置大小,即25,我想停止添加它。我尝试使用sum实现此功能但未成功。示例代码如下。
someList = [(a)| a< - myList,sum someList< 30]
答案 0 :(得分:2)
@DanielFischer表达这个问题的方式与Haskell的思维方式兼容。
你是否希望someList成为myList的最长前缀,其总和为< 30?
以下是我的接触方式:让我们说明一下
>>> let list = [1..20]
我们可以找到"累积总和"使用:
>>> let sums = tail . scanl (+) 0
>>> sums list
[1,3,6,10,15,21,28,36,45,55,66,78,91,105,120,136,153,171,190,210]
现在用原始列表压缩它以获得总和到那一点的元素对
>>> zip list (sums list)
[(1,1),(2,3),(3,6),(4,10),(5,15),(6,21),(7,28),(8,36),
(9,45),(10,55),(11,66),(12,78),(13,91),(14,105),(15,120),
(16,136),(17,153),(18,171),(19,190),(20,210)]
然后我们可以takeWhile
这个列表来获取我们想要的前缀:
>>> takeWhile (\x -> snd x < 30) (zip list (sums list))
[(1,1),(2,3),(3,6),(4,10),(5,15),(6,21),(7,28)]
最后我们可以摆脱用于执行此计算的累积总和:
>>> map fst (takeWhile (\x -> snd x < 30) (zip list (sums list)))
[1,2,3,4,5,6,7]
请注意,由于懒惰,这与递归解决方案一样有效 - 只需要计算它们未通过测试的点的总和。这可以看出因为解决方案适用于无限列表(因为如果我们需要计算所有总和,我们永远不会完成)。
我可能会抽象出这个并将限制作为参数:
>>> :{
... let initial lim list =
... map fst (takeWhile (\x -> snd x < lim) (zip list (sums list)))
... :}
此函数具有应满足的明显属性,即列表的总和应始终小于限制(只要限制大于0)。所以我们可以使用QuickCheck来确保我们做得对:
>>> import Test.QuickCheck
>>> quickCheck (\lim list -> lim > 0 ==> sum (initial lim list) < lim)
+++ OK, passed 100 tests.
答案 1 :(得分:0)
someList = makeList myList [] 0 where
makeList (x:xs) ys total = let newTot = total + x
in if newTot >= 25
then ys
else makeList xs (ys ++ [x]) newTot
只要它们的总和小于25,就会从myList中获取元素。
逻辑发生在makeList
。它接受输入列表的第一个元素并将其添加到运行总计中,以查看它是否大于25.如果是,我们不应将其添加到输出列表中,并且我们完成递归。否则,我们将x
放在输出列表的末尾(ys
)并继续使用输入列表的其余部分。
答案 2 :(得分:0)
您想要的行为是
ghci> appendWhileUnder 25 [1..5] [1..5]
[1,2,3,4,5,1,2,3]
因为总和为21并且加上4会使它达到25。
好的,一种解决方法是将++
添加到appendWhileUnder n xs ys = takeWhileUnder n (xs++ys)
,然后选择25以下的初始段。
n
我不想要对中间列表进行求和,因此我会跟踪我允许的数量(takeWhileUnder n [] = []
takeWhileUnder n (x:xs) | x < n = x:takeWhileUnder (n-x) xs
| otherwise = []
)。
x
我允许appendWhileUnder' n xs ys = xs ++ takeWhileUnder (n - sum xs)
通过,如果它没有超出我的余额。
可能不受欢迎的副作用:如果它总和超过25,它会砍掉原始列表的位。解决方法:使用
xs
保留整个n
是否会让您超过{{1}}。