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 ]
之所以提到这一点,是因为您将期望的结果描述为在树之间移动一个节点:所有数据在 Elm 中都是不可变的——因此,在移动之后,tree1
和tree2
还是老样子。因此,来自 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对我帮助很大。
我想在 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 ]
之所以提到这一点,是因为您将期望的结果描述为在树之间移动一个节点:所有数据在 Elm 中都是不可变的——因此,在移动之后,tree1
和tree2
还是老样子。因此,来自 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对我帮助很大。