1

I've this method:

def isSorted[A](as: Array[A], ordered: (A, A) => Boolean): Boolean = {
    val sorted = for (it <- as.sliding(2))
      yield {
        if (it.length == 2) ordered.apply(it(0), it(1))
        else true
      }

    sorted.find(!_).isEmpty
}

What I'd like to do is use foldLeftand apply the binary operator. However, foldLeft requires an initial value and I don't know what initial value I can provide without knowing the real type of A.

Abhijit Sarkar
  • 21,927
  • 20
  • 110
  • 219
  • Take a look at this: http://stackoverflow.com/a/7852918/1153435 – Eduardo Jan 23 '16 at 06:59
  • @Eduardo already did. There's no consensus in that thread what to do for breaking. Some say `break` is the best thing in Scala since sliced bread, others think it's evil as it throws an exception under the hood. – Abhijit Sarkar Jan 23 '16 at 07:58
  • Have you tried `reduceLeft`? – Jus12 Jan 23 '16 at 08:55
  • @Jus12 yes and it doesn't work. The signatures don't match. `reduceLeft[B >: A](op: (B, T) => B): B`, in my case the binary op is `(A, A) => Boolean` – Abhijit Sarkar Jan 23 '16 at 08:59
  • Yeah, I think the only way to get that signature right is to go with something like `aggregate(true)((b,x) => b && ordered(x(0),x(1)), (_ && _))`. – jwvh Jan 23 '16 at 10:35

3 Answers3

2

I think what you're doing can be simplified.

def isSorted[A](as: Array[A], ordered: (A, A) => Boolean): Boolean = {
  if (as.size < 2)
    true
  else
    as.sliding(2).find(x => !ordered(x(0),x(1))).isEmpty
}

isSorted: [A](as: Array[A], ordered: (A, A) => Boolean)Boolean

scala> isSorted( Array(2), {(a:Int,b:Int) => a < b} )
res42: Boolean = true

scala> isSorted( Array(2,4), {(a:Int,b:Int) => a < b} )
res43: Boolean = true

scala> isSorted( Array(2,4,5), {(a:Int,b:Int) => a < b} )
res44: Boolean = true

scala> isSorted( Array(2,14,5), {(a:Int,b:Int) => a < b} )
res45: Boolean = false

Or, perhaps a little more concisely (but not necessarily easier to understand):

def isSorted[A](as: Array[A], ordered: (A, A) => Boolean): Boolean = {
  if (as.size < 2)
    true
  else
    !as.sliding(2).exists(x => ordered(x(1),x(0)))
}

UPDATE

OK, I think I've got the concise prize nailed.

def isSorted[A](as: Array[A], ordered: (A, A) => Boolean): Boolean =
  as.isEmpty || as.init.corresponds(as.tail)(ordered)
jwvh
  • 50,871
  • 7
  • 38
  • 64
  • Speaking of conciseness and laziness, and borrowing your idea, I could do - `(as.isEmpty) || as.view.sliding(2).find(it => !ordered(it(0), it(1))).isEmpty` – Abhijit Sarkar Jan 23 '16 at 09:32
  • Yes indeed, although the `isEmpty` test will miss the single-element array. – jwvh Jan 23 '16 at 09:40
  • Ugh yes, I meant to use `as.size < 2` but somehow ended up with `isEmpty`. – Abhijit Sarkar Jan 23 '16 at 10:14
  • You know, after looking it over, I'm not sure that `view` is offering much benefit. `sliding()` produces an iterator. Is iterating over a view really better/faster? – jwvh Jan 23 '16 at 10:19
  • No it's not, and using a view as in my comment actually runs into the bug [SI-6709](https://issues.scala-lang.org/browse/SI-6709). Iterator is fine, or I need to use `as.toIndexedSeq.view.sliding`. Good catch. – Abhijit Sarkar Jan 23 '16 at 10:23
  • Does the `find` work lazily? (or does it still iterate though the entire array?) – Jus12 Jan 23 '16 at 11:01
  • Many [collection methods](https://github.com/scala/scala/blob/5adc400f5ece336f3f5ff19691204975d41e652e/src/library/scala/collection/Iterator.scala) that return Boolean or Option use a lazy, or terminate-early, algorithm. – jwvh Jan 23 '16 at 19:25
1

For initial value for foldLeft you could use head of your input array. However foldLeft is not a good choise to check if array is sorted, since you should terminate method when first unsorted element found but foldLeft will traverse whole array

Edit:

I would use the combination of zip with tail and exists:

isSorted(...) = 
   if (as.isEmpty) true
   else !as.zip(as.tail).exists { case (a,b) => !ordered(a,b)}
Nyavro
  • 8,806
  • 2
  • 26
  • 33
1

Adding to the other answers, you probably do not want to iterate through the entire array, but rather terminate the moment you find an unordered pair. So, how about this?

def isSorted[A](as: Array[A], ordered: (A, A) => Boolean): Boolean = {
  var sorted = true
  val ita = as.sliding(2)
  while (sorted && ita.hasNext) {
    val it = ita.next
    sorted = if (it.size > 1) ordered(it(0), it(1)) else true
  }
  sorted
}

val a = Array(1, 3, 2, 4, 5)
val b = Array(1, 2, 3, 4, 5)

isSorted[Int](a, _ < _) // returns false
isSorted[Int](b, _ < _) // returns true
Jus12
  • 17,824
  • 28
  • 99
  • 157