我想在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中都是不可变的——因此,移动后,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名单对我帮助很大。