我制作了以下Haskell函数,用于检查列表中Orange
的最大连续出现次数:
import Data.Maybe
data Fruit = Apple | Orange
findMaxSubStr :: [Fruit] -> Maybe Int
findMaxSubStr xs = val xs 0 0
where val [] prev current = (max prev current)
val (Apple:xs) prev current = val xs (max prev current) 0
val (Orange:xs) prev current = val xs prev (current + 1)
我现在正在尝试集成Maybe类型,以防出现次数为0.例如,我希望[]
和[Apple, Apple]
生成Nothing
而不是0
。我该怎么办?
此外,不是使用预建的max
函数,如果我想创建自己的函数以检查最大值,最好的方法是什么?
答案 0 :(得分:4)
我不明白为什么你这么做:只需使用scanl
来确定“连续计数”,然后在其上应用maximum
,以防结果为{{1} },返回0
,否则Nothing
:
Just ...
这就是说我不明白你想要归还findMaxSubStr fruits | maxc == 0 = Nothing
| otherwise = Just maxc
where maxc = maximum counts
counts = scanl f 0 fruits
f x Orange = x+1
f _ _ = 0
的原因。在这种情况下,零是一个完全有效的答案。 Nothing
通常用于返回某种“异常”答案。与可能无法找到元素的Maybe
类似。
代码的工作原理如下:
首先我们执行find
。 scanl
通过列表传递某种 accumulator :对于每个元素,它使用累加器和列表中的对象调用函数(此处为scanl
)。结果是“新”累加器。然后,此结果作为结果列表中的元素返回,并重新用于将累加器传递给下一个元素。所以一般来说它就像:
f
在这种情况下,累加器是到目前为止-- example of scanl for three elements (this is not its real implementation)
scanl f acc0 [xa,xb,xc] = [acc1,acc2,acc3]
where acc1 = f acc0 xa
acc2 = f acc1 xb
acc3 = f acc2 xc
s 序列的长度 :所以Orange
将映射到{{1此结果将存储在[Orange,Apple,Apple,Orange,Orange,Orange,Apple,Orange]
。
现在我们计算该列表[1,0,0,1,2,3,0,1]
的{{1}}。因此,结果 - counts
- 是maximum
的最大值,因此是最长(不是当前)橙子序列的长度。
接下来在函数定义中,我们检查该值是否等于counts
。如果是这种情况,我们会返回maxc
。否则我们会返回counts
。
答案 1 :(得分:1)
在这里,您可以找到findMaxSubStr
函数的定义,该函数使用折叠直接计算列表中最长橙子序列的长度,而不创建中间列表:
import Data.List (foldl')
findMaxSubStr :: [Fruit] -> Maybe Int
findMaxSubStr fruits =
case numOranges of
0 -> Nothing
n -> Just n
where
numOranges = uncurry max $ foldl' countOranges (0, 0) fruits
countOranges t Orange = (1+) <$> t
countOranges (x,y) _ = (max x y, 0)
在this page中,您可以找到有关何时使用foldl'
与使用foldr
或foldl
的讨论,以及显示这些折叠功能如何工作的示例。
元组用于跟踪橙色序列的长度。第一个值保持找到的最长序列的长度,第二个元素保存当前序列的长度。
where
块的原始实现是:
where
numOranges = fst $ foldl' countOranges (0, 0) fruits
countOranges (x, y) Orange = let y' = y+1 in (max x y', y')
countOranges (x, _) _ = (x, 0)
我在@Ryan建议之后编辑了答案(请参阅下面的评论),以下是一些描述解决方案如何运作的说明:
countOranges t Orange = (1+) <$> t
利用了2元组是仿函数的事实,<$>
运算符将作为第一个参数传递的函数(在我们的例子中是(1+)
)应用到第二个元素2元组。因此,在计数器(x, y)
中,我们在查找橙子的同时增加y
,而不会触及x
,即我们正在计算当前橙色序列的长度(当我们找到一个时)。
当我们找到苹果时,countOranges (x,y) _ = (max x y, 0)
会更新x
中的(x,y)
并将y
重置为0
。您可以看到x
将保留到目前为止发现的最长橙子序列的长度。
如果最长的橙子序列出现在水果列表的末尾,则不会发生前一段中描述的x
更新。这就是我们仍然需要numOranges = uncurry max $ ...
从(x, y)
返回的foldl'
元组中获取最大值的原因。