尝试使用 Maybe 时出现问题
Issues while trying to use Maybe
我创建了以下 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
函数?
我不明白你为什么这么难:只需使用 scanl
确定 "consecutive counts",然后在其上应用 maximum
,如果结果是 0
, return 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
话虽如此,我不明白你为什么要 return Nothing
。在这种情况下,零是一个完全有效的答案。 Maybe
通常用于 return 某种 "exception" 答案。像 find
可能找不到元素。
代码的工作原理如下:
首先我们执行 scanl
。 scanl
通过列表传递某种 累加器 :对于每个元素,它使用累加器和列表中的对象调用函数(此处 f
)。结果是 "new" 累加器。然后将此结果 returned 作为结果列表中的一个元素,并重新用于将累加器传递给下一个元素。所以一般来说它是这样的:
-- 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
在这种情况下,累加器是 Orange
序列的长度 到目前为止 : 所以 [Orange,Apple,Apple,Orange,Orange,Orange,Apple,Orange]
将映射到 [1,0,0,1,2,3,0,1]
此结果将存储在 counts
.
现在我们计算该列表 counts
的 maximum
。所以结果 - maxc
- 是 counts
的最大值,因此是 最长 (不是当前)橙子序列的长度。
接下来在函数定义中我们检查该值是否等于 0
。如果是这样的话,我们returnNothing
。否则我们 return Just maxc
.
在这里您可以找到 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
利用二元组是函子这一事实,<$>
运算符将作为第一个参数传递的函数(即我们的例子中的 (1+)
)应用到第二个2元组的元素。因此,在计数器 (x, y)
中,我们在寻找橙子时增加 y
,而不触及 x
,即我们正在计算当前橙色序列的长度(当我们找到一个时)。
当我们找到苹果时,countOranges (x,y) _ = (max x y, 0)
将更新 (x,y)
中的 x
并将 y
重置为 0
。您可以看到 x
将保存迄今为止找到的最长橙子序列的长度。
如果最长的橙子序列出现在水果列表的末尾,则不会发生上一段描述的x
更新。这就是我们仍然需要 numOranges = uncurry max $ ...
从 foldl'
.
返回的 (x, y)
元组中获取最大值的原因
我创建了以下 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
函数?
我不明白你为什么这么难:只需使用 scanl
确定 "consecutive counts",然后在其上应用 maximum
,如果结果是 0
, return 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
话虽如此,我不明白你为什么要 return Nothing
。在这种情况下,零是一个完全有效的答案。 Maybe
通常用于 return 某种 "exception" 答案。像 find
可能找不到元素。
代码的工作原理如下:
首先我们执行 scanl
。 scanl
通过列表传递某种 累加器 :对于每个元素,它使用累加器和列表中的对象调用函数(此处 f
)。结果是 "new" 累加器。然后将此结果 returned 作为结果列表中的一个元素,并重新用于将累加器传递给下一个元素。所以一般来说它是这样的:
-- 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
在这种情况下,累加器是 Orange
序列的长度 到目前为止 : 所以 [Orange,Apple,Apple,Orange,Orange,Orange,Apple,Orange]
将映射到 [1,0,0,1,2,3,0,1]
此结果将存储在 counts
.
现在我们计算该列表 counts
的 maximum
。所以结果 - maxc
- 是 counts
的最大值,因此是 最长 (不是当前)橙子序列的长度。
接下来在函数定义中我们检查该值是否等于 0
。如果是这样的话,我们returnNothing
。否则我们 return Just maxc
.
在这里您可以找到 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
利用二元组是函子这一事实,<$>
运算符将作为第一个参数传递的函数(即我们的例子中的 (1+)
)应用到第二个2元组的元素。因此,在计数器 (x, y)
中,我们在寻找橙子时增加 y
,而不触及 x
,即我们正在计算当前橙色序列的长度(当我们找到一个时)。
当我们找到苹果时,countOranges (x,y) _ = (max x y, 0)
将更新 (x,y)
中的 x
并将 y
重置为 0
。您可以看到 x
将保存迄今为止找到的最长橙子序列的长度。
如果最长的橙子序列出现在水果列表的末尾,则不会发生上一段描述的x
更新。这就是我们仍然需要 numOranges = uncurry max $ ...
从 foldl'
.
(x, y)
元组中获取最大值的原因