I wrote some simple tree visitor functions but found it a pain to reuse the same functions when searching for a value because there is no way to break out of nested loops using lambda. Then decided to use any return value other than Unit
to signal a loop break.
This propagates up through nested calls and makes for a very flexible way to break out early and return any value, including null
. With Unit
signalling no early termination.
Here is a very basic class for demonstration:
import java.util.*
interface Node {
val value: Any
val children: Iterable<Node>
val parent: Node
fun unlink()
fun accept(visitor: (Node) -> Any?): Any? {
var result = visitor(this);
if (result != Unit) return result
for (child in children) {
result = child.accept(visitor)
if (result != Unit) return result
}
return Unit
}
fun visitOf(value: Any?, visitor: (Node) -> Any? = { it }): Any? {
return accept {
if (it.value == value) visitor(it)
else Unit
}
}
fun removeAllOf(value: Any?): Any? {
return visitOf(value) {
it.unlink()
Unit
}
}
fun removeFirstOf(value: Any?): Any? {
return visitOf(value) {
it.unlink()
it
}
}
fun findFirstOf(value: Any?): Any? {
return visitOf(value) {
it
}
}
fun listAllOf(value: Any?): List<Node> {
val list = ArrayList<Node>()
visitOf(value) {
list.add(it)
Unit
}
return list
}
}
Very compact and the only thing to look out for is implicit return that will abort and return that value.
forEach
is limited because it cannot break out early. This is too convenient not to use. I will add a forAll
to my iterables that will treat a non-Unit return from the lambda as an early loop break.