🎉 Exercism Research is now launched. Help Exercism, help science and have some fun at research.exercism.io 🎉
Avatar of marvinbelfort

marvinbelfort's solution

to List Ops in the Kotlin Track

Published at Jul 13 2018 · 0 comments
Instructions
Test suite
Solution

Note:

This solution was written on an old version of Exercism. The tests below might not correspond to the solution code, and the exercise may have changed since this code was written.

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 existing functions.

Hints

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.

Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

ListExtensionsTest.kt

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> {
    return mutableListOf<T>().also {
       for(item in this) it.add(item)
       for(item in other) it.add(item)
    }
}

fun <T> List<T>.customConcat(): List<Any?> {
    return mutableListOf<T>().also {
        for (list in this){
           when(list) {
               is List<*> -> for (item in list) it.add(item as T)
               else -> it.add(list)
           }
        }
    }
}

fun <T> List<T>.customFilter(filter: (T) -> Boolean): List<T> {
    return mutableListOf<T>().also {
        for(item in this) if (filter(item)) it.add(item)
    }
}

val <T> List<T>.customSize: Int
    get() {
       var size = 0
       for (item in this) size++
       return size
    }

fun <T, R> List<T>.customMap(operation: (T) -> R): List<R> {
    return mutableListOf<R>().also {
        for(item in this) it.add(operation(item))
    }
}

fun <T, R> List<T>.customFoldLeft(initial: R, operation: (acc: R, T) -> R): R  {
    var accumulator = initial
    for (item in this)
        accumulator = operation( accumulator, item)
    return accumulator
}

fun <T, R> List<T>.customFoldRight(initial: R, operation: (acc: T, R) -> R): R  {
    var accumulator = initial
    for (item in this.customReverse())
        accumulator = operation(item, accumulator)
    return accumulator
}


fun <T> List<T>.customReverse(): List<T> {
    return mutableListOf<T>().also {
        (0..lastIndex).toList().forEach { i -> it.add(this[lastIndex - i]) }
    }
}

Community comments

Find this solution interesting? Ask the author a question to learn more.

What can you learn from this solution?

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.

  • What compromises have been made?
  • Are there new concepts here that you could read more about to improve your understanding?