How to make tree mapping tail-recursive?Tail recursive functions for BinaryTreeWhat is tail recursion?What Is Tail Call Optimization?What's a good way to rewrite this non-tail-recursive function?What are the options for storing hierarchical data in a relational database?Scala: Tree Insert Tail Recursion With Complex StructureHow to append to Rose Trees in ScalaSplitting a BinTree with tail recursion in HaskellTail recursive functions for BinaryTreeTransverse a tree like object in a Tail recursive way in scalaNon-binary tree recursion

There is only s̶i̶x̶t̶y one place he can be

Applicability of Single Responsibility Principle

Is there any reason not to eat food that's been dropped on the surface of the moon?

Cynical novel that describes an America ruled by the media, arms manufacturers, and ethnic figureheads

Personal Teleportation as a Weapon

Implement the Thanos sorting algorithm

Where in the Bible does the greeting ("Dominus Vobiscum") used at Mass come from?

Trouble understanding overseas colleagues

Transcription Beats per minute

How could Frankenstein get the parts for his _second_ creature?

Is it correct to write "is not focus on"?

Best way to store options for panels

Opposite of a diet

I'm in charge of equipment buying but no one's ever happy with what I choose. How to fix this?

What is the oldest known work of fiction?

What to do with wrong results in talks?

Hostile work environment after whistle-blowing on coworker and our boss. What do I do?

Go Pregnant or Go Home

How does it work when somebody invests in my business?

How was Earth single-handedly capable of creating 3 of the 4 gods of chaos?

Can a monster with multiattack use this ability if they are missing a limb?

Can I Retrieve Email Addresses from BCC?

Everything Bob says is false. How does he get people to trust him?

How do I rename a LINUX host without needing to reboot for the rename to take effect?



How to make tree mapping tail-recursive?


Tail recursive functions for BinaryTreeWhat is tail recursion?What Is Tail Call Optimization?What's a good way to rewrite this non-tail-recursive function?What are the options for storing hierarchical data in a relational database?Scala: Tree Insert Tail Recursion With Complex StructureHow to append to Rose Trees in ScalaSplitting a BinTree with tail recursion in HaskellTail recursive functions for BinaryTreeTransverse a tree like object in a Tail recursive way in scalaNon-binary tree recursion













6















Suppose I have a tree data structure like this:



trait Node val name: String 
case class BranchNode(name: String, children: List[Node]) extends Node
case class LeafNode(name: String) extends Node


Suppose also I've got a function to map over leaves:



def mapLeaves(root: Node, f: LeafNode => LeafNode): Node = root match 
case ln: LeafNode => f(ln)
case bn: BranchNode => BranchNode(bn.name, bn.children.map(ch => mapLeaves(ch, f)))



Now I am trying to make this function tail-recursive but having a hard time to figure out how to do it. I've read this answer but still don't know to make that binary tree solution work for a multiway tree.



How would you rewrite mapLeaves to make it tail-recursive?










share|improve this question



















  • 1





    I think you need some intermediate format, as you see in your mentioned 'answer' - there is a List (which is also the result as a difference to your problem)

    – pme
    Mar 7 at 13:08











  • Thanks for the suggestion. I concur I need some "todo list" (as in the mentioned answer) but I cannot figure out how to do it.

    – Michael
    Mar 7 at 13:41












  • Can you use cats or scalaz?

    – Krzysztof Atłasik
    Mar 7 at 14:51







  • 2





    I would prefer not to (in this particular case).

    – Michael
    Mar 7 at 14:53















6















Suppose I have a tree data structure like this:



trait Node val name: String 
case class BranchNode(name: String, children: List[Node]) extends Node
case class LeafNode(name: String) extends Node


Suppose also I've got a function to map over leaves:



def mapLeaves(root: Node, f: LeafNode => LeafNode): Node = root match 
case ln: LeafNode => f(ln)
case bn: BranchNode => BranchNode(bn.name, bn.children.map(ch => mapLeaves(ch, f)))



Now I am trying to make this function tail-recursive but having a hard time to figure out how to do it. I've read this answer but still don't know to make that binary tree solution work for a multiway tree.



How would you rewrite mapLeaves to make it tail-recursive?










