SYB:可以用 gfoldl 重写 listify 结果上的映射吗?
SYB: can a map over the result of listify be rewritten with a gfoldl?
我可以用SYB的gfoldl把listify的结果一次性做map吗?
例如考虑以下代码:
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 完成吗?怎么样?
也许这不是最优雅的方法,但这是我的尝试:
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
函数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
。我们可以创建一个类似于 mapMaybe
的替代方案,它将您需要的 map
和 filter
组合成一个:
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
我可以用SYB的gfoldl把listify的结果一次性做map吗?
例如考虑以下代码:
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 完成吗?怎么样?
也许这不是最优雅的方法,但这是我的尝试:
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
函数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
。我们可以创建一个类似于 mapMaybe
的替代方案,它将您需要的 map
和 filter
组合成一个:
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