我想在榆树中将树枝从一棵树移到另一棵树。
例如:
树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,但如果推荐的话,我可以使用另一个库。
在您的代码中,对我来说,您似乎从未真正从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 ]
仅提及这一点是因为您将所需的结果描述为移动树之间的一个节点:Elm中的所有数据都是不可变的–因此,移动之后,tree1
和tree2
仍然相同。因此,将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-list对我有很大帮助。