share|improve this question



















  • 1





    I think you need some intermediate format, as you see in your mentioned 'answer' - there is a List (which is also the result as a difference to your problem)

    – pme
    Mar 7 at 13:08











  • Thanks for the suggestion. I concur I need some "todo list" (as in the mentioned answer) but I cannot figure out how to do it.

    – Michael
    Mar 7 at 13:41












  • Can you use cats or scalaz?

    – Krzysztof Atłasik
    Mar 7 at 14:51







  • 2





    I would prefer not to (in this particular case).

    – Michael
    Mar 7 at 14:53













6












6








6


4






Suppose I have a tree data structure like this:



trait Node val name: String 
case class BranchNode(name: String, children: List[Node]) extends Node
case class LeafNode(name: String) extends Node


Suppose also I've got a function to map over leaves:



def mapLeaves(root: Node, f: LeafNode => LeafNode): Node = root match 
case ln: LeafNode => f(ln)
case bn: BranchNode => BranchNode(bn.name, bn.children.map(ch => mapLeaves(ch, f)))



Now I am trying to make this function tail-recursive but having a hard time to figure out how to do it. I've read this answer but still don't know to make that binary tree solution work for a multiway tree.



How would you rewrite mapLeaves to make it tail-recursive?










share|improve this question
















Suppose I have a tree data structure like this:



trait Node val name: String 
case class BranchNode(name: String, children: List[Node]) extends Node
case class LeafNode(name: String) extends Node


Suppose also I've got a function to map over leaves:



def mapLeaves(root: Node, f: LeafNode => LeafNode): Node = root match 
case ln: LeafNode => f(ln)
case bn: BranchNode => BranchNode(bn.name, bn.children.map(ch => mapLeaves(ch, f)))



Now I am trying to make this function tail-recursive but having a hard time to figure out how to do it. I've read this answer but still don't know to make that binary tree solution work for a multiway tree.



How would you rewrite mapLeaves to make it tail-recursive?







scala recursion functional-programming tree tail-recursion






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Mar 21 at 11:44







Michael

















asked Mar 7 at 11:35









MichaelMichael

15.8k49133256




15.8k49133256







  • 1





    I think you need some intermediate format, as you see in your mentioned 'answer' - there is a List (which is also the result as a difference to your problem)

    – pme
    Mar 7 at 13:08











  • Thanks for the suggestion. I concur I need some "todo list" (as in the mentioned answer) but I cannot figure out how to do it.

    – Michael
    Mar 7 at 13:41












  • Can you use cats or scalaz?

    – Krzysztof Atłasik
    Mar 7 at 14:51







  • 2





    I would prefer not to (in this particular case).

    – Michael
    Mar 7 at 14:53












  • 1





    I think you need some intermediate format, as you see in your mentioned 'answer' - there is a List (which is also the result as a difference to your problem)

    – pme
    Mar 7 at 13:08











  • Thanks for the suggestion. I concur I need some "todo list" (as in the mentioned answer) but I cannot figure out how to do it.

    – Michael
    Mar 7 at 13:41












  • Can you use cats or scalaz?

    – Krzysztof Atłasik
    Mar 7 at 14:51







  • 2





    I would prefer not to (in this particular case).

    – Michael
    Mar 7 at 14:53







1




1





I think you need some intermediate format, as you see in your mentioned 'answer' - there is a List (which is also the result as a difference to your problem)

– pme
Mar 7 at 13:08





I think you need some intermediate format, as you see in your mentioned 'answer' - there is a List (which is also the result as a difference to your problem)

– pme
Mar 7 at 13:08













Thanks for the suggestion. I concur I need some "todo list" (as in the mentioned answer) but I cannot figure out how to do it.

– Michael
Mar 7 at 13:41






Thanks for the suggestion. I concur I need some "todo list" (as in the mentioned answer) but I cannot figure out how to do it.

– Michael
Mar 7 at 13:41














Can you use cats or scalaz?

– Krzysztof Atłasik
Mar 7 at 14:51






Can you use cats or scalaz?

– Krzysztof Atłasik
Mar 7 at 14:51





2




2





I would prefer not to (in this particular case).

– Michael
Mar 7 at 14:53





