如何在列表理解中减少模式匹配的样板以存在单个项目

时间:2014-08-25 22:22:41

标签: haskell list-comprehension

我有一个像下面那样重复的模式。有办法吗? 摆脱样板?模式是我正在检查是否 [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最终是否是一个或多个元素。我只想要第一个元素,如果有的话。

4 个答案:

答案 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)

您所需要的只是take 1forM_

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;假设doSomethingdoSomethingElse类型为(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