Реализуйте isSorted, который проверяет, отсортирован ли массив [A] в соответствии с заданной функцией сравнения:

def isSorted [A] (как: Array [A], упорядочено: (A, A) => Boolean): Boolean

Вот моя реализация

@tailrec
def isSorted[A](as: Array[A], ordered: (A, A) => Boolean): Boolean = {
  if(as.length==0 || as.length == 1 || as.isEmpty) true
  else if(!ordered(as(0),as(1))) false
  isSorted(as.tail,ordered)
}

У меня исключение:
java.lang.UnsupportedOperationException: empty.tail

Я не совсем понимаю, он должен возвращать истину, когда as пуст.

0
zeffer 21 Сен 2018 в 11:47

2 ответа

Лучший ответ

В Scala последнее выражение, которое вычисляется внутри метода или блока, становится значением для этого метода или блока.

В вашем случае последнее выражение, которое оценивается внутри метода, выглядит следующим образом:

isSorted(as.tail,ordered)

Итак, это возвращаемое значение. < EM> Всегда .

Перед этим выражением в вашем методе есть другое выражение:

if(as.length==0 || as.length == 1 || as.isEmpty) true
else if(!ordered(as(0),as(1))) false

Но:

  • это выражение не имеет побочных эффектов
  • значение этого выражения нигде не сохраняется
  • значение этого выражения не возвращается

Следовательно, это выражение, по сути, не работает, и ваш метод действительно таков:

@tailrec
def isSorted[A](as: Array[A], ordered: (A, A) => Boolean): Boolean = 
  isSorted(as.tail,ordered)

Ваш метод будет просто выполнять рекурсию до тех пор, пока массив не станет пустым, а затем выдаст исключение, потому что вы снова пытаетесь выполнить рекурсию с хвостом пустого массива.

Самое простое возможное исправление - просто сделать это последнее выражение частью более крупного выражения, чтобы ваш метод состоял только из одного выражения:

@tailrec
def isSorted[A](as: Array[A], ordered: (A, A) => Boolean): Boolean = {
  if(as.length==0 || as.length == 1 || as.isEmpty) true
  else if(!ordered(as(0),as(1))) false
  else isSorted(as.tail,ordered)
//↑↑↑↑ This is the only change needed.
}

А теперь давайте перейдем к небольшой экскурсии: стиль Scala!

У вас непоследовательный стиль пробелов. Иногда у вас есть пробелы вокруг операторов, иногда нет, и неясно, когда вы выбираете тот или другой, и каково это значение. Например, здесь:

if(as.length==0 || as.length == 1 || as.isEmpty) true
//          ↑↑ ↑↑↑↑         ↑↑↑↑ ↑↑↑↑

Какие критерии вы используете, чтобы решить, когда использовать пробелы или нет? Что это значит, что вы использовали пробелы вокруг || и второго ==, но не первого? Какую важную информацию вы хотите сообщить мне, читателю вашего кода, этим решением?

Лично я бы написал это так:

if(as.length == 0 || as.length == 1 || as.isEmpty) true
//          ↑↑↑↑ ↑↑↑↑         ↑↑↑↑ ↑↑↑↑

Это также соответствует стандартным принципам стиля сообщества Scala.

Точно так же вы используете пробелы после запятой в списках параметров и не используйте пробелы в списках аргументов. Стандартные принципы стиля сообщества Scala рекомендуют использовать пробелы после запятой для удобства чтения:

else if(!ordered(as(0), as(1))) false
//                     ↑
else isSorted(as.tail, ordered)
//                    ↑

Стандартные принципы стиля сообщества Scala также рекомендуют использовать пробелы после ключевого слова потока управления, такого как if или while, чтобы четко отличить их от вызова метода:

if (as.length == 0 || as.length == 1 || as.isEmpty) true
//↑
else if (!ordered(as(0), as(1))) false
//     ↑

Также обратите внимание, что проверка нулевой длины и пустоты избыточна, это одно и то же:

if (as.length == 1 || as.isEmpty) true

И, наконец, теперь, когда наш метод содержит только одно выражение, фигурные скобки нам больше не нужны:

@tailrec
def isSorted[A](as: Array[A], ordered: (A, A) => Boolean): Boolean = 
  if (as.length == 1 || as.isEmpty) true
  else if (!ordered(as(0), as(1))) false
  else isSorted(as.tail, ordered)

Однако на самом деле есть гораздо лучший способ решить эту проблему: массив сортируется, если каждая последующая пара элементов упорядочена:

def isSorted[A](as: Array[A], ordered: (A, A) => Boolean) = 
  as.sliding(2).forall { case Array(a, b) => ordered(a, b) }

Подпись вашего метода неудобна. Вывод типа выполняется только от одного списка аргументов к другому, но не внутри одного списка аргументов, поэтому в вашем случае компилятор не будет знать, что A находится в ordered, даже если он уже знает, что A находится в as:

isSorted(Array(1, 5, 3, 4), (a, b) => a < b)
// error: missing parameter type
// isSorted(Array(1, 5, 3, 4), (a, b) => a < b)
//                              ^
// error: missing parameter type
// isSorted(Array(1, 5, 3, 4), (a, b) => a < b)
//                                 ^

Вы должны явно указать компилятору тип:

isSorted(Array(1, 5, 3, 4), (a: Int, b: Int) => a < b)
//=> res: Boolean = false

По этой причине предпочтительно иметь параметр функции в отдельном списке параметров:

def isSorted[A](as: Array[A])(ordered: (A, A) => Boolean) = 
  as.sliding(2).forall { case Array(a, b) => ordered(a, b) }

Теперь вывод типа работает как задумано:

isSorted(Array(1, 5, 3, 4))((a, b) => a < b)
//=> res: Boolean = false

И вы также можете заблокировать синтаксис для функций с помощью заполнителей:

isSorted(Array(1, 5, 3, 4)) { _ < _ }
//=> res: Boolean = false

Наконец, подпись на самом деле гораздо более ограничена, чем необходимо: на самом деле ничто не требует, чтобы as был Array, она будет работать так же хорошо с гораздо более общим типом, таким как Seq:

def isSorted[A](as: Seq[A])(ordered: (A, A) => Boolean) = 
  as.sliding(2).forall { case Seq(a, b) => ordered(a, b) }

Теперь мы могли бы также передать List, например, вместо только Array s. Фактически, если немного переписать метод, можно будет заставить его работать для всех Iterable.

4
Jörg W Mittag 21 Сен 2018 в 10:22

Используйте ключевое слово return:

@tailrec
  def isSorted[A](as: Array[A], ordered: (A, A) => Boolean): Boolean = {
    if (as.length == 0 || as.length == 1 || as.isEmpty) return true
    else if (!ordered(as(0), as(1))) return false
    isSorted(as.tail, ordered)
  }

Или вы можете сделать это (рекомендуется):

@tailrec
      def isSorted[A](as: Array[A], ordered: (A, A) => Boolean): Boolean = {
        if (as.length == 0 || as.length == 1 || as.isEmpty) return true
        else if (!ordered(as(0), as(1))) return false
        else isSorted(as.tail, ordered)
      }
0
Raman Mishra 21 Сен 2018 в 09:08