我是函数式编程的新手,还有语言haskell。我试图根据谓词函数确定列表中最长的连续元素系列的长度。该功能如下所示:
longestSequence :: (a -> Bool) -> [Int] -> Int
当我这样称呼时:
longestSequence (\x -> x >= 10) [1,44,33,22,2,3,55,66,66,77,88,99]
它应该给我解决方案6
。
我的解决方案是:
longestSequence :: (a -> Bool) -> [a] -> Int
longestSequence p [] = 0
longestSequence p (x:xs)
| (p x) = 1 + (longestSequence p xs)
| otherwise = longestSequence p xs
我如何解决这个问题的任何提示或想法?
答案 0 :(得分:5)
尝试分解较小的子问题。对于这个例子,一般策略可能是这样的:
[Bool]
列表,其中条目为True
。那就是写一个函数(Int -> Bool) -> [Int] -> [Bool]
True
和False
值。那就是写一个函数[Bool] -> [[Bool]]
。您可能需要查看Data.List.group
。False
群组:[[Bool]] -> [[Bool]]
[[Bool]] -> [Int]
[Int] -> Int
然后你只需要编写这些功能就完成了。对于1.和4.您将需要使用函数map :: (a -> b) -> [a] -> [b]
。如果您已经知道map
,请使用它。如果你不这样做,我建议你自己写一下。
我提供的函数的某些类型签名过于具体。也许试着在可能的地方稍微概括一下。
答案 1 :(得分:3)
在警卫中,p x
和otherwise
部分都存在问题。
(p x) = 1 + (longestSequence p xs)
并非总是如此,如果x的下一个元素不能保持预测p
,则无法添加1 {/ p>
如果当前最长的序列比剩余的序列长
otherwise = longestSequence p xs
不成立
可能的解决方法是保持最大长度和当前长度
longestSequence :: (a -> Bool) -> [a] -> Int
longestSequence p x = ls x 0 0
where
ls [] mx cur = max mx cur
ls (x:xs) mx cur
| p x = ls xs mx (cur+1)
| otherwise = ls xs (max mx cur) 0
答案 2 :(得分:1)
您经常可以使用比递归更简单的fold
,在这种情况下,我们希望使用scanl,就像foldl
一样,除了它保留整个列表。
以下内容遍历列表,递增与谓词匹配的每个项目的计数。如果找到一个不匹配的,它会将计数重置为零。最后,它找到结果列表的最大值。
longestSequence p = maximum . scanl (\count x -> if (p x) then count + 1 else 0) 0
对于[1,44,33,22,2,3,55,66,66,77,88,99]
的示例输入和(\x -> x >= 10)
的谓词,scanl
将生成[0,0,1,2,3,0,0,1,2,3,4,5,6]
的输出。取这个列表的最大值给出6
,你可以看到最大值出现在输入的末尾,而且列表中的谓词也不匹配。
顺便说一句,我全心全意地同意将问题分解成小部分的jpath。我已经看到人们通过使用元组填充fold
参数内的max
计算来解决\count
的类似问题,但这使得阅读更加困难。顺便说一句,这也是像你这样的单功能递归解决方案很难解决的问题。一次跟踪太多,所以很难看到在重置为零的位置和方式。