I would prefer not to (in this particular case).

– Michael
Mar 7 at 14:53












2 Answers
2






active

oldest

votes


















5














"Call stack" and "recursion" are merely popular design patterns that later got incorporated into most programming languages (and thus became mostly "invisible"). There is nothing that prevents you from reimplementing both with heap data structures. So, here is "the obvious" 1960's TAOCP retro-style solution:



trait Node val name: String 
case class BranchNode(name: String, children: List[Node]) extends Node
case class LeafNode(name: String) extends Node

def mapLeaves(root: Node, f: LeafNode => LeafNode): Node =
case class Frame(name: String, mapped: List[Node], todos: List[Node])
@annotation.tailrec
def step(stack: List[Frame]): Node = stack match
// "return / pop a stack-frame"
case Frame(name, done, Nil) :: tail =>
val ret = BranchNode(name, done.reverse)
tail match
case Nil => ret
case Frame(tn, td, tt) :: more =>
step(Frame(tn, ret :: td, tt) :: more)



case Frame(name, done, x :: xs) :: tail => x match
// "recursion base"
case l @ LeafNode(_) => step(Frame(name, f(l) :: done, xs) :: tail)
// "recursive call"
case BranchNode(n, cs) => step(Frame(n, Nil, cs) :: Frame(name, done, xs) :: tail)

case Nil => throw new Error("shouldn't happen")

root match
case l @ LeafNode(_) => f(l)
case b @ BranchNode(n, cs) => step(List(Frame(n, Nil, cs)))




The tail-recursive step function takes a reified stack with "stack frames". A "stack frame" stores the name of the branch node that is currently being processed, a list of child nodes that have already been processed, and the list of the remaining nodes that still must be processed later. This roughly corresponds to an actual stack frame of your recursive mapLeaves function.



With this data structure,



  • returning from recursive calls corresponds to deconstructing a Frame object, and either returning the final result, or at least making the stack one frame shorter.

  • recursive calls correspond to a step that prepends a Frame to the stack

  • base case (invoking f on leaves) does not create or remove any frames

Once one understands how the usually invisible stack frames are represented explicitly, the translation is straightforward and mostly mechanical.



Example:



val example = BranchNode("x", List(
BranchNode("y", List(
LeafNode("a"),
LeafNode("b")
)),
BranchNode("z", List(
LeafNode("c"),
BranchNode("v", List(
LeafNode("d"),
LeafNode("e")
))
))
))

println(mapLeaves(example, case LeafNode(n) => LeafNode(n.toUpperCase) ))


Output (indented):



BranchNode(x,List(
BranchNode(y,List(
LeafNode(A),
LeafNode(B)
)),
BranchNode(z, List(
LeafNode(C),
BranchNode(v,List(
LeafNode(D),
LeafNode(E)
))
))
))





share|improve this answer

























  • Thanks a lot for so detailed answer. I understand how to implement call stack explicitly. Now I am just wondering how to simplify this solution.

    – Michael
    Mar 8 at 12:27


















4














It might be easier to implement it using a technique called trampoline.
If you use it, you'd be able to use two functions calling itself doing mutual recursion (with tailrec, you are limited to one function). Similarly to tailrec this recursion will be transformed to plain loop.



Trampolines are implemented in scala standard library in scala.util.control.TailCalls.



import scala.util.control.TailCalls.TailRec, done, tailcall

def mapLeaves(root: Node, f: LeafNode => LeafNode): Node =

//two inner functions doing mutual recursion

//iterates recursively over children of node
def iterate(nodes: List[Node]): TailRec[List[Node]] =
nodes match
case x :: xs => tailcall(deepMap(x)) //it calls with mutual recursion deepMap which maps over children of node
.flatMap(node => iterate(xs).map(node :: _)) //you can flat map over TailRec
case Nil => done(Nil)



//recursively visits all branches
def deepMap(node: Node): TailRec[Node] =
node match
case ln: LeafNode => done(f(ln))
case bn: BranchNode => tailcall(iterate(bn.children))
.map(BranchNode(bn.name, _)) //calls mutually iterate



deepMap(root).result //unwrap result to plain node



Instead of TailCalls you could also use Eval from Cats or Trampoline from scalaz.



