Elm-树-向另一棵树添加分支-递归forloop

问题描述 投票:2回答:2

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

例如:

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

for-loop recursion functional-programming tree elm
2个回答
3
投票

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

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

的副本中

0
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.