Haskell 在可遍历结构中寻找线性路径

Haskell 在可遍历结构中寻找线性路径,haskell,tree,traversal,Haskell,Tree,Traversal,给出了一个步骤列表: >>> let path = ["item1", "item2", "item3", "item4", "item5"] 和标记的树: >>> import Data.Tree >>> let tree = Node "item1" [Node "itemA" [], Node "item2" [Node "item3" []]] 我想要一个函数,它通过path中的步骤匹配树中的标签,直到它不能再继续,因为没有更多的

给出了一个步骤列表:

>>> let path = ["item1", "item2", "item3", "item4", "item5"]
和标记的

>>> import Data.Tree
>>> let tree = Node "item1" [Node "itemA" [], Node "item2" [Node "item3" []]]
我想要一个函数,它通过
path
中的步骤匹配
树中的标签,直到它不能再继续,因为没有更多的标签匹配这些步骤。具体地说,当进入“item4”时(对于我的用例,我仍然需要指定最后一个匹配的步骤):

如果我允许
[String]->Tree String->[String]
作为
trav
的类型,我可以编写一个递归函数,同时在两个结构中执行步骤,直到没有与步骤匹配的标签为止。但我想知道是否可以使用更通用的类型,特别是
。例如:
Foldable t=>[String]->t String->[String]
。如果这是可能的,如何实现
trav


我怀疑有一种方法可以使用
lens

首先,让我们使用
type Label=String
。字符串不完全是描述性的,最终可能不是理想的

现在。要使用可遍历的
,您需要选择一个合适的
应用程序
,它可以包含决定在其“结构”中执行什么操作所需的信息。您只需要在匹配失败后传回信息。这听起来像是一些

因此,猜测将是
作为预结果的[Label](t Label)
。这意味着,我们使用实例化

    traverse :: Traversable t
      => (Label -> Either [Label] Label) -> t Label -> Either [Label] (t Label)
那么我们可以传递什么作为参数函数呢

travPt0 :: [Label] -> Label -> Either [Label] Label
travPt0 ls@(l0 : _) label
   | l0 /= label   = Left ls
   | otherwise     = Right label     ?
问题是,如果任何节点具有不匹配的标签,
遍历
将立即完全失败<代码>可遍历实际上没有“有选择地”深入到数据结构中的概念,它只是始终遍历所有内容。实际上,我们首先只想在最顶端的节点上进行匹配,只有那个节点是必须首先匹配的

避免直接深度遍历的一种方法是首先将树拆分为一棵子树。好吧,那么。。。我们需要提取最上面的标签。我们需要把树分成子树


我尚未测试此代码

首先,请使用
type Label=String
。字符串不完全是描述性的,最终可能不是理想的

现在。要使用可遍历的,您需要选择一个合适的应用程序,它可以包含决定在其“结构”中执行什么操作所需的信息。您只需要在匹配失败后传回信息。这听起来像是一些

因此,猜测将是
作为预结果的[Label](t Label)
。这意味着,我们使用实例化

    traverse :: Traversable t
      => (Label -> Either [Label] Label) -> t Label -> Either [Label] (t Label)
那么我们可以传递什么作为参数函数呢

travPt0 :: [Label] -> Label -> Either [Label] Label
travPt0 ls@(l0 : _) label
   | l0 /= label   = Left ls
   | otherwise     = Right label     ?
问题是,如果任何节点具有不匹配的标签,
遍历
将立即完全失败<代码>可遍历实际上没有“有选择地”深入到数据结构中的概念,它只是始终遍历所有内容。实际上,我们首先只想在最顶端的节点上进行匹配,只有那个节点是必须首先匹配的

避免直接深度遍历的一种方法是首先将树拆分为一棵子树。好吧,那么。。。我们需要提取最上面的标签。我们需要把树分成子树


我尚未测试此代码

我们将以下递归模型作为参考