With that implementation function worked without problems:



def build(counter: Int): Node = 
if (counter > 0)
BranchNode("branch", List(build(counter-1)))
else
LeafNode("leaf")



val root = build(4000)

mapLeaves(root, x => x.copy(name = x.name.reverse)) // no problems


When I ran that example with your implementation it caused java.lang.StackOverflowError as expected.






share|improve this answer




















  • 1





    Thanks for introducing trampolines. I need to learn about them.

    – Michael
    Mar 8 at 8:38










Your Answer






StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);













draft saved

draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55042834%2fhow-to-make-tree-mapping-tail-recursive%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown

























2 Answers
2






active

oldest

votes








2 Answers
2






active

oldest

votes









active

oldest

votes






active

oldest

votes









5














"Call stack" and "recursion" are merely popular design patterns that later got incorporated into most programming languages (and thus became mostly "invisible"). There is nothing that prevents you from reimplementing both with heap data structures. So, here is "the obvious" 1960's TAOCP retro-style solution:



trait Node val name: String 
case class BranchNode(name: String, children: List[Node]) extends Node
case class LeafNode(name: String) extends Node

def mapLeaves(root: Node, f: LeafNode => LeafNode): Node =
case class Frame(name: String, mapped: List[Node], todos: List[Node])
@annotation.tailrec
def step(stack: List[Frame]): Node = stack match
// "return / pop a stack-frame"
case Frame(name, done, Nil) :: tail =>
val ret = BranchNode(name, done.reverse)
tail match
case Nil => ret
case Frame(tn, td, tt) :: more =>
step(Frame(tn, ret :: td, tt) :: more)



case Frame(name, done, x :: xs) :: tail => x match
// "recursion base"
case l @ LeafNode(_) => step(Frame(name, f(l) :: done, xs) :: tail)
// "recursive call"
case BranchNode(n, cs) => step(Frame(n, Nil, cs) :: Frame(name, done, xs) :: tail)

case Nil => throw new Error("shouldn't happen")

root match
case l @ LeafNode(_) => f(l)
case b @ BranchNode(n, cs) => step(List(Frame(n, Nil, cs)))




The tail-recursive step function takes a reified stack with "stack frames". A "stack frame" stores the name of the branch node that is currently being processed, a list of child nodes that have already been processed, and the list of the remaining nodes that still must be processed later. This roughly corresponds to an actual stack frame of your recursive mapLeaves function.



With this data structure,



  • returning from recursive calls corresponds to deconstructing a Frame object, and either returning the final result, or at least making the stack one frame shorter.

  • recursive calls correspond to a step that prepends a Frame to the stack

  • base case (invoking f on leaves) does not create or remove any frames

Once one understands how the usually invisible stack frames are represented explicitly, the translation is straightforward and mostly mechanical.



Example:



val example = BranchNode("x", List(
BranchNode("y", List(
LeafNode("a"),
LeafNode("b")
)),
BranchNode("z", List(
LeafNode("c"),
BranchNode("v", List(
LeafNode("d"),
LeafNode("e")
))
))
))

println(mapLeaves(example, case LeafNode(n) => LeafNode(n.toUpperCase) ))


Output (indented):



BranchNode(x,List(
BranchNode(y,List(
LeafNode(A),
LeafNode(B)
)),
BranchNode(z, List(
LeafNode(C),
BranchNode(v,List(
LeafNode(D),
LeafNode(E)
))
))
))





share|improve this answer

























  • Thanks a lot for so detailed answer. I understand how to implement call stack explicitly. Now I am just wondering how to simplify this solution.

    – Michael
    Mar 8 at 12:27















5














"Call stack" and "recursion" are merely popular design patterns that later got incorporated into most programming languages (and thus became mostly "invisible"). There is nothing that prevents you from reimplementing both with heap data structures. So, here is "the obvious" 1960's TAOCP retro-style solution:



trait Node val name: String 
case class BranchNode(name: String, children: List[Node]) extends Node
case class LeafNode(name: String) extends Node

