15

According to scaladoc, sliding() returns... "An iterator producing iterable collections of size size, except the last and the only element will be truncated if there are fewer elements than size."

For me, intuitivelly, sliding(n) would return a sliding window of n elements if available. With the current implementation, I need to perform an extra check to make sure I don't get a list of 1 or 2 elements.

scala> val xs = List(1, 2)
xs: List[Int] = List(1, 2)

scala> xs.sliding(3).toList
res2: List[List[Int]] = List(List(1, 2))

I expected here an empty list instead. Why is sliding() implemented this way instead?

Adrian
  • 3,762
  • 2
  • 31
  • 40

3 Answers3

12

It was a mistake, but wasn't fixed as of 2.9. Everyone occasionally makes design errors, and once one gets into the library it's a nontrivial task to remove it.

Workaround: add a filter.

xs.sliding(3).filter(_.size==3).toList
Rex Kerr
  • 166,841
  • 26
  • 322
  • 407
  • Shouldn't it be fixed sooner rather than later? – Adrian Oct 31 '11 at 20:19
  • 1
    "Fixing" an API is always a tricky thing: by changing the behavior, you would break any existing code that relies on the partial windows being returned. – Emil Sit Oct 31 '11 at 20:29
7

You can "work around" this by using the GroupedIterator#withPartial modifier.

scala> val xs = List(1, 2)
xs: List[Int] = List(1, 2)

scala> xs.iterator.sliding(3).withPartial(false).toList
res7: List[Seq[Int]] = List()

(I don't know why you need to say xs.iterator but xs.sliding(3).withPartial(false) does not work because you get an Iterator instead of a GroupedIterator.

Emil Sit
  • 22,894
  • 7
  • 53
  • 75
  • `xs.sliding` is defined in the `IterableLike` trait, whereas `xs.iterator.sliding` is defined in the `Iterator` trait. The sliding method on IterableLike captures the collection type as `Repr` as a container for the groups. So if you call sliding on a List you get an iterator of Lists or on an Array an Iterator of Arrays. This notion of capturing the `Repr` type would need to be pushed down into the `GroupedIterator` to keep the same behaviour but also allow you to call the withPartial etc methods. Not sure what the pros and cons of this would be. – Silas Davis Dec 30 '15 at 17:40
1

EDIT:

Check Rex's answer (which is the correct one). I'm leaving this just because (as Rex said on the comments) it was the original (wrong) idea behind that design decision.


I don't know why you would expect an empty list there, returning the full list seems like the best result, consider this example:

def slidingWindowsThing(windows : List[List[Int]]) { // do your thing

For this methods you probably want all these calls to work:

slidingWindowsThing((1 to 10).sliding(3))

slidingWindowsThing((1 to 3).sliding(3))

slidingWindowsThing((1 to 1).sliding(3))

This is why the method defaults to a list of size list.length instead of Nil (empty list).

Community
  • 1
  • 1
Pablo Fernandez
  • 103,170
  • 56
  • 192
  • 232
  • This was the reasoning, but it's faulty reasoning: `slidingWindowsThing((1 to 0).sliding(3))` also probably should work, which means `slidingWindowThing` has to handle the empty case. And if it can handle the empty case, it's fine for `sliding` to return an empty iterator (of type `Iterator[List[Int]]` in this case). And then the length would be guaranteed. – Rex Kerr Oct 31 '11 at 20:10
  • Well I'm actually glad I had the same reasoning (though faulty) as the library designers at that time :) – Pablo Fernandez Oct 31 '11 at 20:12
  • @Rex Also... if returning `Nil`, how would you know that you've parsed every element? Some might slip because you're doing `sliding(3)` and the list actually had 2 remaining elements, right? – Pablo Fernandez Oct 31 '11 at 20:13
  • Indeed. But if you are sliding them, you probably can't interpret only 2 out of 3. After all, if you have a 4-element list and use sliding(3), the first and last element only are used once, while the inner ones are each used twice. It's not instantly obvious what the right answer is...it's only obvious after lots of people use the library and start scratching their heads over unexpected behavior. – Rex Kerr Oct 31 '11 at 20:35