【问题标题】:Elm - tree - add branch to another tree - recursive forloop榆树 - 树 - 将分支添加到另一棵树 - 递归 for 循环
【发布时间】:2020-03-03 11:45:55
【问题描述】:

我想在 Elm 中将树枝从一棵树移到另一棵树。

例如:

树 1:

A-1
- A-1-1
- - A-1-1-1
- - A-1-1-2
- - - A-1-1-2-1
- - - A-1-1-2-2

树 2

B-1
- B-1-1
- - B-1-1-1
- - B-1-1-2
- - - B-1-1-2-1
- - - B-1-1-2-2

我想将 A-1-1 移到 B-1-1-2-1 下,这应该会给

B-1
- B-1-1
- - B-1-1-1
- - B-1-1-2
- - - B-1-1-2-1
- - - - A-1-1
- - - - - A-1-1-1
- - - - - A-1-1-2
- - - - - - A-1-1-2-1
- - - - - - A-1-1-2-2
- - - B-1-1-2-2

我是函数式编程的新手。我可以想象如何在 Python 中使用递归 forloop 来做到这一点,但我被 Elm 卡住了。

我可以轻松移动一个节点,但我不知道如何递归添加子节点:

module Main exposing (..)

import Canopy exposing (Node, append, children, leaf, mapChildren, node, value)
import Html exposing (Html, b, div, h1, h2, li, text, ul)
import List exposing (map)


tree1 : Node String
tree1 =
    node "A-1"
        [ node "A-1-1"
            [ leaf "A-1-1-1"
            , node "A-1-1-2"
                [ leaf "A-1-1-2-1"
                , leaf "A-1-1-2-2"
                ]
            ]
        ]


tree2 : Node String
tree2 =
    node "B-1"
        [ node "B-1-1"
            [ leaf "B-1-1-1"
            , node "B-1-1-2"
                [ leaf "B-1-1-2-1"
                , leaf "B-1-1-2-2"
                ]
            ]
        ]


tree3 : Node String
tree3 =
    let
        nodeToMove =
            "A-1-1"

        newParentNode =
            "B-1-1-2-1"

        -- append the node only but not its descendants
        treeWithNewNode =
            append newParentNode nodeToMove tree2

        -- type mismatch
        --        treeWithNewNodeAndNewNodeChildren =
        --            nodeToMove |> mapChildren (\child -> append 

        -- does not do what I was hopping for
        -- newTree =
        --    mapChildrenAt
        --        nodeToMove
        --        (\child -> append newParentNode (value child) treeWithNewNode)
        --        tree2

newParentNode child tree2)
    in
    treeWithNewNode


main =
    div []
        [ h1 [] [ text "Adding a branch to another tree" ]
        , h2 [] [ text "Tree 1" ]
        , viewNode tree1
        , h2 [] [ text "Tree 2" ]
        , viewNode tree2
        , h2 [] [ text "Move A-1-1 under B-1-1-2-1" ]
        , viewNode tree3
        ]


viewNode : Node String -> Html msg
viewNode node =
    let
        subNodes =
            children node
    in
    li []
        [ b [] [ text (value node) ]
        , ul [] (List.map viewNode subNodes)
        ]

我的试用期在这里: https://ellie-app.com/7842F8jCLpCa1

我在这里使用 Canopy,但如果有人推荐,我可以使用其他库。

【问题讨论】:

  • 请把完整的代码贴在这里,包括数据类型的定义和你所有的函数
  • @Bergi 我已经完成了代码。
  • “没有做我想做的事”不是对问题的非常有用的描述。代码有什么作用?等效的 python 代码会是什么样子,Canopy 中具体缺少什么才能做到这一点?给我们一点帮助以了解问题,那么我们更有可能为您提供帮助。
  • @glennsl 这就是它的作用:ellie-app.com/78tWMPxVPrca1

标签: for-loop recursion functional-programming tree elm


【解决方案1】:

我的解决方案没有replaceChildrenAt 来保留现有的孩子。

