更好的解决方法[Int] - > Int - >诠释

时间:2013-09-26 23:23:33

标签: list haskell

以下是我正在处理的示例问题:

示例输入:test [4, 1, 5, 6] 6返回5

我正在使用此功能解决此问题:

test :: [Int] -> Int -> Int
test [] _ = 0
test (x:xs) time = if (time - x) < 0
                   then x
                   else test xs $ time - x

任何更好的方法来解决这个功能(可能使用任何内置的高阶函数)?

5 个答案:

答案 0 :(得分:2)

怎么样

test xs time = maybe 0 id . fmap snd . find ((>time) . fst) $ zip sums xs
   where sums =  scanl1 (+) xs

或与含糖列表理解等效

 test xs time = headDef 0 $ [v | (s, v) <- zip sums xs, s > time]
      where sums = scanl1 (+) xs

headDefsafe提供。实现(f _ (x:_) = x; f x _ = x)是微不足道的,但是安全包有很多有用的功能,所以最好检查一下。

将列表汇总到每个点并找到第一个出现的值大于timescanl是一个有用的函数,其行为类似于foldl,但保留了中间结果,zip将两个列表压缩为元组列表。然后,我们只需使用fmapmaybe来操纵Maybe (Integer, Integer)即可获得结果。

默认为0,但我喜欢从用户角度更简单地转到Maybe Integer的版本,只需删除maybe 0 id

答案 1 :(得分:1)

您可能希望scanl及其近亲,scanl1。例如:

test_ xs time = [curr | (curr, tot) <- zip xs (scanl1 (+) xs), tot > time]

这会查找运行总和大于time的所有位置。然后你可以选择第一个(或0),如下所示:

safeHead def xs = head (xs ++ [def])
test xs time = safeHead 0 (test_ xs time)

答案 2 :(得分:1)

这很冗长,我不一定建议写这样一个简单的函数(IMO模式匹配和递归很清楚)。但是,这是一个非常有说服力的管道:

import Control.Error
import Data.List

deadline :: (Num a, Ord a) => a -> [a] -> a
deadline time = fromMaybe 0 . findDeadline time

findDeadline :: (Num a, Ord a) => a -> [a] -> Maybe a
findDeadline time xs = decayWithDifferences time xs
                   >>= findIndex (< 0)
                   >>= atMay xs

decayWithDifferences :: Num b => b -> [b] -> Maybe [b]
decayWithDifferences time = tailMay . scanl (-) time

-- > deadline 6 [4, 1, 5, 6]
-- 5

这会对代码进行一些记录,原则上可以让你进行更好的测试,尽管IMO这些功能或多或少适合“明显正确”的类别。

您可以验证它是否与您的实施相匹配:

import Test.QuickCheck

prop_equality :: [Int] -> Int -> Bool
prop_equality time xs = test xs time == deadline time xs

-- > quickCheck prop_equality
-- +++ OK, passed 100 tests.

答案 3 :(得分:1)

在这种特殊情况下,其他人提出的压缩不太必要:

test xs time = head $ [y-x | (x:y:_) <- tails $ scanl1 (+) $ 0:xs, y > time]++[0]

此处scanl1将生成列表xs的滚动总和列表,从0开始。因此,tails将生成一个列表,其中至少有一个列表包含两个元素非空xs。模式匹配(x:y:_)从滚动总和的每个尾部提取两个元素,因此实际上它枚举了滚动总和列表中的相邻元素对。对条件进行过滤,我们重建列表的一部分,该部分以产生大于time的滚动总和的第一个元素开始。然后按照之前的建议使用headDef 0,或附加[0],以便head始终返回一些内容。

答案 4 :(得分:1)

如果您想保持可读性,我会坚持使用您当前的解决方案。这很容易理解,也没有做错任何事。

仅仅因为你可以使它成为一行扫描地图折叠突变体并不意味着你应该