Exercise: Filter Out
So first off, we want to be able to do this:listOf(1, 2, 3, 4, 5) .filterOut { it == 2 }Where the result would be a list of [1, 3, 4, 5] where 2 was filtered out, because it matched the predicate in the filterOut method. To do this, we can do the following:
public inline funNote that we we were able to mark this function as inline, so in essense, when the code compiles, it ends up being equivalent to if you had written:Iterable .filterOut(predicate: (T) -> Boolean) = filter { !predicate(it) }
listOf(1, 2, 3, 4, 5) .filter { !(it == 2) }Though that's actually a lie, because the filter method itself is inline, and as such when the code compiles, it's more like this:
val source = listOf(1, 2, 3, 4, 5) val destination = ArrayList<Int>() for (element in source) if (!(element == 2)) destination.add(element)So you get the speed and efficiency benefits as if you had actually written the code like this, while still getting the niceness how how the code was written above.
But we're not done with this example just yet, because as we learned in chapter 5, you can also do the same operations with a sequence. A sequence will simply try to optimize all the operations being performed to a collection, and our solution wouldn't be complete without having a filter out method for Sequence. So we ultimately want to be able to do the following:
listOf(1, 2, 3, 4, 5) .asSequence() .filterOut { it == 2 } .toList()To do this, we'll want to write the following code:
public funIt's nearly identical to the filter out method that we wrote for the Iterable, but you'll notice that we didn't use inline here. That's because we can't inline this function, because the filter function on the Sequence is not inlined. If you try to put inline on this function, you will get a compile time error. The reason why the filter function is not inline on the sequence is so that the sequence has the flexibility to optimize the order in which different calls are made in order to minimize the amount of work that needs to be done when processing large data sets.Sequence .filterOut(predicate: (T) -> Boolean) = filter { !predicate(it) }
Now if you really had your heart set on inlining this function, you could do the following:
public inline funWhich essentially tells the compiler that the lambda being passed down into the filter method shouldn't be inlined. Now while this can be useful in a number of situations, in this particular case it really isn't all that useful, because it won't be able to eliminate any of the previously mentioned overhead. After compilation, it's essentially as if you had written the following code:Sequence .filterOut(noinline predicate: (T) -> Boolean) = filter { !predicate(it) }
listOf(1, 2, 3, 4, 5) .asSequence() .filter { !(it == 2) } .toList()And it can't optimize any further past that because the filter function is not inlined.
And so with that, the final solution is this:
public inline funIterable .filterOut(predicate: (T) -> Boolean) = filter { !predicate(it) } public fun Sequence .filterOut(predicate: (T) -> Boolean) = filter { !predicate(it) }