diff -r c02929f2647c -r 6e1237691307 progs/lecture4.scala --- a/progs/lecture4.scala Mon Dec 07 01:25:41 2020 +0000 +++ b/progs/lecture4.scala Fri Jan 15 02:40:57 2021 +0000 @@ -249,6 +249,57 @@ // Source.fromFile(name)(encoding) +// Tail recursion +//================ + +@tailrec +def fact(n: BigInt): BigInt = + if (n == 0) 1 else n * fact(n - 1) + + +fact(10) +fact(1000) +fact(100000) + +def factB(n: BigInt): BigInt = + if (n == 0) 1 else n * factB(n - 1) + +def factT(n: BigInt, acc: BigInt): BigInt = + if (n == 0) acc else factT(n - 1, n * acc) + + +factB(1000) + + +factT(10, 1) +println(factT(500000, 1)) + + +// there is a flag for ensuring a function is tail recursive +import scala.annotation.tailrec + +@tailrec +def factT(n: BigInt, acc: BigInt): BigInt = + if (n == 0) acc else factT(n - 1, n * acc) + +factT(100000, 1) + +// for tail-recursive functions the Scala compiler +// generates loop-like code, which does not need +// to allocate stack-space in each recursive +// call; Scala can do this only for tail-recursive +// functions + +// Moral: Whenever a recursive function is resource-critical +// (i.e. works with a large recursion depth), then you need to +// write it in tail-recursive fashion. +// +// Unfortuantely, Scala because of current limitations in +// the JVM is not as clever as other functional languages. It can +// only optimise "self-tail calls". This excludes the cases of +// multiple functions making tail calls to each other. Well, +// nothing is perfect. + @@ -391,66 +442,22 @@ -// Tail recursion -//================ - -@tailrec -def fact(n: BigInt): BigInt = - if (n == 0) 1 else n * fact(n - 1) - - -fact(10) -fact(1000) -fact(100000) - -def factB(n: BigInt): BigInt = - if (n == 0) 1 else n * factB(n - 1) - -def factT(n: BigInt, acc: BigInt): BigInt = - if (n == 0) acc else factT(n - 1, n * acc) - - -factB(1000) - - - - -factT(10, 1) -println(factT(500000, 1)) - - - - - -// there is a flag for ensuring a function is tail recursive +// tail recursive version that searches +// for all Sudoku solutions import scala.annotation.tailrec @tailrec -def factT(n: BigInt, acc: BigInt): BigInt = - if (n == 0) acc else factT(n - 1, n * acc) - -factT(100000, 1) - -// for tail-recursive functions the Scala compiler -// generates loop-like code, which does not need -// to allocate stack-space in each recursive -// call; Scala can do this only for tail-recursive -// functions - -// tail recursive version that searches -// for all Sudoku solutions - -@tailrec -def searchT(games: List[String], sols: List[String]): List[String] = games match { - case Nil => sols - case game::rest => { - if (isDone(game)) searchT(rest, game::sols) - else { - val cs = candidates(game, emptyPosition(game)) - searchT(cs.map(c => update(game, empty(game), c)) ::: rest, sols) - } - } -} +def searchT(games: List[String], sols: List[String]): List[String] = + games match { + case Nil => sols + case game::rest => { + if (isDone(game)) searchT(rest, game::sols) + else { + val cs = candidates(game, emptyPosition(game)) + searchT(cs.map(c => update(game, empty(game), c)) ::: rest, sols) + } + } + } searchT(List(game3), List()).map(pretty) @@ -487,17 +494,6 @@ searchT(List(game3), Nil).map(pretty) search1T(List(game3)).map(pretty) -// Moral: Whenever a recursive function is resource-critical -// (i.e. works with a large recursion depth), then you need to -// write it in tail-recursive fashion. -// -// Unfortuantely, Scala because of current limitations in -// the JVM is not as clever as other functional languages. It can -// only optimise "self-tail calls". This excludes the cases of -// multiple functions making tail calls to each other. Well, -// nothing is perfect. - -