def mapLeaves(root: Node, f: LeafNode => LeafNode): Node =
case class Frame(name: String, mapped: List[Node], todos: List[Node])
@annotation.tailrec
def step(stack: List[Frame]): Node = stack match
// "return / pop a stack-frame"
case Frame(name, done, Nil) :: tail =>
val ret = BranchNode(name, done.reverse)
tail match
case Nil => ret
case Frame(tn, td, tt) :: more =>
step(Frame(tn, ret :: td, tt) :: more)



case Frame(name, done, x :: xs) :: tail => x match
// "recursion base"
case l @ LeafNode(_) => step(Frame(name, f(l) :: done, xs) :: tail)
// "recursive call"
case BranchNode(n, cs) => step(Frame(n, Nil, cs) :: Frame(name, done, xs) :: tail)

case Nil => throw new Error("shouldn't happen")

root match
case l @ LeafNode(_) => f(l)
case b @ BranchNode(n, cs) => step(List(Frame(n, Nil, cs)))




The tail-recursive step function takes a reified stack with "stack frames". A "stack frame" stores the name of the branch node that is currently being processed, a list of child nodes that have already been processed, and the list of the remaining nodes that still must be processed later. This roughly corresponds to an actual stack frame of your recursive mapLeaves function.



With this data structure,



  • returning from recursive calls corresponds to deconstructing a Frame object, and either returning the final result, or at least making the stack one frame shorter.

  • recursive calls correspond to a step that prepends a Frame to the stack

  • base case (invoking f on leaves) does not create or remove any frames

Once one understands how the usually invisible stack frames are represented explicitly, the translation is straightforward and mostly mechanical.



Example:



val example = BranchNode("x", List(
BranchNode("y", List(
LeafNode("a"),
LeafNode("b")
)),
BranchNode("z", List(
LeafNode("c"),
BranchNode("v", List(
LeafNode("d"),
LeafNode("e")
))
))
))

println(mapLeaves(example, case LeafNode(n) => LeafNode(n.toUpperCase) ))


Output (indented):



BranchNode(x,List(
BranchNode(y,List(
LeafNode(A),
LeafNode(B)
)),
BranchNode(z, List(
LeafNode(C),
BranchNode(v,List(
LeafNode(D),
LeafNode(E)
))
))
))





share|improve this answer

























  • Thanks a lot for so detailed answer. I understand how to implement call stack explicitly. Now I am just wondering how to simplify this solution.

    – Michael
    Mar 8 at 12:27













5












5








5







"Call stack" and "recursion" are merely popular design patterns that later got incorporated into most programming languages (and thus became mostly "invisible"). There is nothing that prevents you from reimplementing both with heap data structures. So, here is "the obvious" 1960's TAOCP retro-style solution:



trait Node val name: String 
case class BranchNode(name: String, children: List[Node]) extends Node
case class LeafNode(name: String) extends Node

def mapLeaves(root: Node, f: LeafNode => LeafNode): Node =
case class Frame(name: String, mapped: List[Node], todos: List[Node])
@annotation.tailrec
def step(stack: List[Frame]): Node = stack match
// "return / pop a stack-frame"
case Frame(name, done, Nil) :: tail =>
val ret = BranchNode(name, done.reverse)
tail match
case Nil => ret
case Frame(tn, td, tt) :: more =>
step(Frame(tn, ret :: td, tt) :: more)



case Frame(name, done, x :: xs) :: tail => x match
// "recursion base"
case l @ LeafNode(_) => step(Frame(name, f(l) :: done, xs) :: tail)
// "recursive call"
case BranchNode(n, cs) => step(Frame(n, Nil, cs) :: Frame(name, done, xs) :: tail)

case Nil => throw new Error("shouldn't happen")

root match
case l @ LeafNode(_) => f(l)
case b @ BranchNode(n, cs) => step(List(Frame(n, Nil, cs)))




The tail-recursive step function takes a reified stack with "stack frames". A "stack frame" stores the name of the branch node that is currently being processed, a list of child nodes that have already been processed, and the list of the remaining nodes that still must be processed later. This roughly corresponds to an actual stack frame of your recursive mapLeaves function.



With this data structure,



  • returning from recursive calls corresponds to deconstructing a Frame object, and either returning the final result, or at least making the stack one frame shorter.

  • recursive calls correspond to a step that prepends a Frame to the stack

  • base case (invoking f on leaves) does not create or remove any frames

