Implement basic list operations.
In functional languages list operations like length
, map
, and
reduce
are very common. Implement a series of basic list operations,
without using the existing functions of the same name.
The tests for this exercise require you to use extensions, a mechanism for adding new functionality to an existing class whose source you do not directly control without having to subclass it. To learn more about Kotlin's implementations of extensions, check out the official documentation.
The customFoldLeft
and customFoldRight
methods are "fold" functions, which is a concept well-known in the functional programming world, but less so in the object-oriented one. If you'd like more background information, check out this fold page.
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
import org.junit.Ignore
import org.junit.Test
import kotlin.test.assertEquals
class ListExtensionsTest {
@Test
fun testAppendingEmptyLists() {
assertEquals(
emptyList(),
emptyList<Int>().customAppend(emptyList()))
}
@Ignore
@Test
fun testAppendingNonEmptyListOnEmptyList() {
assertEquals(
listOf('1', '2', '3', '4'),
emptyList<Char>().customAppend(listOf('1', '2', '3', '4')))
}
@Ignore
@Test
fun testAppendingNonEmptyListOnNonEmptyList() {
assertEquals(
listOf("1", "2", "2", "3", "4", "5"),
listOf("1", "2").customAppend(listOf("2", "3", "4", "5")))
}
@Ignore
@Test
fun testConcatOnEmptyListOfLists() {
assertEquals(
emptyList(),
emptyList<List<Int>>().customConcat())
}
@Ignore
@Test
fun testConcatOnNonEmptyListOfLists() {
assertEquals(
listOf('1', '2', '3', '4', '5', '6'),
listOf(listOf('1', '2'), listOf('3'), emptyList(), listOf('4', '5', '6')).customConcat())
}
@Ignore
@Test
fun testFilteringEmptyList() {
assertEquals(
emptyList(),
emptyList<Int>().customFilter { it % 2 == 1 })
}
@Ignore
@Test
fun testFilteringNonEmptyList() {
assertEquals(
listOf(1, 3, 5),
listOf(1, 2, 3, 5).customFilter { it % 2 == 1 })
}
@Ignore
@Test
fun testSizeOfEmptyList() {
assertEquals(0, emptyList<Int>().customSize)
}
@Ignore
@Test
fun testSizeOfNonEmptyList() {
assertEquals(4, listOf("one", "two", "three", "four").customSize)
}
@Ignore
@Test
fun testTransformingEmptyList() {
assertEquals(
emptyList(),
emptyList<Int>().customMap { it -> it + 1 })
}
@Ignore
@Test
fun testTransformingNonEmptyList() {
assertEquals(
listOf(2, 4, 6, 8),
listOf(1, 3, 5, 7).customMap { it -> it + 1 })
}
@Ignore
@Test
fun testFoldLeftOnEmptyList() {
assertEquals(
2.0,
emptyList<Int>().customFoldLeft(2.0, Double::times))
}
@Ignore
@Test
fun testFoldLeftWithDirectionIndependentOperationOnNonEmptyList() {
assertEquals(
15,
listOf(1, 2, 3, 4).customFoldLeft(5, Int::plus))
}
@Ignore
@Test
fun testFoldLeftWithDirectionDependentOperationOnNonEmptyList() {
assertEquals(
0,
listOf(2, 5).customFoldLeft(5, Int::div))
}
@Ignore
@Test
fun testFoldRightOnEmptyList() {
assertEquals(
2.0,
emptyList<Double>().customFoldRight(2.0, Double::times))
}
@Ignore
@Test
fun testFoldRightWithDirectionIndependentOperationOnNonEmptyList() {
assertEquals(
15,
listOf(1, 2, 3, 4).customFoldRight(5, Int::plus))
}
@Ignore
@Test
fun testFoldRightWithDirectionDependentOperationOnNonEmptyList() {
assertEquals(
2,
listOf(2, 5).customFoldRight(5, Int::div))
}
@Ignore
@Test
fun testReversingEmptyList() {
assertEquals(
emptyList(),
emptyList<Int>().customReverse())
}
@Ignore
@Test
fun testReversingNonEmptyList() {
assertEquals(
listOf('7', '5', '3', '1'),
listOf('1', '3', '5', '7').customReverse())
}
}
fun <T> List<T>.customAppend(other: List<T>): List<T> {
val newList = this.toMutableList()
other.forEach { newList.add(it) }
return newList
}
fun <T> List<List<T>>.customConcat(): List<T> {
val newList = mutableListOf<T>()
this.forEach { it.forEach { newList.add(it) } }
return newList
}
fun <T> List<T>.customFilter(function: (t: T) -> Boolean): List<T> {
val newList = mutableListOf<T>()
this.forEach { if (function(it)) newList.add(it) }
return newList
}
val <T> List<T>.customSize: Int
get() {
var sizeCounter = 0
this.forEach { sizeCounter++ }
return sizeCounter
}
fun <T> List<T>.customMap(function: (t: T) -> Any?): List<Any?> {
val newList = mutableListOf<Any?>()
this.forEach { newList.add(function(it)) }
return newList
}
fun <T> List<T>.customFoldLeft(parameter: T, function: (t1: T, t2: T) -> T): T {
var result = parameter
this.forEach { result = function(result, it) }
return result
}
fun <T> List<T>.customFoldRight(parameter: T, function: (t1: T, t2: T) -> T): T {
var result = parameter
(this.size - 1 downTo 0).forEach { result = function(this[it], result) }
return result
}
fun <T> List<T>.customReverse(): List<T> {
val newList = mutableListOf<T>()
(this.size - 1 downTo 0).forEach { newList.add(this[it]) }
return newList
}
A huge amount can be learned from reading other people’s code. This is why we wanted to give exercism users the option of making their solutions public.
Here are some questions to help you reflect on this solution and learn the most from it.
Level up your programming skills with 3,450 exercises across 52 languages, and insightful discussion with our volunteer team of welcoming mentors. Exercism is 100% free forever.
Sign up Learn More