Edgar Avuzi
12/24/2024, 2:49 AMfor comprehension in KotlinEdgar Avuzi
12/24/2024, 2:50 AMimport scala.io.Source
object Main
val XMAS = "XMAS"
val directions: List[(Int, Int)] = List(
(0, 1), // Right (horizontal)
(1, 0), // Down (vertical)
(1, 1), // Diagonal down-right
(1, -1), // Diagonal down-left
(0, -1), // Left (reverse horizontal)
(-1, 0), // Up (reverse vertical)
(-1, -1), // Diagonal up-left
(-1, 1) // Diagonal up-right
)
@main def run(): Unit = {
val grid =
Option(Main.getClass.getClassLoader.getResourceAsStream("input4.txt"))
.map(Source.fromInputStream)
.map(_.getLines().toList)
.getOrElse(List.empty)
val count = for {
i <- grid.indices
j <- grid(i).indices
(dx, dy) <- directions
if isMatch(grid, i, j, dx, dy)
} yield 1
println(s"Total occurrences of XMAS: ${count.sum}")
}
def isMatch(grid: List[String], i: Int, j: Int, dx: Int, dy: Int): Boolean = {
val endX = i + (XMAS.length - 1) * dx
val endY = j + (XMAS.length - 1) * dy
(grid.isDefinedAt(endX) && grid.isDefinedAt(endY)) &&
XMAS.indices.forall { k =>
grid(i + k * dx)(j + k * dy) == XMAS(k)
}
}Edgar Avuzi
12/24/2024, 2:51 AMephemient
12/24/2024, 2:51 AMephemient
12/24/2024, 2:54 AMval count = grid.indices
.flatMap { i -> grid[i].indices.map(i::to) }
.flatMap { ij -> directions.map(ij::to) }
.count { (ij, dxdy) -> isMatch(grid, ij.first, ij.second, dxdy.first, dxdy.second) }ephemient
12/24/2024, 2:55 AMfor comprehensions desugar into a chain of flatMap calls somewhat like thatEdgar Avuzi
12/24/2024, 3:16 AMconst val XMAS = "XMAS"
val directions: List<Pair<Int, Int>> = listOf(
Pair(0, 1), // Right (horizontal)
Pair(1, 0), // Down (vertical)
Pair(1, 1), // Diagonal down-right
Pair(1, -1), // Diagonal down-left
Pair(0, -1), // Left (reverse horizontal)
Pair(-1, 0), // Up (reverse vertical)
Pair(-1, -1), // Diagonal up-left
Pair(-1, 1) // Diagonal up-right
)
fun main() {
val grid =
({}.javaClass.classLoader.getResourceAsStream("input4.txt") ?: return)
.reader()
.use { it.readLines() }
val count = grid.indices
.flatMap { i -> grid[i].indices.map(i::to) }
.flatMap { ij -> directions.map(ij::to) }
.count { (ij, dxdy) -> isMatch(grid, ij.first, ij.second, dxdy.first, dxdy.second) }
println("Total occurrences of XMAS: $count")
}
fun isMatch(grid: List<String>, i: Int, j: Int, dx: Int, dy: Int): Boolean {
val endX = i + (XMAS.length - 1) * dx
val endY = j + (XMAS.length - 1) * dy
return (endX in grid.indices && endY in grid[0].indices)
&&
XMAS.indices.all { k ->
grid[i + k * dx][j + k * dy] == XMAS[k]
}
}CLOVIS
12/24/2024, 9:19 AMval count = sequence {
parameterize {
val i by parameterOf(grid.indices)
val ij by parameterOf(grid[i].indices.map(i::to))
yield(directions.map(ij::to))
}
}.count [ (ij, dxdy) -> isMatch(grid, ij.first, ij.second, dxdy.first, dxdy.second) }
That looks fairly close to the Scala example 🤔Edgar Avuzi
12/24/2024, 10:12 AMephemient
12/26/2024, 10:10 AMCLOVIS
12/26/2024, 10:15 AMephemient
12/26/2024, 10:22 AMsuspend, so we have tests that do
@get:Rule
val errorCollector = ErrorCollector()
@Test
fun test() = runTest {
chooseAll(errorCollector::addError) {
coroutineScope {
val foo = choose(...)
val bar = choose(...)
but I have been thinking that longer-term it's probably better to build this on top of the Compose machinery instead, so that we get to have the benefits of skipping (which is more likely what a user would expect)CLOVIS
12/26/2024, 10:32 AMYoussef Shoaib [MOD]
11/06/2025, 10:43 PMvar sum = 0
runChoose {
val i = grid.indices.bind()
val j = grid[i].indices.bind()
val (dx, dy) = directions.bind()
ensure(isMatch(grid, i, j, dx, dy))
sum++
}
and it implements the "skipping" mentioned earlier in this thread. It uses multishot continuations, and so it's able to resume this coroutine multiple times. I even have a PR for Parameterize that uses Kontinuity to make it no longer finicky (e.g. you can do side effects safely in here, while in Parameterize, they get repeated).
It's even better that Scala's for comprehensions, actually. You can extract functions!
context(_: Choose)
fun List[String].someCoord(): Pair<Int, Int> {
val i = this.indices.bind()
return i to this[i].indices.bind()
}
var sum = 0
runChoose {
val (i, j) = grid.someCoord()
val (dx, dy) = directions.bind()
ensure(isMatch(grid, i, j, dx, dy))
sum++
}
You can't do that in Scala! I need to add some docs and helpful examples to the library eventually, but in the meantime, you can have a look around the tests!Edgar Avuzi
11/07/2025, 3:50 AMYoussef Shoaib [MOD]
11/07/2025, 12:03 PM