Once one understands how the usually invisible stack frames are represented explicitly, the translation is straightforward and mostly mechanical.



Example:



val example = BranchNode("x", List(
BranchNode("y", List(
LeafNode("a"),
LeafNode("b")
)),
BranchNode("z", List(
LeafNode("c"),
BranchNode("v", List(
LeafNode("d"),
LeafNode("e")
))
))
))

println(mapLeaves(example, case LeafNode(n) => LeafNode(n.toUpperCase) ))


Output (indented):



BranchNode(x,List(
BranchNode(y,List(
LeafNode(A),
LeafNode(B)
)),
BranchNode(z, List(
LeafNode(C),
BranchNode(v,List(
LeafNode(D),
LeafNode(E)
))
))
))





share|improve this answer















"Call stack" and "recursion" are merely popular design patterns that later got incorporated into most programming languages (and thus became mostly "invisible"). There is nothing that prevents you from reimplementing both with heap data structures. So, here is "the obvious" 1960's TAOCP retro-style solution:



trait Node val name: String 
case class BranchNode(name: String, children: List[Node]) extends Node
case class LeafNode(name: String) extends Node

def mapLeaves(root: Node, f: LeafNode => LeafNode): Node =
case class Frame(name: String, mapped: List[Node], todos: List[Node])
@annotation.tailrec
def step(stack: List[Frame]): Node = stack match
// "return / pop a stack-frame"
case Frame(name, done, Nil) :: tail =>
val ret = BranchNode(name, done.reverse)
tail match
case Nil => ret
case Frame(tn, td, tt) :: more =>
step(Frame(tn, ret :: td, tt) :: more)



case Frame(name, done, x :: xs) :: tail => x match
// "recursion base"
case l @ LeafNode(_) => step(Frame(name, f(l) :: done, xs) :: tail)
// "recursive call"
case BranchNode(n, cs) => step(Frame(n, Nil, cs) :: Frame(name, done, xs) :: tail)

case Nil => throw new Error("shouldn't happen")

root match
case l @ LeafNode(_) => f(l)
case b @ BranchNode(n, cs) => step(List(Frame(n, Nil, cs)))




The tail-recursive step function takes a reified stack with "stack frames". A "stack frame" stores the name of the branch node that is currently being processed, a list of child nodes that have already been processed, and the list of the remaining nodes that still must be processed later. This roughly corresponds to an actual stack frame of your recursive mapLeaves function.



With this data structure,



  • returning from recursive calls corresponds to deconstructing a Frame object, and either returning the final result, or at least making the stack one frame shorter.

  • recursive calls correspond to a step that prepends a Frame to the stack

  • base case (invoking f on leaves) does not create or remove any frames

Once one understands how the usually invisible stack frames are represented explicitly, the translation is straightforward and mostly mechanical.



Example:



val example = BranchNode("x", List(
BranchNode("y", List(
LeafNode("a"),
LeafNode("b")
)),
BranchNode("z", List(
LeafNode("c"),
BranchNode("v", List(
LeafNode("d"),
LeafNode("e")
))
))
))

println(mapLeaves(example, case LeafNode(n) => LeafNode(n.toUpperCase) ))


Output (indented):



BranchNode(x,List(
BranchNode(y,List(
LeafNode(A),
LeafNode(B)
)),
BranchNode(z, List(
LeafNode(C),
BranchNode(v,List(
LeafNode(D),
LeafNode(E)
))
))
))






share|improve this answer














share|improve this answer



share|improve this answer








edited Mar 7 at 16:10

























answered Mar 7 at 15:26









Andrey TyukinAndrey Tyukin

30k42351




30k42351












  • Thanks a lot for so detailed answer. I understand how to implement call stack explicitly. Now I am just wondering how to simplify this solution.

    – Michael
    Mar 8 at 12:27

















  • Thanks a lot for so detailed answer. I understand how to implement call stack explicitly. Now I am just wondering how to simplify this solution.

    – Michael
    Mar 8 at 12:27
















Thanks a lot for so detailed answer. I understand how to implement call stack explicitly. Now I am just wondering how to simplify this solution.

– Michael
Mar 8 at 12:27





