Elm - 树 - 将分支添加到另一棵树 - 递归 for 循环

Elm - tree - add branch to another tree - recursive forloop

我想在 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,但如果推荐,我可以使用另一个库。

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

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

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

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

treeWithNewNode =
    tree2 |> replaceChildrenAt newParentNode [ subtreeToMove ]

All done!

之所以提到这一点,是因为您将期望的结果描述为在树之间移动一个节点:所有数据在 Elm 中都是不可变的——因此,在移动之后,tree1tree2 还是老样子。因此,来自 tree1 的子树已被复制到 tree2

的副本中

我的解决方案没有 replaceChildrenAt 以保留现有的 children。

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对我帮助很大。