如何基于单子模式匹配进行过滤?
How to filter based on monadic pattern match?
我有以下问题:
给定一个类型 data T = T (Maybe Int)
,我如何过滤一个列表以获得非 Nothing
值?
输入
a = [T (Just 3), T Nothing, T (Just 4)]
期望输出
b = [T (Just 3), T (Just 4)]
我试过类似的东西:
b = filter (\x-> x@(Just t)) a
...认为我可以根据模式匹配进行过滤,但我收到错误:
Pattern syntax in expression context: x@(Just t)
Did you mean to enable TypeApplications?
稍后能够解压内部值(在 Just
下)并相应地使用它。
我认为在这里我们可以更好地使用列表理解的模式匹配语义:
result = [ e | e@(T (Just _)) <- a]
这里我们枚举a
中的元素e
,如果与T (Just _)
的模式匹配成功,我们在结果列表中yield它。
如果您希望解压封装在T (Just x)
中的值,我们可以执行模式匹配,并生成包装元素:
result = [ e | T (Just <b>e</b>) <- a]
因此,这不仅会 "filter" 值,还会同时解包。所以 T Nothing
被忽略,只有包装的 T (Just e)
被保留,相应的 e
最终出现在列表中。
模式匹配仅适用于函数的参数,不适用于主体。您需要匹配的模式是 T
,使用像 maybe
这样的变形将包装值转换为布尔值。
Prelude> a = [T (Just 3), T Nothing, T (Just 4)]
Prelude> filter (\(T x) -> maybe False (const True) x) a
[T (Just 3),T (Just 4)]
但是请注意,maybe False (const True)
已定义为 Data.Maybe.isJust
。
Prelude> import Data.Maybe
Prelude> filter (\(T x) -> isJust x) a
[T (Just 3),T (Just 4)]
如果你有一些 T -> Maybe Int
类型的函数与 isJust
组合,你可以简化谓词。例如:
Prelude> data T = T { getT :: Maybe Int } deriving Show
Prelude> a = [T (Just 3), T Nothing, T (Just 4)]
Prelude> filter (isJust . getT) a
[T {getT = Just 3},T {getT = Just 4}]
如果您想在 T
列表中获取 Int
值的列表(因此键入 [T] -> [Int]
),则 mapMaybe
来自 Data.Maybe
几乎完全符合您的要求。除此之外,您只需要一个类型为 T -> Maybe Int
的解包函数
import Data.Maybe ( mapMaybe )
data T = T (Maybe Int)
deriving (Eq, Show)
unT :: T -> Maybe Int
unT (T x) = x
filterTs = mapMaybe unT
然后:
λ a = [T (Just 3), T Nothing, T (Just 4)]
a :: [T]
λ filterTs a
[3,4]
it :: [Int]
在我看来,将此过滤器操作设为 [T] -> [Int]
类型比将 return 包含非 Nothing
值的 T
值更有用;原因是,即使您将 a
过滤到 [T (Just 3), T (Just 4)]
,那么稍后处理该问题的代码 仍然 必须在 [=23= 上进行模式匹配] 获取 Int
值,即使您知道永远不会有 Nothing
1,因为 T
仍然被硬编码为包含Nothing
.
作为一般规则,如果您正在过滤(或默认等)以保证不存在大小写,您应该考虑转换为不再有大小写的类型。它通常使生成的数据更易于处理(例如,不需要模式匹配或 fmap
s 进入冗余层),并有助于避免错误。
还有 catMaybes :: [Maybe a] -> [a]
,这样做 "filtering out Nothing
s without mapping",但由于您要映射到解包,T
构造函数 mapMaybe
更合适。
1 而这种 "I know there'll never be Nothing
here, so I don't have to handle it" 情况是一个非常丰富的 bug 来源,当某些东西改变时隐藏的不变性在未来很容易被破坏。因此,实际编写利用 Nothing
"cannot" 存在的知识的代码甚至不是一个好主意;你应该仍然处理这两种情况!
我有以下问题:
给定一个类型 data T = T (Maybe Int)
,我如何过滤一个列表以获得非 Nothing
值?
输入
a = [T (Just 3), T Nothing, T (Just 4)]
期望输出
b = [T (Just 3), T (Just 4)]
我试过类似的东西:
b = filter (\x-> x@(Just t)) a
...认为我可以根据模式匹配进行过滤,但我收到错误:
Pattern syntax in expression context: x@(Just t)
Did you mean to enable TypeApplications?
Just
下)并相应地使用它。
我认为在这里我们可以更好地使用列表理解的模式匹配语义:
result = [ e | e@(T (Just _)) <- a]
这里我们枚举a
中的元素e
,如果与T (Just _)
的模式匹配成功,我们在结果列表中yield它。
如果您希望解压封装在T (Just x)
中的值,我们可以执行模式匹配,并生成包装元素:
result = [ e | T (Just <b>e</b>) <- a]
因此,这不仅会 "filter" 值,还会同时解包。所以 T Nothing
被忽略,只有包装的 T (Just e)
被保留,相应的 e
最终出现在列表中。
模式匹配仅适用于函数的参数,不适用于主体。您需要匹配的模式是 T
,使用像 maybe
这样的变形将包装值转换为布尔值。
Prelude> a = [T (Just 3), T Nothing, T (Just 4)]
Prelude> filter (\(T x) -> maybe False (const True) x) a
[T (Just 3),T (Just 4)]
但是请注意,maybe False (const True)
已定义为 Data.Maybe.isJust
。
Prelude> import Data.Maybe
Prelude> filter (\(T x) -> isJust x) a
[T (Just 3),T (Just 4)]
如果你有一些 T -> Maybe Int
类型的函数与 isJust
组合,你可以简化谓词。例如:
Prelude> data T = T { getT :: Maybe Int } deriving Show
Prelude> a = [T (Just 3), T Nothing, T (Just 4)]
Prelude> filter (isJust . getT) a
[T {getT = Just 3},T {getT = Just 4}]
如果您想在 T
列表中获取 Int
值的列表(因此键入 [T] -> [Int]
),则 mapMaybe
来自 Data.Maybe
几乎完全符合您的要求。除此之外,您只需要一个类型为 T -> Maybe Int
import Data.Maybe ( mapMaybe )
data T = T (Maybe Int)
deriving (Eq, Show)
unT :: T -> Maybe Int
unT (T x) = x
filterTs = mapMaybe unT
然后:
λ a = [T (Just 3), T Nothing, T (Just 4)]
a :: [T]
λ filterTs a
[3,4]
it :: [Int]
在我看来,将此过滤器操作设为 [T] -> [Int]
类型比将 return 包含非 Nothing
值的 T
值更有用;原因是,即使您将 a
过滤到 [T (Just 3), T (Just 4)]
,那么稍后处理该问题的代码 仍然 必须在 [=23= 上进行模式匹配] 获取 Int
值,即使您知道永远不会有 Nothing
1,因为 T
仍然被硬编码为包含Nothing
.
作为一般规则,如果您正在过滤(或默认等)以保证不存在大小写,您应该考虑转换为不再有大小写的类型。它通常使生成的数据更易于处理(例如,不需要模式匹配或 fmap
s 进入冗余层),并有助于避免错误。
还有 catMaybes :: [Maybe a] -> [a]
,这样做 "filtering out Nothing
s without mapping",但由于您要映射到解包,T
构造函数 mapMaybe
更合适。
1 而这种 "I know there'll never be Nothing
here, so I don't have to handle it" 情况是一个非常丰富的 bug 来源,当某些东西改变时隐藏的不变性在未来很容易被破坏。因此,实际编写利用 Nothing
"cannot" 存在的知识的代码甚至不是一个好主意;你应该仍然处理这两种情况!