Haskell SYB:listify结果的映射可以用gfoldl重写吗?
我可以使用SYB的gfoldl一次完成listify结果的映射吗 例如,考虑以下代码:Haskell SYB:listify结果的映射可以用gfoldl重写吗?,haskell,generic-programming,fold,scrap-your-boilerplate,Haskell,Generic Programming,Fold,Scrap Your Boilerplate,我可以使用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
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构造函数感兴趣。我宁愿将isNum和numVals替换为下面的VAL函数:
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 _ = []
不过,我预计它的表现将低于标准。也许使用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 []))
这类似于过滤器
。我们可以创建一个类似于mappaye
的替代方案,它将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
您可以使用
Data.Sequence
中的DList
或Seq
之类的内容。它们有O(1)连接,只有一个O(n)转换到列表。@Alex I添加了一个DList
版本。另外,根据文档,似乎Seq
的append是对数的。我一定把append的运行时和snoc的运行时搞混了