-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Abhijit Sarkar
committed
Jan 16, 2025
1 parent
8d6d635
commit 8620c78
Showing
14 changed files
with
238 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
package bintree | ||
package mtree | ||
|
||
final case class DList[A](run: List[A] => List[A]): | ||
// O(1) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package mtree | ||
|
||
final case class MTree[+T](value: T, children: Seq[MTree[T]] = Nil): | ||
override def toString: String = | ||
s"M(${value.toString} {${children.map(_.toString).mkString(",")}})" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package mtree | ||
|
||
// P70 (**) Tree construction from a node string. | ||
// | ||
// We suppose that the nodes of a multiway tree contain single characters. | ||
// In the depth-first order sequence of its nodes, a special character ^ | ||
// has been inserted whenever, during the tree traversal, the move is a | ||
// backtrack to the previous level. | ||
// | ||
// By this rule, the tree in the figure opposite is represented as: | ||
// | ||
// afg^^c^bd^e^^^ | ||
// Define the syntax of the string and write a function string2MTree to construct | ||
// an MTree from a String. Make the function an implicit conversion from String. | ||
// Write the reverse function, and make it the toString method of MTree. | ||
// | ||
// scala> MTree('a', List(MTree('f', List(MTree('g'))), MTree('c'), MTree('b', List(MTree('d'), MTree('e'))))).toString | ||
// res0: String = afg^^c^bd^e^^^ | ||
object P70: | ||
def string2MTree(s: String): MTree[Char] = | ||
/* | ||
First recursive dfs call collects the children, | ||
second recursive dfs call collects the siblings. | ||
x=g, xs=^^c^bd^e^^^, ys=^c^bd^e^^^, zs=c^bd^e^^^ | ||
x=e, xs=^^^, ys=^^, zs=^ | ||
x=d, xs=^e^^^, ys=e^^^, zs=^ | ||
x=b, xs=d^e^^^, ys=^, zs= | ||
x=c, xs=^bd^e^^^, ys=bd^e^^^, zs= | ||
x=f, xs=g^^c^bd^e^^^, ys=c^bd^e^^^, zs= | ||
x=a, xs=fg^^c^bd^e^^^, ys=, zs= | ||
*/ | ||
def loop(s: String): (List[MTree[Char]], String) = | ||
s match | ||
case "" => (Nil, "") | ||
case s"^$xs" => (Nil, xs) | ||
case _ => | ||
val (x, xs) = (s.head, s.tail) | ||
val (children, ys) = loop(xs) | ||
val (siblings, zs) = loop(ys) | ||
(MTree(x, children) :: siblings, zs) | ||
|
||
loop(s)._1.head | ||
|
||
def tree2String(t: MTree[Char]): String = | ||
def loop(acc: DList[Char], t: MTree[Char]): DList[Char] = | ||
val xs = t.children.foldLeft(DList.empty[Char])(loop) | ||
val ys = DList.singleton(t.value) ++ xs ++ DList.singleton('^') | ||
acc ++ ys | ||
|
||
loop(DList.empty[Char], t).toList.mkString |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package mtree | ||
|
||
// P70C (*) Count the nodes of a multiway tree. | ||
// Write a method nodeCount which counts the nodes of a given multiway tree. | ||
// | ||
// scala> MTree('a', List(MTree('f'))).nodeCount | ||
// res0: Int = 2 | ||
object P70C: | ||
extension [A](t: MTree[A]) | ||
def nodeCount: Int = 1 + t.children.foldLeft(0)((acc, c) => acc + c.nodeCount) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package mtree | ||
|
||
// P71 (*) Determine the internal path length of a tree. | ||
// We define the internal path length of a multiway tree as the | ||
// total sum of the path lengths from the root to all nodes of the tree. | ||
// By this definition, the tree in the figure of problem P70 has an | ||
// internal path length of 9. Write a method internalPathLength to return that sum. | ||
// | ||
// scala> "afg^^c^bd^e^^^".internalPathLength | ||
// res0: Int = 9 | ||
/* | ||
ANSWER: We observe that the path length is equal to the number of | ||
nodes in a path from the root to a leaf, with a node counted only | ||
once. So, path length of abd = 3, but abd + abe is 4, not 6. | ||
The catch is to pass the _same_ accoumulated value to all the | ||
children of a node. | ||
*/ | ||
object P71: | ||
private def loop[A](acc: Int, t: MTree[A]): Int = | ||
acc + t.children.map(loop(acc + 1, _)).sum | ||
|
||
extension [A](t: MTree[A]) | ||
def internalPathLength: Int = | ||
loop(0, t) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package mtree | ||
|
||
// P72 (*) Construct the postorder sequence of the tree nodes. | ||
// | ||
// Write a method postorder which constructs the postorder sequence | ||
// of the nodes of a multiway tree. The result should be a List. | ||
// | ||
// scala> "afg^^c^bd^e^^^".postorder | ||
// res0: List[Char] = List(g, f, c, d, e, b, a) | ||
object P72: | ||
private def loop[A](acc: List[A], t: MTree[A]): List[A] = | ||
t.children.foldRight(t.value :: acc)((tree, xs) => loop(xs, tree)) | ||
|
||
extension [A](t: MTree[A]) | ||
def postorder: List[A] = | ||
loop(Nil, t) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
package mtree | ||
|
||
// P73 (**) Lisp-like tree representation. | ||
// There is a particular notation for multiway trees in Lisp. | ||
// Lisp is a prominent functional programming language. | ||
// In Lisp almost everything is a list. | ||
// | ||
// Our example tree would be represented in Lisp as (a (f g) c (b d e)). | ||
// The following pictures give some more examples. | ||
// | ||
// Note that in the "lispy" notation a node with successors (children) in | ||
// the tree is always the first element in a list, followed by its children. | ||
// The "lispy" representation of a multiway tree is a sequence of atoms and | ||
// parentheses '(' and ')', with the atoms separated by spaces. | ||
// We can represent this syntax as a Scala String. | ||
// Write a method lispyTree which constructs a "lispy string" from an MTree. | ||
// | ||
// scala> MTree("a", List(MTree("b", List(MTree("c"))))).lispyTree | ||
// res0: String = (a (b c)) | ||
// | ||
// As a second, even more interesting, exercise try to write a method that | ||
// takes a "lispy" string and turns it into a multiway tree. | ||
object P73: | ||
private def loop(s: String, i: Int): (MTree[Char], Int) = | ||
if i >= s.length() then throw StringIndexOutOfBoundsException(s"String index out of range: $i") | ||
else if s(i).isLetter then (MTree(s(i)), i + 1) | ||
else if s(i) == '(' then | ||
val x = s(i + 1) | ||
val xs = IndexedSeq.unfold(i + 2)(j => | ||
Option.when(j < s.length() && s(j) != ')') { | ||
val x = loop(s, j) | ||
((x, x._2)) | ||
} | ||
) | ||
(MTree(x, xs.map(_._1)), xs.last._2 + 1) | ||
else loop(s, i + 1) | ||
|
||
def lispyString2Tree(s: String): MTree[Char] = | ||
loop(s, 0)._1 | ||
|
||
extension [A](t: MTree[Char]) | ||
def lispyTree: String = | ||
def loop(acc: DList[Char], t: MTree[Char]): DList[Char] = | ||
if t.children.isEmpty then | ||
acc | ||
++ DList.singleton(' ') | ||
++ DList.singleton(t.value) | ||
else | ||
val xs = t.children.foldLeft(DList.empty[Char])(loop) | ||
acc | ||
++ DList.singleton(' ') | ||
++ DList.singleton('(') | ||
++ DList.singleton(t.value) | ||
++ xs | ||
++ DList.singleton(')') | ||
|
||
loop(DList.empty[Char], t).toList.tail.mkString |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
package mtree | ||
|
||
given Conversion[String, MTree[Char]] = s => P70.string2MTree(s) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package mtree | ||
|
||
import org.scalatest.funspec.AnyFunSpec | ||
import org.scalatest.matchers.should.Matchers.shouldBe | ||
import P70C.nodeCount | ||
|
||
class P70CSpec extends AnyFunSpec: | ||
it("count the nodes of a multiway tree"): | ||
MTree('a', List(MTree('f'))).nodeCount shouldBe 2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package mtree | ||
|
||
import org.scalatest.funspec.AnyFunSpec | ||
import org.scalatest.matchers.should.Matchers.shouldBe | ||
|
||
class P70Spec extends AnyFunSpec: | ||
val tree = MTree( | ||
'a', | ||
List(MTree('f', List(MTree('g'))), MTree('c'), MTree('b', List(MTree('d'), MTree('e')))) | ||
) | ||
it("tree construction from a node string"): | ||
P70.string2MTree("afg^^c^bd^e^^^") shouldBe tree | ||
|
||
it("tree to string"): | ||
P70.tree2String(tree) shouldBe "afg^^c^bd^e^^^" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package mtree | ||
|
||
import org.scalatest.funspec.AnyFunSpec | ||
import org.scalatest.matchers.should.Matchers.shouldBe | ||
import mtree.P71.internalPathLength | ||
import scala.language.implicitConversions | ||
|
||
class P71Spec extends AnyFunSpec: | ||
it("internal path length of a tree"): | ||
"afg^^c^bd^e^^^".internalPathLength shouldBe 9 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package mtree | ||
|
||
import org.scalatest.funspec.AnyFunSpec | ||
import org.scalatest.matchers.should.Matchers.shouldBe | ||
import mtree.P72.postorder | ||
import scala.language.implicitConversions | ||
|
||
class P72Spec extends AnyFunSpec: | ||
it("postorder sequence of the tree nodes"): | ||
"afg^^c^bd^e^^^".postorder shouldBe List('g', 'f', 'c', 'd', 'e', 'b', 'a') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package mtree | ||
|
||
import org.scalatest.funspec.AnyFunSpec | ||
import org.scalatest.matchers.should.Matchers.shouldBe | ||
import mtree.P73.lispyTree | ||
import scala.language.implicitConversions | ||
|
||
class P73Spec extends AnyFunSpec: | ||
it("lisp-like tree representation"): | ||
val data = List( | ||
("a^", "a"), | ||
("ab^^", "(a b)"), | ||
("abc^^^", "(a (b c))"), | ||
("bd^e^^", "(b d e)"), | ||
("afg^^c^bd^e^^^", "(a (f g) c (b d e))") | ||
) | ||
|
||
data.foreach { (s, expected) => | ||
s.lispyTree shouldBe expected | ||
P73.lispyString2Tree(expected) shouldBe P70.string2MTree(s) | ||
} |