【问题标题】:Efficiently search for a single element in a large Pandoc在大型 Pandoc 中有效地搜索单个元素
【发布时间】:2020-08-29 22:12:45
【问题描述】:

除非我遗漏了什么,否则似乎只有两种方法可以“遍历”Pandoc 数据结构:

  1. BlockInline 构造函数上手动进行模式匹配
  2. 通过Walkable type-class 和相关实用函数

使用Walkable 类型类,有没有一种有效的方法来搜索 first 匹配元素(最好以广度优先的方式),并在找到后立即停止遍历?在我看来,Walkable 类型类周围的所有函数无论如何都会遍历 整个 数据结构。

如果不是,我想唯一的方法是模式匹配 BlockInline 构造函数并自己构建它。

【问题讨论】:

  • Walkable 给你query,它与Monoid 聚合它的结果,你可以使用Option (First a) 上的 Monoid 来获得你想要的行为吗?

标签: haskell pandoc


【解决方案1】:

另一个答案指出了有用的query 功能。我要补充一点,有一个package of pandoc lenses。你也问过广度优先遍历,所以这里都有。

import Data.Semigroup (First (..))

dfsFirstLink :: Walkable Inline b => b -> Maybe Text
dfsFirstLink = fmap getFirst . query (preview $ _Link . _2 . _1 . to First) 

bfsFirstLink :: Walkable Inline b => b -> Maybe Text
bfsFirstLink = fmap getFirst . getConst . traverseOf (levels query . folded) (Const . preview (_Link . _2 . _1 . to First))

-- Construct a walkable value where dfs != bfs
p :: Pandoc
p = Pandoc mempty [Plain [Note [Plain [Link mempty [] ("a","b")]]],Plain [Link mempty [] ("c","d")]]

>> dfsFirstLink p
Just "a"
>> bfsFirstLink p
Just "c"

尽管不幸的是,一些临时实验表明它可能不像人们希望的那样懒惰。

【讨论】:

    【解决方案2】:

    Walkable 类型类包含一个名为 query 的函数,其类型签名如下:

    query :: Monoid c => (a -> c) -> b -> c
    

    Data.Semigroup 中,有一个名为First 的类型,带有一个半群实例,其中的累加行为是返回“最左边的值”。

    这可以与Maybe 上的Monoid 结合使用,将任何Semigroup 转换为MonoidmemptyNothing,以提供您想要的行为。

    例如,将函数从 Inline -> Maybe String 调整为 Pandoc -> Maybe String,可以这样完成:

    import Text.Pandoc
    import Text.Pandoc.Walk (query)
    import Data.Semigroup
    
    findUrl :: Inline -> Maybe String
    findUrl (Link _ _ target) = Just $ fst target
    findUrl _ = Nothing
    
    findFirstUrl :: Pandoc -> Maybe String
    findFirstUrl = (fmap getFirst) . (query findUrl')
      where
        findUrl' :: Inline -> Maybe (First String)
        findUrl' = (fmap First) . findUrl
    
    

    关于您担心这将遍历整个数据结构:Haskell 是惰性的;它不应该比它需要的更远。

    正如 cmets 中所指出的,也可以通过将 query 特化为 List Monoid 来编写:

    import Text.Pandoc
    import Text.Pandoc.Walk (query)
    import Data.Maybe (listToMaybe)
    
    findUrl :: Inline -> [String]
    findUrl (Link _ _ target) = [fst target]
    findUrl _ = []
    
    findFirstUrl :: Pandoc -> Maybe String
    findFirstUrl = listToMaybe . (query findUrl)
    

    【讨论】:

    • 您可能需要将我的示例中的一些 Strings 更改为 Text 以使其编译,我在旧版本的 Pandoc 上对此进行了测试,因为那是我安装的那个
    • First 的版本在Data.SemigroupData.Monoid 之间也有一堆“历史差异”,我相信Data.Semigroup 中的版本(如此处所述)是推荐一个
    • 两个问题:(a) Maybe a 仅当 aSemigroup 时才是 Monoid。如果我想找到第一个匹配条件的Pandoc.Block 怎么办——既不是Semigroup 也不是Monoid? (b) First 有什么特别之处可以使 getFirst 高效,还是取决于 Haskell 的懒惰? (文档太混乱了 - Monoid.FirstSemigroup.FirstOption 似乎在不断变化)如果这主要取决于惰性评估,那么 query 可以专门用于列表 monoid,然后我们做 @987654356 @关于结果?它会同样有效吗?
    • a) 首先 a 是一个半群,其中First a <> First b = First a
    • b) First 没有什么特别之处,除了惰性求值之外,它还可以使其高效,我希望您建议使用 list monoid 来工作,并且同样高效
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-11-13
    • 2017-02-27
    • 1970-01-01
    • 2012-03-04
    • 1970-01-01
    • 2011-04-02
    • 1970-01-01
    相关资源
    最近更新 更多