我有一个像下面那样重复的模式。有办法吗?
摆脱样板?模式是我正在检查是否
[Param]
中至少有一个项与特定构造函数匹配。如果
这样的项目存在,我调用一个函数与该值的数据。如果不,
我通过了。
data Param = UserId Int | PostId Int | ...
...
-- in one function:
let matches = [uid | x@(UserId uid) <- xs]
case matches of
[uid'] -> doSomething uid'
_ -> return () -- do nothing
-- in another function:
let matches = [pid | x@(PostId pid) <- xs]
case matches of
[pid'] -> doSomethingElse pid'
_ -> return () -- do nothing
修改
我不在乎matches
最终是否是一个或多个元素。我只想要第一个元素,如果有的话。
答案 0 :(得分:4)
如果您只关心第一个元素,那么最好的选择是在listToMaybe
中使用Data.Maybe
以及maybe
函数,您可以执行以下操作:
onSingleton :: Monad m => (a -> m ()) -> [a] -> m ()
onSingleton f = maybe (return ()) f . listToMaybe
然后你可以写
onSingleton doSomething [uid | x@(UserId uid) <- xs]
onSingleton doSomethingElse [pid | x@(PostId pid) <- xs]
listToMaybe
功能非常简单。如果传递给它的列表为空,则返回Nothing
,否则返回Just (head list)
。 maybe
函数采用传入Nothing
时的默认值以及应用于传入Just value
内部的函数,因此将它们一起使用意味着您可以快速应用monadic函数到列表中的第一个元素,如果该列表为空则不执行任何操作。
答案 1 :(得分:1)
data Param = UserId Int | PostId Int | ...
....
forM_ (take 1 [x | UserId x <- xs]) doSomething
....
forM_ (take 1 [x | PostId x <- xs]) doSomethingElse
每个forM_
表达式最多只处理一个来自输入的匹配元素。
&#34;行动&#34;假设doSomething
和doSomethingElse
类型为(Monad m) => a -> m b
(或(Monad m) => a -> m ()
,如问题所示)。
每个forM_ ...
表达式的类型为(Monad m) => m ()
。
答案 2 :(得分:0)
在您的特定示例中,您不需要列表推导。您可以在case语句中对所有内容(列表长度和构造函数)进行模式匹配:
case xs of
[UserId uid] -> doSomething uid
_ -> return ()
答案 3 :(得分:0)
您可以使用find :: (a -> Bool) -> [a] -> Maybe a
中的Data.List
找到列表中的第一个元素,然后您可以更改列表理解
maybe (return ()) doSomething $ find (\x -> not $ null [0 | (UserId _) <-[x]]) xs
我们可以简化一下:
doFirst f bl xs = maybe (return ()) f $ find (not . null . bl) xs
并使用我们的功能
doFirst doSometing (\x -> [0 | (UserId _) <-[x]]) xs
doFirst doSometingElse (\x -> [0 | (PostId _) <-[x]]) xs