1

我正在尝试使用 Haskell 的 hxt 包解析 COLLADA 文件。

我一直做得很好,但我遇到了一个奇怪的错误(或者更有可能是我的错误)。

我有一个看起来像这样的箭头:

processGeometry = proc x -> do
    geometry <- atTag "geometry" -< x
    meshID <- getAttrValue "id" -< geometry
    meshName <- getAttrValue "name" -< geometry
    mesh <- atTag "mesh" -< geometry
    sources <- hasName "source" <<< getChildren -< mesh
    positionSource <- hasAttrValue "id" ("-positions" `isSuffixOf`) -< sources
    positionArray  <- processFloatSource -< positionSource
    returnA -< positionArray

添加行

normalSource <- hasAttrValue "id" ("-normals" `isSuffixOf`) -< sources

然而,靠近底部会使整个箭头失效。

无论我返回什么,都会发生这种情况,即使我返回的是原件x

这是我的atTag功能:

atTag tag = deep (isElem >>> hasName tag)

这是我试图解析的示例 COLLADA 文件: https ://pastebin.com/mDSTH2TW

为什么添加一条线会完全改变箭头的结果,而它根本不应该做任何事情?

4

1 回答 1

2

TL;DR:如果您正在寻找两个单独的子元素,请使用单独的调用getChildren.

您的变量sources不代表所有源元素的列表。相反,它是单一来源。如果您检查 的类型sources,您会看到它是XMLTree。因此,当您使用hasAttrValue它两次时,您正在寻找与这两种情况都匹配的单个源元素。

至于为什么返回什么并不重要:即使不使用其值,也会执行每一行。事实上,除非您使用输出,否则您甚至不必为它指定名称: only hasAttrValue "id" (isSuffixOf "-normals") <- sources(removing normalSource <-) 行的工作方式相同。所以如果你 return x,它仍然x只有在它可以找到那个不可能的源元素时才返回。

您可以通过单独两次调用来让您的代码找到两个单独的源元素getChildren- 每个您要查找的单独元素一个 - 并分别检查每个单独的“id”属性。


如果上述内容不清楚,这是一个独立的示例。

data Tree a = Tree a [Tree a]

exampleTree :: Tree String
exampleTree = Tree "root" [Tree "childA" [], Tree "childB" []]

newtype ListArrow a b = ListArrow { runListArrow :: a -> [b] }

instance Category ListArrow where
    id = ListArrow (\x -> [x])
    (ListArrow g) . (ListArrow f) = ListArrow (\x -> concatMap g (f x))

instance Arrow ListArrow where
    arr f = ListArrow (\x -> [f x])
    first (ListArrow f) = ListArrow (\(a, b) -> [ (a', b) | a' <- f a ])

getChildren :: ListArrow (Tree a) (Tree a)
getChildren = ListArrow gc where
    gc (Tree _ children) = children

hasContent :: Eq a => a -> ListArrow (Tree a) (Tree a)
hasContent content = ListArrow hc where
    hc cur@(Tree c _) = if content == c then [cur] else []

getContent :: ListArrow (Tree a) a
getContent = ListArrow gc where
    gc (Tree c _) = [c]

-- this has the same problem as the code in the question
findBothChildrenBad :: ListArrow (Tree String) (String, String)
findBothChildrenBad = proc root -> do
    -- child is a (single) child of the root
    child <- getChildren -< root

    -- childA == child, and filter to only cases where its content is "childA"
    childA <- hasContent "childA" -< child

    -- childB == child, and filter to only cases where its content is "childB"
    childB <- hasContent "childB" -< child
    -- now the content has to be both "childA" and "childB" -- so we're stuck

    childAContent <- getContent -< childA
    childBContent <- getContent -< childB
    returnA -< (childAContent, childBContent)

-- this is the fixed version
findBothChildren :: ListArrow (Tree String) (String, String)
findBothChildren = proc root -> do
    -- childA is a (single) child of the root
    childA <- getChildren -< root

    -- filter to only cases where its content is "childA"
    hasContent "childA" -< childA

    -- childB is a (potentially different) child of the root
    childB <- getChildren -< root

    -- filter to only cases where its content is "childB"
    hasContent "childB" -< childB
    -- we're not stuck here

    childAContent <- getContent -< childA
    childBContent <- getContent -< childB
    returnA -< (childAContent, childBContent)
于 2017-05-01T20:43:53.873 回答