如何根据单子模式匹配进行过滤?

时间:2018-09-13 14:01:18

标签: haskell pattern-matching filtering maybe

我有以下问题:

给定类型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?

I want to之后可以解包内部值(在Just下)并相应地使用它。

3 个答案:

答案 0 :(得分:4)

我认为在这里我们可以更好地使用列表理解的模式匹配语义:

result = [ e | e@(T (Just _)) <- a]

因此,我们在此处枚举e中的元素a,如果与T (Just _)匹配的模式成功,我们将在结果列表中产生它。

但是,如果您希望解包封装在T (Just x)中的值,则我们可以执行模式匹配,并产生包装的元素:

result = [ e | T (Just e) <- a]

因此,这不仅可以“过滤”值,还可以同时解压缩。因此,T Nothing被忽略,只有保留的T (Just e)被保留,并且相应的e最终出现在列表中。

答案 1 :(得分:3)

如果您想在Int列表中键入T(因此键入[T] -> [Int])中的Data.Maybe值列表,则从T -> Maybe Int中选择mapMaybe已经几乎可以满足您的要求。最重要的是,类型为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]

我认为让此过滤操作为T类型比使它返回包含非{Nothing值的a值更有用;原因是,即使您将[T (Just 3), T (Just 4)]过滤到Just,处理以后的 still 的代码也必须在Int上进行模式匹配以获取即使您知道永远不会有Nothing 1 ,因为T仍被硬编码为包含Nothing

作为一般规则,如果要过滤(或默认设置等)以保证不存在大小写,则应考虑转换为不再包含大小写的类型。它通常使结果数据更易于处理(例如,无需模式匹配或fmap即可进入冗余层),并有助于避免错误。

还有catMaybes :: [Maybe a] -> [a],它可以执行“无需映射就过滤掉Nothing s”,但是由于要映射为展开,T构造函数mapMaybe更加接近适合。


1 这种“我知道这里永远不会Nothing,所以我不必处理”这种情况是非常丰富的错误源,容易被破坏当某些事情发生变化时,将来隐藏着不变性。因此,实际上利用Nothing“不能”存在的知识来编写代码甚至不是一个好主意。您应该仍然处理这两种情况!

答案 2 :(得分:2)

模式匹配仅在函数的自变量中起作用,而不是函数体。您需要匹配的模式是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}]