You can do it this way:
fun <T> nonexhaustivePermutations(length: Int, components: List<T>): List<List<T>> =
if (components.isEmpty() || length <= 0) listOf(emptyList())
else nonexhaustivePermutations(length - 1, components)
.flatMap { sub -> components.map { sub + it } }
Now I'll explain how you can fix your solution. First of all, I'll reformat it:
fun <T> nonexhaustivePermutations(length: Int, components: List<T>): List<List<T>> =
if (components.isEmpty() || length <= 0) listOf(listOf())
else components.map { elm ->
nonexhaustivePermutations(length - 1, components).map { sub -> listOf(elm) + sub }
}.fold(listOf(listOf())) { result, elm -> result + elm }
The first problem is elm -> nonexhaustivePermutations(length - 1, components)
. Here you call nonexhaustivePermutations
with the same arguments for each elm
on each recursion step. So I suggest swapping components.map
with nonexhaustivePermutations(length - 1, components).map
:
fun <T> nonexhaustivePermutations(length: Int, components: List<T>): List<List<T>> =
if (components.isEmpty() || length <= 0) listOf(listOf())
else nonexhaustivePermutations(length - 1, components).map { sub ->
components.map { elm -> listOf(elm) + sub }
}.fold(listOf(listOf())) { result, elm -> result + elm }
But in addition to significantly improving performance, this swapping also reorders the returned elements. It can partly be fixed by replacing listOf(elm) + sub
with sub + elm
:
fun <T> nonexhaustivePermutations(length: Int, components: List<T>): List<List<T>> =
if (components.isEmpty() || length <= 0) listOf(listOf())
else nonexhaustivePermutations(length - 1, components).map { sub ->
components.map { elm -> sub + elm }
}.fold(listOf(listOf())) { result, elm -> result + elm }
If you run this method, you will see that it returns permutations in order of length increasing. So short permutations are added first. To be more precise, they are added at the time the list is created. So to get rid of them, fold(listOf(listOf()))
must be replaced with fold(emptyList())
:
fun <T> nonexhaustivePermutations(length: Int, components: List<T>): List<List<T>> =
if (components.isEmpty() || length <= 0) listOf(listOf())
else nonexhaustivePermutations(length - 1, components).map { sub ->
components.map { elm -> sub + elm }
}.fold(emptyList()) { result, elm -> result + elm }
This version works properly. But the only thing the last line does is list flatting. And flatting can be combined with mapping by replacing map
with flatMap
:
fun <T> nonexhaustivePermutations(length: Int, components: List<T>): List<List<T>> =
if (components.isEmpty() || length <= 0) listOf(listOf())
else nonexhaustivePermutations(length - 1, components).flatMap { sub ->
components.map { elm -> sub + elm }
}