Elm-tree-向另一个树添加分支-递归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的子级,所以让我们从以下内容开始:

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

get函数通过值在树中查找节点。由于可能没有具有指定值的节点,因此它返回一个Maybe,因此我们传入一个默认值。

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

treeWithNewNode =
tree2 |> replaceChildrenAt newParentNode [ subtreeToMove ]

全部完成!

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

我的解决方案不使用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名单对我帮助很大。

最新更新