木構造の再帰を使った巡回 という エントリーのコードの改良版です。
なぜか最近たびたび木構造を扱うことがあったので、その辺を整理を含めた覚え書きです。
$ kotlin -version
Kotlin version 1.9.22-release-704 (JRE 17.0.10+7-Ubuntu-122.04.1)
以前のエントリーでは、木を次のように表現していました。
data class Node(val id: String, val children: List<Node> = listOf())
別にこれでも機能するのですが、今回はこのように表現することにします。
typealias ID = String
sealed class Tree(open val id: ID) {
data class Leaf(override val id: ID): Tree(id)
data class Node(override val id: ID, val children: List<Tree>): Tree(id)
}
このように 木を sealed クラスで表現した方がこのクラスを使う側のコードが書きやすい気がする。
テストに使う木構造を定義。
val rootTree =
Tree.Node("0", listOf(
Tree.Node("01", listOf(
Tree.Leaf("01-01"),
Tree.Leaf("01-02"),
Tree.Leaf("01-03"))),
Tree.Node("02", listOf(
Tree.Leaf("02-01"),
Tree.Leaf("02-02"),
Tree.Leaf("02-03"))),
Tree.Node("03", listOf(
Tree.Leaf("03-01"),
Tree.Leaf("03-02"),
Tree.Leaf("03-03")))))
Tree を List<ID> にする。(つまりフラットにする)
typealias Converter: (Tree)->List<ID>
肝心の実装:
val depthFirstSearch: Converter = { tree->
fun f(t: Tree): List<String> {
return when( t ) {
is Tree.Leaf -> { listOf(t.id) }
is Tree.Node -> {
t.children.fold(listOf(t.id), { acc, childT-> acc + f(childT) })
//listOf(t.id) + t.children.map { childT-> f(childT) }.flatten()
}
}
}
f(tree)
}
実行して確かめる:
val idList: List<ID> = depthFirstSearch(rootTree)
idList.forEach {
val indent = 0.until(it.id.length).map { " " }.joinToString("")
println("${indent}- ${it.id}")
}
結果が分かりやすいようにインデントを計算しています。 id の文字列の長さから深さをきめるという適当なコード。
$ kotlin main.kts
- 0
- 01
- 01-01
- 01-02
- 01-03
- 02
- 02-01
- 02-02
- 02-03
- 03
- 03-01
- 03-02
- 03-03
できた。
コード全体を掲載。
main.kts
typealias ID = String
typealias Converter = (Tree)->List<ID>
sealed class Tree(open val id: ID) {
data class Leaf(override val id: ID): Tree(id)
data class Node(override val id: ID, val children: List<Tree>): Tree(id)
}
val depthFirstSearch: Converter = { tree->
fun f(t: Tree): List<String> {
return when( t ) {
is Tree.Leaf -> { listOf(t.id) }
is Tree.Node -> {
t.children.fold(listOf(t.id), { acc, childT-> acc + f(childT) })
//listOf(t.id) + t.children.map { childT-> f(childT) }.flatten()
}
}
}
f(tree)
}
val rootTree =
Tree.Node("0", listOf(
Tree.Node("01", listOf(
Tree.Leaf("01-01"),
Tree.Leaf("01-02"),
Tree.Leaf("01-03"))),
Tree.Node("02", listOf(
Tree.Leaf("02-01"),
Tree.Leaf("02-02"),
Tree.Leaf("02-03"))),
Tree.Node("03", listOf(
Tree.Leaf("03-01"),
Tree.Leaf("03-02"),
Tree.Leaf("03-03")))))
val idList: List<ID> = depthFirstSearch(rootTree)
idList.forEach { id->
val indent = 0.until(id.length).map { " " }.joinToString("")
println("${indent}- ${id}")
}
次のようにして実行。
$ kotlin main.kts
以上です。