3

我可以使用 SYB 的 gfoldl 一次性对 listify 的结果进行映射吗?

例如,考虑以下代码:

extractNums :: Expr -> [Int]
extractNums e = map numVal $ listify isNum e

  where isNum :: Expr -> Bool
        isNum (Num _) = True
        isNum _       = False

        numVal :: Expr -> Int
        numVal (Num i) = i
        numVal _       = error "Somehow filter did not work?"

我不喜欢在 numVal 函数中这样,我必须考虑 Expr 类型的不同数据构造函数,而我只对 Num 构造函数感兴趣。我宁愿用下面的 vals 函数替换 isNum 和 numVals:

    vals :: [Int] -> Expr -> [Int]
    vals xs (Num x) = x : xs
    vals xs _       = xs

这可以用gfoldl完成吗?如何?

4

2 回答 2

2

也许这不是最优雅的方法,但这是我的尝试:

extractNums :: Expr -> [Int]
extractNums e = everything (++) (mkQ [] q) e
   where q (Num n) = [n]
         q _ = []

不过,我预计它的表现会低于标准。Maye使用flip (++)会更好吗?我现在看不到。

另一方面,我刚刚意识到listify以类似的方式定义。所以至少不会比你现在的情况更糟。


或者,正如下面@Alex 所建议的那样:

import qualified Data.DList as D

extractNums :: Expr -> [Int]
extractNums e = D.toList $ everything (D.append) (mkQ D.empty q) e
   where q (Num n) = D.singleton n
         q _ = D.empty
于 2015-03-27T10:37:18.123 回答
2

函数listify定义为

-- | Get a list of all entities that meet a predicate
listify :: Typeable r => (r -> Bool) -> GenericQ [r]
listify p = everything (++) ([] `mkQ` (\x -> if p x then [x] else []))

这类似于filter。我们可以创建一种替代方案,类似于mapMaybewhich combine mapand filteryou need into one:

import Data.Generics
import Data.Generics.Schemes
import Data.Maybe (maybeToList)
import Data.Typeable

listify' :: (Typeable t) => (t -> Maybe r) -> GenericQ [r]
listify' f = everything (++) ([] `mkQ` (maybeToList . f))

那么你的例子可以表示为

numVal :: Expr -> Maybe Int
numVal (Num i) = Just i
numVal _       = Nothing

test :: Expr -> [Int]
test = listify' numVal
于 2015-03-27T13:00:17.580 回答