簡體   English   中英

Haskell 在提升結果的同時遍歷和過濾列表

[英]Haskell traverse and filter through a list while lifting results

假設我有要執行以下操作的代碼:

  1. 輸入:字符串列表[String]
  2. 操作( checkSatcheckResult
    • 從輸入字符串中獲取布爾值。
  3. 輸出:
    • 如果輸入中的所有內容都可解析為布爾值,則僅返回導致“未飽和”的內容。
    • 如果其中至少一個有錯誤,則返回錯誤。
data Err = Err String deriving (Show)

-- Detail omitted for this example
checkSat :: String -> Either Err String
checkSat = Right . id

checkResult :: String -> Either Err Bool
checkResult "sat"    = Right True
checkResult "unsat"  = Right False
checkResult err      = Left $ Err err

onlyUnsat :: [String] -> Either Err [String]
onlyUnsat xs = filter (traverse notSat xs) xs
  where notSat x  = fmap not $ checkSat x >>= checkResult
        onlyUnsat = filter (traverse notSat xs) xs

上面的代碼不太好用。 用正確的打字方式寫onlyUnsat的正確方法是什么?

嘗試這個:

onlyUnsat :: [String] -> Either Err [String]
onlyUnsat = traverse checkSat >=> filterM (fmap not . checkResult)

  where

  filterM :: Applicative m => (a -> m Bool) -> [a] -> m [a]
  filterM p = fmap (map fst . filter snd) . traverse (\x -> withFst x <$> p x)
    where withFst x = \y -> (x, y)

  (>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c
  f >=> g = \x -> f x >>= g

請注意, filterM>=>都可以從Control.Monad導入。 為了清楚起見,我在這里重新定義了它們:-)

基本思想是將操作分為兩個階段:(1)對列表中的每個項目應用checkSat (2) 過濾列表中的每個項目。 第一階段由traverse checkSat給出,其類型為[String] -> Either Err [String] 第二階段由filterM (fmap not . checkResult)給出,它也有類型[String] -> Either Err String 然后我們使用>=>將這兩個計算鏈接在一起,形成一個相同的類型。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM