Skip to content

Commit

Permalink
Add memoize Functions
Browse files Browse the repository at this point in the history
Add functions that take a function and record the input-output values and cache them. There are options for one, two, or three argument functions, as well as the same with a set of default values for the memoization cache
  • Loading branch information
GeistInDerSH committed Dec 11, 2024
1 parent 077f28b commit 0a230cf
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 13 deletions.
103 changes: 103 additions & 0 deletions src/main/kotlin/com/geistindersh/aoc/helper/caching/Memoize.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package com.geistindersh.aoc.helper.caching

/**
* Generate a function that will keep track of the input and output of the function
* and cache the results.
* Repeated calls with the same arguments will return the same result, and [fn] should
* be free of side effects.
*
* @param fn A function to memoize
* @return A function that takes the same arguments as the memoized function
*/
@Suppress("unused")
fun <A, Z> memoize(fn: (A) -> Z): (A) -> Z {
val memory = mutableMapOf<A, Z>()
return { memory.getOrPut(it) { fn(it) } }
}

/**
* Generate a function that will keep track of the input and output of the function
* and cache the results.
* Repeated calls with the same arguments will return the same result, and [fn] should
* be free of side effects.
*
* @param fn A function to memoize
* @param defaultValues Values to pre-populate the input-output cache with
* @return A function that takes the same arguments as the memoized function
*/
@Suppress("unused")
fun <A, Z> memoize(
fn: (A) -> Z,
defaultValues: Map<A, Z>,
): (A) -> Z {
val memory = defaultValues.toMutableMap()
return { memory.getOrPut(it) { fn(it) } }
}

/**
* Generate a function that will keep track of the input and output of the function
* and cache the results.
* Repeated calls with the same arguments will return the same result, and [fn] should
* be free of side effects.
*
* @param fn A function to memoize
* @return A function that takes the same arguments as the memoized function
*/
@Suppress("unused")
fun <A, B, Z> memoize(fn: (A, B) -> Z): (A, B) -> Z {
val memory = mutableMapOf<Pair<A, B>, Z>()
return { a, b -> memory.getOrPut(a to b) { fn(a, b) } }
}

/**
* Generate a function that will keep track of the input and output of the function
* and cache the results.
* Repeated calls with the same arguments will return the same result, and [fn] should
* be free of side effects.
*
* @param fn A function to memoize
* @param defaultValues Values to pre-populate the input-output cache with
* @return A function that takes the same arguments as the memoized function
*/
@Suppress("unused")
fun <A, B, Z> memoize(
fn: (A, B) -> Z,
defaultValues: Map<Pair<A, B>, Z>,
): (A, B) -> Z {
val memory = defaultValues.toMutableMap()
return { a, b -> memory.getOrPut(a to b) { fn(a, b) } }
}

/**
* Generate a function that will keep track of the input and output of the function
* and cache the results.
* Repeated calls with the same arguments will return the same result, and [fn] should
* be free of side effects.
*
* @param fn A function to memoize
* @return A function that takes the same arguments as the memoized function
*/
@Suppress("unused")
fun <A, B, C, Z> memoize(fn: (A, B, C) -> Z): (A, B, C) -> Z {
val memory = mutableMapOf<Triple<A, B, C>, Z>()
return { a, b, c -> memory.getOrPut(Triple(a, b, c)) { fn(a, b, c) } }
}

/**
* Generate a function that will keep track of the input and output of the function
* and cache the results.
* Repeated calls with the same arguments will return the same result, and [fn] should
* be free of side effects.
* @param fn A function to memoize
* @param defaultValues Values to pre-populate the input-output cache with
*
* @return A function that takes the same arguments as the memoized function
*/
@Suppress("unused")
fun <A, B, C, Z> memoize(
fn: (A, B, C) -> Z,
defaultValues: Map<Triple<A, B, C>, Z>,
): (A, B, C) -> Z {
val memory = defaultValues.toMutableMap()
return { a, b, c -> memory.getOrPut(Triple(a, b, c)) { fn(a, b, c) } }
}
27 changes: 14 additions & 13 deletions src/main/kotlin/com/geistindersh/aoc/year2024/Day11.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.geistindersh.aoc.year2024

import com.geistindersh.aoc.helper.binary.digitCount
import com.geistindersh.aoc.helper.caching.memoize
import com.geistindersh.aoc.helper.files.DataFile
import com.geistindersh.aoc.helper.files.fileToString
import com.geistindersh.aoc.helper.report
Expand All @@ -10,22 +11,22 @@ class Day11(
dataFile: DataFile,
) {
private val stones = fileToString(2024, 11, dataFile).split(" ").associate { it.toLong() to 1L }
private val memory = mutableMapOf(0L to listOf(1L))
private val memoizedCore = memoize(::core, mapOf(0L to listOf(1L)))

private fun core(value: Long): List<Long> {
val digits = value.digitCount()
return if (digits % 2 == 0) {
val div = 10.0.pow(digits / 2.0).toLong()
listOf(value.floorDiv(div), value % div)
} else {
listOf(value * 2024)
}
}

private fun Map<Long, Long>.update() =
this
.flatMap { (k, v) ->
memory
.computeIfAbsent(k) {
val digits = k.digitCount()
if (digits % 2 == 0) {
val div = 10.0.pow(digits / 2.0).toLong()
listOf(k.floorDiv(div), k % div)
} else {
listOf(k * 2024)
}
}.map { it to v }
}.groupingBy { it.first }
.flatMap { (k, v) -> memoizedCore(k).map { it to v } }
.groupingBy { it.first }
.fold(0L) { acc, element -> acc + element.second }

private fun Map<Long, Long>.blink(times: Int) =
Expand Down

0 comments on commit 0a230cf

Please sign in to comment.