Thanks a lot for so detailed answer. I understand how to implement call stack explicitly. Now I am just wondering how to simplify this solution.

– Michael
Mar 8 at 12:27













4














It might be easier to implement it using a technique called trampoline.
If you use it, you'd be able to use two functions calling itself doing mutual recursion (with tailrec, you are limited to one function). Similarly to tailrec this recursion will be transformed to plain loop.



Trampolines are implemented in scala standard library in scala.util.control.TailCalls.



import scala.util.control.TailCalls.TailRec, done, tailcall

def mapLeaves(root: Node, f: LeafNode => LeafNode): Node =

//two inner functions doing mutual recursion

//iterates recursively over children of node
def iterate(nodes: List[Node]): TailRec[List[Node]] =
nodes match
case x :: xs => tailcall(deepMap(x)) //it calls with mutual recursion deepMap which maps over children of node
.flatMap(node => iterate(xs).map(node :: _)) //you can flat map over TailRec
case Nil => done(Nil)



//recursively visits all branches
def deepMap(node: Node): TailRec[Node] =
node match
case ln: LeafNode => done(f(ln))
case bn: BranchNode => tailcall(iterate(bn.children))
.map(BranchNode(bn.name, _)) //calls mutually iterate



deepMap(root).result //unwrap result to plain node



Instead of TailCalls you could also use Eval from Cats or Trampoline from scalaz.



With that implementation function worked without problems:



def build(counter: Int): Node = 
if (counter > 0)
BranchNode("branch", List(build(counter-1)))
else
LeafNode("leaf")



val root = build(4000)

mapLeaves(root, x => x.copy(name = x.name.reverse)) // no problems


When I ran that example with your implementation it caused java.lang.StackOverflowError as expected.






share|improve this answer




















  • 1





    Thanks for introducing trampolines. I need to learn about them.

    – Michael
    Mar 8 at 8:38















4














It might be easier to implement it using a technique called trampoline.
If you use it, you'd be able to use two functions calling itself doing mutual recursion (with tailrec, you are limited to one function). Similarly to tailrec this recursion will be transformed to plain loop.



Trampolines are implemented in scala standard library in scala.util.control.TailCalls.



import scala.util.control.TailCalls.TailRec, done, tailcall

def mapLeaves(root: Node, f: LeafNode => LeafNode): Node =

//two inner functions doing mutual recursion

//iterates recursively over children of node
def iterate(nodes: List[Node]): TailRec[List[Node]] =
nodes match
case x :: xs => tailcall(deepMap(x)) //it calls with mutual recursion deepMap which maps over children of node
.flatMap(node => iterate(xs).map(node :: _)) //you can flat map over TailRec
case Nil => done(Nil)



//recursively visits all branches
def deepMap(node: Node): TailRec[Node] =
node match
case ln: LeafNode => done(f(ln))
case bn: BranchNode => tailcall(iterate(bn.children))
.map(BranchNode(bn.name, _)) //calls mutually iterate



deepMap(root).result //unwrap result to plain node



Instead of TailCalls you could also use Eval from Cats or Trampoline from scalaz.



With that implementation function worked without problems:



def build(counter: Int): Node = 
if (counter > 0)
BranchNode("branch", List(build(counter-1)))
else
LeafNode("leaf")



val root = build(4000)

mapLeaves(root, x => x.copy(name = x.name.reverse)) // no problems


When I ran that example with your implementation it caused java.lang.StackOverflowError as expected.






share|improve this answer




















  • 1





    Thanks for introducing trampolines. I need to learn about them.

    – Michael
    Mar 8 at 8:38













4












4








4







It might be easier to implement it using a technique called trampoline.
If you use it, you'd be able to use two functions calling itself doing mutual recursion (with tailrec, you are limited to one function). Similarly to tailrec this recursion will be transformed to plain loop.



Trampolines are implemented in scala standard library in scala.util.control.TailCalls.



import scala.util.control.TailCalls.TailRec, done, tailcall

def mapLeaves(root: Node, f: LeafNode => LeafNode): Node =

//two inner functions doing mutual recursion

