UPDATE:
Pavel correctly answered the original question and made me realize I left out an important detail - the search is on the nth parent. I'm curious if there's a more elegant solution, but at least I got something that works.
type searchf =
| None of None: unit
| CountUp of CountUp: int
| ParentNode of ParentNode: Node
let search (nthParent: int) (tree: Node) (child: Node): Node =
if (nthParent = -1) //-1 Top, 0 Current, 1 Parent, 2 Grandparent
then tree
else
let rec f (t: Node): searchf =
match t = child with
| true when nthParent = 0 -> ParentNode t
| true -> CountUp nthParent
| _ ->
let foldSubs (acc: searchf) (xy: Node): searchf =
match acc with
| CountUp i -> CountUp i
| _ -> f xy
let acc =
match t.ChildNode with
| [] -> None ()
| x -> x |> List.fold foldSubs (None ())
match acc with
| CountUp x when x = 1 -> ParentNode t //parent found in sub list, so this is it
| CountUp x -> CountUp (x - 1)
| _ -> acc
match (f tree) with
| ParentNode t -> t
| _ -> tree
ORIGINAL QUESTION:
I have a small tree, usually 2-5 nodes per branch, max depth of 4. I'm stuck trying to figure out the functional way of searching the tree for a node and returning updated trees.
//I'm not opposed to adding a key field, but my tree does not have one naturally.
type Node = { Name: string; ChildNode: Node list; }
let newNode (name: string)(childNode: Node list) =
{Name=name; ChildNode=childNode;}
let tree =
newNode
"Main" [
newNode "File" [
newNode "Open" [];
newNode "Close" [];
newNode "Print" [
newNode "Preview" [];
newNode "Settings" [];
]
];
newNode "Edit" [
newNode "Cut" [];
newNode "Copy" [];
newNode "Paste" [];
newNode "Preferences" [
newNode "User" [];
newNode "System" [];
]
]
]
let updateTree (tree: Node ) (oldNode: Node ) (newNode: Node ): Node =
//??
tree
let randomNode = tree.ChildNode.[1].ChildNode.[3]
let newRandomNode = { randomNode with Name = "Options"}
let updatedTree = updateTree tree randomNode newRandomNode
Here's a simple example.
let rec updateTree tree oldNode newNode =
if tree = oldNode
then newNode
else { tree with ChildNode = [for a in tree.ChildNode -> updateTree a oldNode newNode]}
UPD:
With the update of the question here's another solution.
To find nth parent of node you can use something like this.
type SearchResult =
| Failure
| Incomplete of int
| Complete of Node
let searchNthParent nthParent node target =
if nthParent < 0
then Complete node
else
// You can just use this function if you
// don't need check on nthParent < 0
let rec inFunc left node target =
if node = target
then
if left > 0
then Incomplete left
else Complete node
else
let rec iterateWhileFailure f list =
match list with
| [] -> Failure
| h::t ->
match f left h target with
| Failure -> iterateWhileFailure f t
| a -> a
match iterateWhileFailure inFunc node.ChildNode with
| Incomplete left when left > 1 -> Incomplete (left - 1)
| Incomplete _ -> Complete node
| a -> a
inFunc nthParent node target
Related
I have a Tree type:
type Tree<'value> =
| Node of value: 'value * children: ConsList<Tree<'value>>
| Leaf of value: 'value
And a fold function for it:
let rec fold folder acc tree =
let f = fold folder
match tree with
| Leaf value -> folder acc value
| Node(value, children) -> ConsList.fold f (folder acc value) children
ConsList in case you need it:
type ConsList<'value> =
| Cons of head: 'value * tail: ConsList<'value>
| Empty
let rec fold folder acc lst =
let f = fold folder
match lst with
| Empty -> acc
| Cons (hd, tl) -> f (folder acc hd) tl
I need a foldBack function, meaning the function passes through the nodes from left to right from top to bottom, starting from the root.
I ended up on this:
let rec foldBack folder acc tree =
let f = fold folder
match tree with
| Leaf value -> f acc value
| Node(value, children) -> f value (f acc *children*)
Children with ** type is expected to be Tree<'a> but has type ConsList<Tree<Tree<'a>>>
For back folds it is common to have accumulator as a function which receives intermediate folded value of sub-branch and returns new folded value with respect to the current element. Thus iterating through the tree normally from top to bottom you literally construct the computation which when receives the terminal elements will compute the bottom to top fold.
You can look for continuation passing style topic more yourself. This approach is also used to optimize for tail-call recursion because function you accumulating is the chain of function objects which doesn't affect stack.
Here is what I've done so far (I replaced ConsList with normal List type, because otherwise it would require to create the foldBack for it as well, which you can try yourself)
type ConsList<'t> = 't list
type Tree<'value> =
| Node of value: 'value * children: ConsList<Tree<'value>>
| Leaf of value: 'value
let foldBack seed folder (tree: 't Tree) =
let rec fold acc tree =
match tree with
| Leaf value ->
let acc' inner = folder (acc inner) value
acc'
| Node (value, children) ->
let acc' inner = List.foldBack (fold acc) children inner
let acc'' inner = folder (acc' inner) value
acc''
fold id tree seed
let treeSample =
Node ("node1", [
Leaf "subnode1";
Node ("node1.1", [
Leaf "subnode1.1";
Node("node1.2", [
Leaf "leaf1.2"
])
])
])
treeSample|>foldBack ">>seed<<" (fun value acc -> $"{value} -> {acc}" )
val it: string = ">>seed<< -> leaf1.2 -> node1.2 -> subnode1.1 -> node1.1 -> subnode1 -> node1"
This function is supposed to just return the index of a list. That part works. However when a element is not in a list it must return -1.
For some reason it does not return -1.
let rec search f list =
match list with
| head::tail ->
if f head then 0
else 1 + search f tail
| [] -> -1
printfn "%A" (search (fun x -> x = 5) [ 5; 4; 3; 2 ])
//>> return index 0 for #5
printfn "%A" (search (fun x -> x = 6) [ 5; 4; 3; 2 ])
//>> should return -1 but it returns 3 which is the len of the list not -1
EDIT: Can not use nested functions.
You could use e.g.
let search f list =
let rec where at list =
match list with
| [] -> -1
| head::tail ->
if f head then at
else where (at + 1) tail
where 0 list
which has the benefit of being tail-recursive. Regarding your comment:
let rec search f list =
match list with
| [] -> -1
| head::tail ->
if f head then 0 else
match search f tail with
| -1 -> -1
| i -> i + 1
I've got a discriminated union tree like this:
type rbtree =
| LeafB of int
| LeafR of int
| Node of int*rbtree*rbtree
And what I have to do is to search for every LeafB present in the tree, so I came with a this recursive function:
let rec searchB (tree:rbtree) : rbtree list =
match tree with
| LeafB(n) -> LeafB(n)::searchB tree
| LeafR(n) -> []
| Node(n,left,right) -> List.append (searchB left) (searchB right)
But when I try to test it I get stack overflow exception and I have no idea how to modify it to work properly.
As #kvb says your updated version isn't truely tail-rec and might cause a stackoverflow as well.
What you can do is using continuations essentially using heap space instead of stack space.
let searchB_ tree =
let rec tail results continuation tree =
match tree with
| LeafB v -> continuation (v::results)
| LeafR _ -> continuation results
| Node (_, lt, rt) -> tail results (fun leftResults -> tail leftResults continuation rt) lt
tail [] id tree |> List.rev
If we looks at the generated code in ILSpy it looks essentially like this:
internal static a tail#13<a>(FSharpList<int> results, FSharpFunc<FSharpList<int>, a> continuation, Program.rbtree tree)
{
while (true)
{
Program.rbtree rbtree = tree;
if (rbtree is Program.rbtree.LeafR)
{
goto IL_34;
}
if (!(rbtree is Program.rbtree.Node))
{
break;
}
Program.rbtree.Node node = (Program.rbtree.Node)tree;
Program.rbtree rt = node.item3;
FSharpList<int> arg_5E_0 = results;
FSharpFunc<FSharpList<int>, a> arg_5C_0 = new Program<a>.tail#17-1(continuation, rt);
tree = node.item2;
continuation = arg_5C_0;
results = arg_5E_0;
}
Program.rbtree.LeafB leafB = (Program.rbtree.LeafB)tree;
int v = leafB.item;
return continuation.Invoke(FSharpList<int>.Cons(v, results));
IL_34:
return continuation.Invoke(results);
}
So as expected with tail recursive functions in F# it is tranformed into a while loop. If we look at the non-tail recursive function:
// Program
public static FSharpList<int> searchB(Program.rbtree tree)
{
if (tree is Program.rbtree.LeafR)
{
return FSharpList<int>.Empty;
}
if (!(tree is Program.rbtree.Node))
{
Program.rbtree.LeafB leafB = (Program.rbtree.LeafB)tree;
return FSharpList<int>.Cons(leafB.item, FSharpList<int>.Empty);
}
Program.rbtree.Node node = (Program.rbtree.Node)tree;
Program.rbtree right = node.item3;
Program.rbtree left = node.item2;
return Operators.op_Append<int>(Program.searchB(left), Program.searchB(right));
}
We see the recursive call at the end of the function Operators.op_Append<int>(Program.searchB(left), Program.searchB(right));
So the tail-recursive function allocates continuations functions instead of creating a new stack frame. We can still run out of heap but there's lot more heap than stack.
Full example demonstrating a stackoverflow:
type rbtree =
| LeafB of int
| LeafR of int
| Node of int*rbtree*rbtree
let rec searchB tree =
match tree with
| LeafB(n) -> n::[]
| LeafR(n) -> []
| Node(n,left,right) -> List.append (searchB left) (searchB right)
let searchB_ tree =
let rec tail results continuation tree =
match tree with
| LeafB v -> continuation (v::results)
| LeafR _ -> continuation results
| Node (_, lt, rt) -> tail results (fun leftResults -> tail leftResults continuation rt) lt
tail [] id tree |> List.rev
let rec genTree n =
let rec loop i t =
if i > 0 then
loop (i - 1) (Node (i, t, LeafB i))
else
t
loop n (LeafB n)
[<EntryPoint>]
let main argv =
printfn "generate left leaning tree..."
let tree = genTree 100000
printfn "tail rec"
let s = searchB_ tree
printfn "rec"
let f = searchB tree
printfn "Is equal? %A" (f = s)
0
Oh, I might came with an solution:
let rec searchB (tree:rbtree) : rbtree list =
match tree with
| LeafB(n) -> LeafB(n)::[]
| LeafR(n) -> []
| Node(n,left,right) -> List.append (searchB left) (searchB right)
Now it looks like working properly when I try it.
I was trying to build a binary tree in F# but when I tried to test my code, I met the problem above.
Here is my code:
type TreeNode<'a> = { Key: int; Val: 'a }
type Tree<'a> = { LT: Tree<'a> option; TreeNode: TreeNode<'a>; RT: Tree<'a> option; }
//insert a node according to Binary Tree operation
let rec insert (node: TreeNode<'a>) (tree: Tree<'a> option) =
match tree with
| None -> {LT = None; RT = None; TreeNode = node }
| Some t when node.Key < t.TreeNode.Key -> insert node t.LT
| Some t when node.Key > t.TreeNode.Key -> insert node t.RT
let t = seq { for i in 1 .. 10 -> { Key = i; Val = i } }|> Seq.fold (fun a i -> insert i a) None
Your insert function takes option<Tree<'T>> but returns Tree<'T>. When performing the fold, you need to keep state of the same type - so if you want to use None to represent empty tree, the state needs to be optional type.
The way to fix this is to wrap the result of insert in Some:
let tree =
seq { for i in 1 .. 10 -> { Key = i; Val = i } }
|> Seq.fold (fun a i -> Some(insert i a)) None
I worked it out now... It should be like below:
type TreeNode<'a> = { Key: int; Val: 'a }
type Tree<'a> = { TreeNode: TreeNode<'a>; RT: Tree<'a> option; LT: Tree<'a> option; }
//insert a node according to Binary Tree operation
let rec insert (node: TreeNode<'a>) (tree: Tree<'a> option) =
match tree with
| None -> {LT = None; RT = None; TreeNode = node }
| Some t when node.Key < t.TreeNode.Key -> {TreeNode = t.TreeNode; LT = Some(insert node t.LT); RT = t.RT}
| Some t when node.Key > t.TreeNode.Key -> {TreeNode = t.TreeNode; RT = Some(insert node t.RT); LT = t.LT}
let t = seq { for i in 1 .. 10-> { Key = i; Val = i } }|> Seq.fold (fun a i -> Some(insert i a)) None
this code i got is from Alexander Battisti about how to make a tree from a list of data:
let data = [4;3;8;7;10;1;9;6;5;0;2]
type Tree<'a> =
| Node of Tree<'a> * 'a * Tree<'a>
| Leaf
let rec insert tree element =
match element,tree with
| x,Leaf -> Node(Leaf,x,Leaf)
| x,Node(l,y,r) when x <= y -> Node((insert l x),y,r)
| x,Node(l,y,r) when x > y -> Node(l,y,(insert r x))
| _ -> Leaf
let makeTree = List.fold insert Leaf data
then i want to implement this code to my binary search tree code
let rec BinarySearch tree element =
match element,tree with
| x,Leaf -> BinarySearch (Node(Leaf,x,Leaf)) x
| x,Node(l,y,r) when x<=y ->
BinarySearch l y
| x,Node(l,y,r) when x>y ->
BinarySearch r y
| x,Node(l,y,r) when x=y ->
true
| _ -> false
then i use my search code like this:
> BinarySearch makeTree 5;;
and the result is none because it's like i got an infinite looping
can someone help me? if my code is wrong, please help me to correct it, thank you
The solution by Yin is how I would write it too.
Anyway, here is a solution that is closer to your version and (hopefully) explains what went wrong:
let rec BinarySearch tree element =
match element,tree with
| x, Leaf ->
// You originally called 'BinarySearch' here, but that's wrong - if we reach
// the leaf of the tree (on the path from root to leaf) then we know that the
// element is not in the tree so we return false
false
| x, Node(l,y,r) when x<y ->// This needs to be 'x<y', otherwise the clause would be
// matched when 'x=y' and we wouldn't find the element!
BinarySearch l element // Your recursive call was 'BinarySearch l y' but
// that's wrong - you want to search for 'element'
| x, Node(l,y,r) when x>y ->
BinarySearch r element
| x,Node(l,y,r) -> // You can simplify the code by omitting the 'when'
true // clause (because this will only be reached when
// x=y. Then you can omit the last (unreachable) case
let rec BinarySearch tree element =
match tree with
| Leaf -> false
| Node(l, v, r) ->
if v = element then
true
elif v < element then
BinarySearch r element
else
BinarySearch l element
BinarySearch makeTree 5