module Main exposing (main)

import Canopy exposing (Node, append, children, get, leaf, node, value)
import Html exposing (Html, b, div, h1, h2, li, text, ul)



-- add a node (and its children) under a branch in another tree


tree1 : Node String
tree1 =
    node "A-1"
        [ node "A-1-1"
            [ leaf "A-1-1-1"
            , node "A-1-1-2"
                [ leaf "A-1-1-2-1"
                , leaf "A-1-1-2-2"
                ]
            ]
        ]


tree2 : Node String
tree2 =
    node "B-1"
        [ node "B-1-1"
            [ leaf "B-1-1-1"
            , node "B-1-1-2"
                [ node "B-1-1-2-1"
                    [ leaf "don't remove me"
                    ]
                , leaf "B-1-1-2-2"
                ]
            ]
        ]


tree3 : Node String
tree3 =
    let
        nodeToMove =
            Maybe.withDefault (leaf <| "Failed to find node " ++ "A-1-1") <| get "A-1-1" tree1

        newParentNodeValue =
            "B-1-1-2-1"

        treeWithNewNode =
            tree2 |> addNodeAt nodeToMove newParentNodeValue
    in
    treeWithNewNode



-- treeWithNewNode


main =
    div []
        [ h1 [] [ text "Adding a branch to another tree" ]
        , h2 [] [ text "Tree 1" ]
        , viewNode tree1
        , h2 [] [ text "Tree 2" ]
        , viewNode tree2
        , h2 [] [ text "Move A-1-1 under B-1-1-2-1" ]
        , viewNode tree3
        ]


viewNode : Node String -> Html msg
viewNode node =
    let
        subNodes =
            children node
    in
    li []
        [ b [] [ text (value node) ]
        , ul [] (List.map viewNode subNodes)
        ]


addNodeAt : Node String -> String -> Node String -> Node String
addNodeAt node firstParentNodeValue toTree =
    --Canopy.toList ->
    -- [("A-1-1",Nothing),("A-1-1-1",Just "A-1-1"),("A-1-1-2",Just "A-1-1"),...]
    node
        |> Canopy.toList
        |> List.foldl
            -- acc is the updated toTree
            (\( nodeValue, parentValue ) acc ->
                append
                    (Maybe.withDefault firstParentNodeValue parentValue)
                    nodeValue
                    acc
            )
            -- initial value
            toTree



在这里可见:https://ellie-app.com/79sd7H8fCjNa1


@o-o-balance 的回答和这个:https://elmprogramming.com/list.html#folding-a-list 帮助了我很多。

【讨论】:

    【解决方案2】:

    在你的代码中,在我看来你从未真正从tree1 中提取A-1-1 的孩子,所以让我们开始吧:

    subtreeToMove =
        Maybe.withDefault (leaf <| "Failed to find node " ++ nodeToMove) <| get nodeToMove tree1
    

    get 函数按值在树中查找节点。由于可能没有指定值的节点,所以返回一个Maybe,所以我们传入一个默认值。

    接下来,您在tree2 中找到目标节点,并附加该节点及其子节点。我将在这里使用replaceChildrenAt,因为目标节点是叶子:

    treeWithNewNode =
        tree2 |> replaceChildrenAt newParentNode [ subtreeToMove ]
    

    All done!

    之所以提到这一点,是因为您将所需结果描述为在树之间移动一个节点:Elm 中的所有数据都是不可变的——因此,在移动之后,tree1tree2 仍然相同.因此,tree1 的子树已被复制到 tree2 的副本中

    【讨论】:

    • 谢谢@O.O.Balance。这适用于替换儿童。你是对的,我没有提取孩子 - 我只是使用价值。我确实尝试了节点(使用getmapChildrenAtappend),结果完全不同 - 它实际上最终将两棵树合并在一起:ellie-app.com/78tWMPxVPrca1。我会再试一次并发布一个不同的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-19
    相关资源
    最近更新 更多