//iterates recursively over children of node
def iterate(nodes: List[Node]): TailRec[List[Node]] =
nodes match
case x :: xs => tailcall(deepMap(x)) //it calls with mutual recursion deepMap which maps over children of node
.flatMap(node => iterate(xs).map(node :: _)) //you can flat map over TailRec
case Nil => done(Nil)



//recursively visits all branches
def deepMap(node: Node): TailRec[Node] =
node match
case ln: LeafNode => done(f(ln))
case bn: BranchNode => tailcall(iterate(bn.children))
.map(BranchNode(bn.name, _)) //calls mutually iterate



deepMap(root).result //unwrap result to plain node



Instead of TailCalls you could also use Eval from Cats or Trampoline from scalaz.



With that implementation function worked without problems:



def build(counter: Int): Node = 
if (counter > 0)
BranchNode("branch", List(build(counter-1)))
else
LeafNode("leaf")



val root = build(4000)

mapLeaves(root, x => x.copy(name = x.name.reverse)) // no problems


When I ran that example with your implementation it caused java.lang.StackOverflowError as expected.






share|improve this answer















It might be easier to implement it using a technique called trampoline.
If you use it, you'd be able to use two functions calling itself doing mutual recursion (with tailrec, you are limited to one function). Similarly to tailrec this recursion will be transformed to plain loop.



Trampolines are implemented in scala standard library in scala.util.control.TailCalls.



import scala.util.control.TailCalls.TailRec, done, tailcall

def mapLeaves(root: Node, f: LeafNode => LeafNode): Node =

//two inner functions doing mutual recursion

//iterates recursively over children of node
def iterate(nodes: List[Node]): TailRec[List[Node]] =
nodes match
case x :: xs => tailcall(deepMap(x)) //it calls with mutual recursion deepMap which maps over children of node
.flatMap(node => iterate(xs).map(node :: _)) //you can flat map over TailRec
case Nil => done(Nil)



//recursively visits all branches
def deepMap(node: Node): TailRec[Node] =
node match
case ln: LeafNode => done(f(ln))
case bn: BranchNode => tailcall(iterate(bn.children))
.map(BranchNode(bn.name, _)) //calls mutually iterate



deepMap(root).result //unwrap result to plain node



Instead of TailCalls you could also use Eval from Cats or Trampoline from scalaz.



With that implementation function worked without problems:



def build(counter: Int): Node = 
if (counter > 0)
BranchNode("branch", List(build(counter-1)))
else
LeafNode("leaf")



val root = build(4000)

mapLeaves(root, x => x.copy(name = x.name.reverse)) // no problems


When I ran that example with your implementation it caused java.lang.StackOverflowError as expected.







share|improve this answer














share|improve this answer



share|improve this answer








edited Mar 8 at 14:42

























answered Mar 7 at 15:41









Krzysztof AtłasikKrzysztof Atłasik

4,79711234




4,79711234







  • 1





    Thanks for introducing trampolines. I need to learn about them.

    – Michael
    Mar 8 at 8:38












  • 1





    Thanks for introducing trampolines. I need to learn about them.

    – Michael
    Mar 8 at 8:38







1




1





Thanks for introducing trampolines. I need to learn about them.

– Michael
Mar 8 at 8:38





Thanks for introducing trampolines. I need to learn about them.

– Michael
Mar 8 at 8:38

















draft saved

draft discarded
















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid


  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55042834%2fhow-to-make-tree-mapping-tail-recursive%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

AWS Lex not identifying response if by a variable The 2019 Stack Overflow Developer Survey Results Are In Announcing the arrival of Valued Associate #679: Cesar Manara Planned maintenance scheduled April 17/18, 2019 at 00:00UTC (8:00pm US/Eastern) The Ask Question Wizard is Live! Data science time! April 2019 and salary with experienceEnforcing custom enumeration in AWS LEX for slot valuesHow to give response based on user response in Amazon Lex?Intercepting AWS Lambda Response to a AWS Lex QueryLex chat bot error: Reached second execution of fulfillment lambda on the same utteranceamazon lex showing invalid responseLambda response send back to Lex slot?Response card in Amazon lexAmazon Lex - Lambda response return HTML to botHow can I solve 424 (Failed Dependency) (python) obtained from Amazon lex?

Алба-Юлія

Захаров Федір Захарович