import           Data.List   (minimumBy)
import           Data.Ord    (comparing)
import           Data.Tree

-- | Follows a path into a 'Tree' returning steps in the path which
-- are not contained in the 'Tree'
treeTail :: Eq a => [a] -> Tree a -> [a]
treeTail [] _ = []
treeTail (a:as) (Node a' trees)
  | a == a'   = minimumBy (comparing length) 
              $ (a:as) : map (treeTail as) trees
  | otherwise = as
这表明这里的机制不是我们在遍历树(这是一个
可遍历的
实例可能会做的),而是我们根据某个状态在树中遍历并搜索最深的路径

如果愿意,我们可以用
棱镜
来描述这个“步骤”

import Control.Lens

step :: Eq a => a -> Prism' (Tree a) (Forest a)
step a = 
  prism' (Node a)
         (\n -> if rootLabel n == a 
                  then Just (subForest n)
                  else Nothing)
这将允许我们将算法编写为

treeTail :: Eq a => [a] -> Tree a -> [a]
treeTail [] _ = []
treeTail pth@(a:as) t =
  maybe (a:as)
        (minimumBy (comparing length) . (pth:) . map (treeTail as))
        (t ^? step a)

但我不确定这是否更清楚。

我们将参考以下递归模型

import           Data.List   (minimumBy)
import           Data.Ord    (comparing)
import           Data.Tree

-- | Follows a path into a 'Tree' returning steps in the path which
-- are not contained in the 'Tree'
treeTail :: Eq a => [a] -> Tree a -> [a]
treeTail [] _ = []
treeTail (a:as) (Node a' trees)
  | a == a'   = minimumBy (comparing length) 
              $ (a:as) : map (treeTail as) trees
  | otherwise = as
这表明这里的机制不是我们在遍历树(这是一个
可遍历的
实例可能会做的),而是我们根据某个状态在树中遍历并搜索最深的路径

如果愿意,我们可以用
棱镜
来描述这个“步骤”

import Control.Lens

step :: Eq a => a -> Prism' (Tree a) (Forest a)
step a = 
  prism' (Node a)
         (\n -> if rootLabel n == a 
                  then Just (subForest n)
                  else Nothing)
这将允许我们将算法编写为

treeTail :: Eq a => [a] -> Tree a -> [a]
treeTail [] _ = []
treeTail pth@(a:as) t =
  maybe (a:as)
        (minimumBy (comparing length) . (pth:) . map (treeTail as))
        (t ^? step a)

但我不确定这是否更清楚。

这条路径应该如何使用还不清楚。一种方法是树拉链。另外,仅使用Foldable.toList进行比较。普通遍历有一个固定的顺序,因此它们不允许您自己按路径进行索引。@J.Abrahamson,我对它进行了编辑以使其更清晰。我希望它能被理解。这让我想起了一个xml xpath查询。。。。您可能想看看xml导管是如何做类似事情的。请注意,xpath允许在给定级别上进行多个匹配,并使用列表monad来表示结果。@jamshidh,在我的具体案例中,我可以假设在同一级别上不会有重复的标签。这也是我希望使用更多通用树的原因,例如,使用
Set
s而不是分支的列表来强制每个级别的标签唯一性。Data.Tree使用Haskell列表来保存项目(具有潜在的重复),也许您是想定义不同类型的树,也许是使用Data.Map?无论如何,我仍然认为单子是一条出路,因为每次查找都可能失败(即,地图中可能不存在该级别的项目),您需要将这些链接在一起。现在还不清楚该如何使用这条路径。一种方法是树拉链。另外,仅使用Foldable.toList进行比较。普通遍历有一个固定的顺序,因此它们不允许您自己按路径进行索引。@J.Abrahamson,我对它进行了编辑以使其更清晰。我希望它能被理解。这让我想起了一个xml xpath查询。。。。您可能想看看xml导管是如何做类似事情的。不