Skip to content

Instantly share code, notes, and snippets.

@merklol
Last active December 8, 2020 15:29
Show Gist options
  • Save merklol/ba967e25e5c4057abd192670f0cd92f1 to your computer and use it in GitHub Desktop.
Save merklol/ba967e25e5c4057abd192670f0cd92f1 to your computer and use it in GitHub Desktop.
Singly linked list in Functional Programming Style
sealed class FList<out A> {
object Empty : FList<Nothing>()
data class Node<out A>(val head: A, val tail: FList<A>) : FList<A>() {
override fun toString() =
foldLeft("[", { acc: String, initial: A -> "$acc$initial, " }).dropLast(2) + "]"
}
companion object {
fun <A> of(element: A): FList<A> = Node(element, Empty)
fun <A> of(): FList<A> = Empty
fun <A> of(vararg elements: A): FList<A> = when {
elements.isEmpty() -> Empty
else -> Node(elements[0], of(*elements.sliceArray(1 until elements.size)))
}
fun <A> empty(): FList<A> = Empty
}
}
val <A> FList<A>.tail get() = when(this) {
is FList.Empty -> throw IllegalStateException("Empty FList cannot contains a tail.")
is FList.Node -> this.tail
}
val <A> FList<A>.head get() = when(this) {
is FList.Empty -> throw IllegalStateException("Empty FList cannot contains a head.")
is FList.Node -> this.head
}
val <A> FList<A>.indices: FList<Int> get() {
fun loop(fl: FList<Int>, n: Int): FList<Int> = when(fl) {
is FList.Empty -> FList.Empty
is FList.Node -> when(n) { size() -> FList.empty(); else -> FList.Node(n, loop(fl, n + 1))
}
}
return loop(FList.of(0), 0)
}
operator fun <A> FList<A>.plus(element: A): FList<A> =
foldLeft(FList.of(element), { acc: FList<A>, initial: A -> FList.Node(initial, acc) })
operator fun <A> FList<A>.plus(elements: FList<A>): FList<A> =
foldLeft(elements, {acc: FList<A>, initial: A -> FList.Node(initial, acc) })
tailrec fun <A, B> FList<A>.foldLeft(initial: B, operation: (B, A) -> B): B = when(this) {
is FList.Empty -> initial
is FList.Node -> tail.foldLeft(operation(initial, head), operation)
}
fun <A, B> FList<A>.foldRight(initial: B, operation: (A, B) -> B): B =
foldLeft({ b: B -> b }, { g: (B) -> B, a -> { b: B -> g(operation(a, b)) } })(initial)
fun<A> FList<FList<A>>.concat() = foldLeft(FList.empty(), { a: FList<A>, b: FList<A> -> a + b })
fun<A, B> FList<A>.map(transform: (A) -> B): FList<B> =
foldRight(FList.empty(), { a: A, b: FList<B> -> FList.Node(transform(a), b) })
fun<A, B> FList<A>.flatMap(transform: (A) -> FList<B>): FList<B> =
foldRight(FList.empty(), { a: A, b: FList<B> ->
transform(a).foldRight(b, { b1: B, b2: FList<B> -> FList.Node(b1, b2) }) })
fun<A> FList<A>.filter(predicate: (A) -> Boolean): FList<A> =
flatMap { a: A -> when (predicate(a)) { true -> FList.of(a); else -> FList.empty() } }
fun <A> FList<A>.reverse() = foldLeft(FList.empty(), { acc: FList<A>, a: A -> FList.Node(a, acc) })
fun<A> FList<A>.size(): Int = foldLeft(0, { acc, _ -> acc + 1 })
fun FList<Int>.sum(): Int = foldLeft(0, { a: Int, b: Int -> a + b })
fun FList<Double>.sum(): Double = foldLeft(0.0, { a: Double, b: Double -> a + b })
fun FList<Float>.sum(): Float = foldLeft(0f, { a: Float, b: Float -> a + b })
fun FList<Int>.product(): Int = foldLeft(1, { a: Int, b: Int -> a * b })
fun FList<Double>.product(): Double = foldLeft(1.0, { a: Double, b: Double -> a * b })
fun FList<Float>.product(): Float = foldLeft(1f, { a: Float, b: Float -> a * b })
fun FList<Int>.average() = sum() / size()
fun FList<Double>.average() = sum() / size()
fun FList<Float>.average() = sum() / size()
fun <A: Comparable<A>> FList<A>.max(): A = foldLeft(head, { a, b -> if( a > b) a else b })
fun <A: Comparable<A>> FList<A>.min(): A = foldLeft(head, { a, b -> if( a < b) a else b })
fun <A, B, C> FList<A>.zipWith(fl: FList<B>, f: (A, B) -> C): FList<C> = when (this) {
is FList.Empty -> FList.Empty
is FList.Node -> when (fl) {
is FList.Empty -> FList.Empty
is FList.Node -> FList.Node(f(head, fl.head), tail.zipWith(fl.tail, f))
}
}
fun <A> FList<A>.zipWithIndex(): FList<Pair<Int, A>> = zipWith(indices) { a, b -> b to a }
fun <A> FList<A>.dropLast(): FList<A> =
zipWithIndex().filter { (i, _) -> i + 1 != size() }.flatMap { (_, a) -> FList.of(a) }
fun <A> FList<A>.dropFirst(): FList<A> =
zipWithIndex().filter { (i, _) -> i != 0 }.flatMap { (_, a) -> FList.of(a) }
fun <A> FList<A>.drop(n: Int): FList<A> =
zipWithIndex().filter { (i, _) -> i + 1 != n }.flatMap { (_, a) -> FList.of(a) }
fun <A> FList<A>.dropWhile(predicate: (A) -> Boolean): FList<A> = filter(predicate)
fun <A> FList<A>.startWith(vararg elements: A): Boolean {
tailrec fun compare(fl: FList<A>, fl2: FList<A>): Boolean = when(fl) {
is FList.Empty -> fl2 == FList.Empty
is FList.Node -> when(fl2) {
is FList.Empty -> true
is FList.Node -> when (fl.head) { fl2.head -> compare(fl.tail, fl2.tail); else -> false }
}
}
return compare(this, FList.of(*elements))
}
fun <A> FList<A>.hasSubSequence(vararg elements: A): Boolean = when(this) {
is FList.Empty -> false
is FList.Node -> if(this.startWith(*elements)) true else hasSubSequence(*elements)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment