Как найти самый большой элемент в списке целых чисел рекурсивно?

Я пытаюсь написать функцию, которая будет рекурсивно находить наибольший элемент в списке целых чисел. Я знаю, как это сделать на Java, но не могу понять, как это сделать на Scala.

Вот что я до сих пор, но без рекурсии:

  def max(xs: List[Int]): Int = {
    if (xs.isEmpty) throw new java.util.NoSuchElementException();
    else xs.max;
  }

Как мы можем найти его рекурсивно с помощью Scala semantic.

Ответы

Ответ 1

Это самая минимальная рекурсивная реализация max, которую я когда-либо мог придумать:

def max(xs: List[Int]): Option[Int] = xs match {
  case Nil => None
  case List(x: Int) => Some(x)
  case x :: y :: rest => max( (if (x > y) x else y) :: rest )
} 

Он работает, сравнивая первые два элемента в списке, отбрасывая меньший (или первый, если оба равны), а затем вызывает себя в оставшемся списке. В конце концов, это уменьшит список до одного элемента, который должен быть самым большим.

Я возвращаю Опцию, чтобы иметь дело с тем, чтобы получить пустой список без исключения исключения, что заставляет вызывающий код распознавать возможность и обрабатывать его (вплоть до вызывающего, если они хотят выбросить исключение).

Если вы хотите, чтобы он был более общим, он должен быть написан следующим образом:

def max[A <% Ordered[A]](xs: List[A]): Option[A] = xs match {
  case Nil => None
  case x :: Nil => Some(x)
  case x :: y :: rest => max( (if (x > y) x else y) :: rest )
}

который будет работать с любым типом, который либо расширяет черту Ordered, либо имеет неявное преобразование от A до Ordered[A] в области видимости. Поэтому по умолчанию он работает для Int, BigInt, Char, String и т.д., Потому что scala.Predef определяет для них конверсии.

Мы можем стать еще более типичными:

def max[A <% Ordered[A]](xs: Seq[A]): Option[A] = xs match {
  case s if s.isEmpty || !s.hasDefiniteSize => None
  case s if s.size == 1 => Some(s(0))
  case s if s(0) <= s(1) => max(s drop 1)
  case s => max((s drop 1).updated(0, s(0)))
}

который будет работать не только со списками, но и с векторами и любой другой коллекцией, которая расширяет черту Seq. Обратите внимание, что мне пришлось добавить чек, чтобы увидеть, действительно ли последовательность имеет определенный размер - это может быть бесконечный поток, поэтому мы отступаем, если это может быть так. Если вы уверены, что ваш поток будет иметь определенный размер, вы всегда можете заставить его вызывать эту функцию - она ​​все равно будет работать через весь поток. См. Примечания в конце, почему я действительно не хотел бы возвращать None для неопределенного потока. Я делаю это здесь исключительно для простоты.

Но это не работает для множеств и карт. Что делать? Следующий общий супертип Iterable, но это не поддерживает updated или что-либо эквивалентное. Все, что мы построим, может быть очень плохо выполнено для фактического типа. Таким образом, моя чистая не-вспомогательная функция рекурсии ломается. Мы могли бы перейти на использование вспомогательной функции, но в других ответах есть много примеров, и я собираюсь придерживаться подхода с одной простой функцией. Итак, в этот момент мы можем перейти на reduceLeft (и пока мы на нем, отпустим для "Traversable" и обслуживаем все коллекции):

def max[A <% Ordered[A]](xs: Traversable[A]): Option[A] = {
  if (xs.hasDefiniteSize) 
    xs reduceLeftOption({(b, a) => if (a >= b) a else b}) 
  else None
}

но если вы не учитываете редукцию reduceLeft, мы можем это сделать:

def max[A <% Ordered[A]](xs: Traversable[A]): Option[A] = xs match {
  case i if i.isEmpty => None
  case i if i.size == 1 => Some(i.head)
  case i if (i collect { case x if x > i.head => x }).isEmpty => Some(i.head)
  case _ => max(xs collect { case x if x > xs.head => x })
}

Он использует комбинатор collect, чтобы избежать некоторого неуклюжего способа выведения нового Итератора из xs.head и xs drop 2.

Любой из них будет работать безопасно почти с любой коллекцией всего, что имеет порядок. Примеры:

scala>  max(Map(1 -> "two", 3 -> "Nine", 8 -> "carrot"))
res1: Option[(Int, String)] = Some((8,carrot))

scala> max("Supercalifragilisticexpialidocious")
res2: Option[Char] = Some(x)

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

Кроме того, помните, что базовый признак Traversable предоставляет метод max, так что все это просто для практики;)

Примечание. Я надеюсь, что все мои примеры показывают, насколько тщательный выбор последовательности выражений case может сделать каждое индивидуальное выражение как можно более простым.

Важное примечание: О, также, когда я очень комфортно возвращаю None для ввода Nil, на практике я бы сильно склонялся к исключению для hasDefiniteSize == false. Во-первых, конечный поток может иметь определенный или неопределенный размер, зависящий исключительно от последовательности оценки, и эта функция будет эффективно случайным образом возвращать Option в этих случаях, что может занять много времени для отслеживания. Во-вторых, я хотел бы, чтобы люди могли различать пройденный Nil и прошедший поистине ввод риска (т.е. Бесконечный поток). Я только вернул Option в эти демонстрации, чтобы код был как можно более простым.

Ответ 2

Самый простой подход - использовать максимальную функцию TraversableOnce, следующим образом,

val list = (1 to 10).toList
list.max

чтобы защитить от пустоты, вы можете сделать что-то вроде этого,

if(list.empty) None else Some(list.max)

Выше будет дано Option[Int]

Мой второй подход будет использовать foldLeft

(list foldLeft None)((o, i) => o.fold(Some(i))(j => Some(Math.max(i, j))))

или если вы знаете значение по умолчанию, которое будет возвращено в случае пустого списка, это станет более простым.

val default = 0
(list foldLeft default)(Math.max)

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

def recur(list:List[Int], i:Option[Int] = None):Option[Int] = list match {
  case Nil => i
  case x :: xs => recur(xs, i.fold(Some(x))(j => Some(Math.max(j, x))))
}

или по умолчанию,

val default = 0
def recur(list:List[Int], i:Int = default):Int = list match {
  case Nil => i
  case x :: xs => recur(xs, i.fold(x)(j => Math.max(j, x)))
}

Обратите внимание, что это tail recursive. Поэтому стек также сохраняется.

Ответ 3

Если вам нужен функциональный подход к этой проблеме, используйте reduceLeft:

def max(xs: List[Int]) = {
  if (xs.isEmpty) throw new NoSuchElementException
  xs.reduceLeft((x, y) => if (x > y) x else y)
}

Эта функция специфична для списка int, если вам нужен более общий подход, используйте Ordering typeclass:

def max[A](xs: List[A])(implicit cmp: Ordering[A]): A = {
  if (xs.isEmpty) throw new NoSuchElementException
  xs.reduceLeft((x, y) => if (cmp.gteq(x, y)) x else y)
}   

reduceLeft - это функция более высокого порядка, которая принимает функцию типа (A, A) => A, в этом случае она принимает два значения, сравнивает их и возвращает большую.

Ответ 4

Вы можете использовать сопоставление с шаблоном, подобное этому

def max(xs: List[Int]): Int = xs match {
  case Nil => throw new NoSuchElementException("The list is empty")
  case x :: Nil => x
  case x :: tail => x.max(max(tail)) //x.max is Integer class method
}

Ответ 5

Scala - это функциональный язык, посредством которого можно поощрять думать рекурсивно. Мое решение, как показано ниже. Я возвращаю его по вашему методу.

  def max(xs: List[Int]): Int = {
    if(xs.isEmpty == true) 0
    else{
      val maxVal= max(xs.tail)
      if(maxVal >= xs.head) maxVal 
      else                  xs.head     
    }
  }

Обновлено мое решение хвостом рекурсивным благодаря предложениям.

  def max(xs: List[Int]): Int = {    
    def _max(xs: List[Int], maxNum: Int): Int = {   
      if (xs.isEmpty) maxNum
      else {
        val max = {
          if (maxNum >= xs.head) maxNum
          else xs.head
        }
        _max(xs.tail, max)
      }
    }
    _max(xs.tail, xs.head)
  }

Ответ 6

Я использовал только head() и tail()

def max(xs: List[Int]): Int = {
    if (xs.isEmpty) throw new NoSuchElementException
    else maxRecursive(xs.tail,xs.head) 
  }

  def maxRecursive(xs: List[Int], largest: Int): Int = {
    if (!xs.isEmpty ){
      if (xs.head > largest) maxRecursive(xs.tail, xs.head)
      else maxRecursive(xs.tail, largest)
    }else{
      largest
    }
  }

Вот тест:

test("max of a few numbers") {
    assert(max(List(3, 7, 2, 1, 10)) === 10)
    assert(max(List(3, -7, 2, -1, -10)) === 3)
    assert(max(List(-3, -7, -2, -5, -10)) === -2)
  }

Ответ 7

  • Складывание может помочь:

    if(xs.isEmpty)
      throw new NoSuchElementException
    else
      (Int.MinValue /: xs)((max, value) => math.max(max, value))
    
  • Сравнение списков и шаблонов ( обновлено, благодаря @x3ro)

    def max(xs:List[Int], defaultValue: =>Int):Int = {
      @tailrec
      def max0(xs:List[Int], maxSoFar:Int):Int = xs match {
        case Nil => maxSoFar
        case head::tail => max0(tail, math.max(maxSoFar, head))
      }
      if(xs.isEmpty)
        defaultValue
      else
        max0(xs, Int.MinValue)
    }
    

(Это решение не создает экземпляр Option каждый раз, а также является хвостовым рекурсивным и будет таким же быстрым, как и императивное решение.)

Ответ 8

Похоже, вы только начинаете с scala, поэтому я пытаюсь дать вам самый простой ответ на ваш ответ, как это сделать рекурсивно:

 def max(xs: List[Int]): Int = {
   def maxrec(currentMax : Int, l: List[Int]): Int = l match {
     case Nil => currentMax
     case head::tail => maxrec(head.max(currentMax), tail) //get max of head and curretn max
   }
   maxrec(xs.head, xs)
 }

Этот метод определяет собственный внутренний метод (maxrec), чтобы обеспечить рекурсивность. Он не сработает (исключение), вы дадите ему пустой список (в пустом списке нет максимума)

Ответ 9

Вот мой код (я новичок в функциональном программировании), и я предполагаю, что тот, кто приземляется на этот вопрос, будет таким же, как я. Главный ответ, в то время как отличный, слишком много для новичков! Итак, вот мой простой ответ. Обратите внимание, что меня попросили (как часть курса) сделать это, используя только голову и хвост.

/**
   * This method returns the largest element in a list of integers. If the
   * list `xs` is empty it throws a `java.util.NoSuchElementException`.
   *
   * @param xs A list of natural numbers
   * @return The largest element in `xs`
   * @throws java.util.NoSuchElementException if `xs` is an empty list
   */
    @throws(classOf[java.util.NoSuchElementException])
    def max(xs: List[Int]): Int = find_max(xs.head, xs.tail)

    def find_max(max: Int, xs: List[Int]): Int = if (xs.isEmpty) max else if (max >= xs.head) find_max(max, xs.tail) else find_max(xs.head, xs.tail)

Некоторые тесты:

test("max of a few numbers") {
    assert(max(List(3, 7, 2)) === 7)
    intercept[NoSuchElementException] {
      max(List())
    }
    assert(max(List(31,2,3,-31,1,2,-1,0,24,1,21,22)) === 31)
    assert(max(List(2,31,3,-31,1,2,-1,0,24,1,21,22)) === 31)
    assert(max(List(2,3,-31,1,2,-1,0,24,1,21,22,31)) === 31)
    assert(max(List(Int.MaxValue,2,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,22)) === Int.MaxValue)
  }

Ответ 10

list.sortWith(_ > ).head и list.sortWith( > _). reverse.head для наибольшего и наименьшего числа

Ответ 11

Если вам нужно написать рекурсивную функцию max в списке, используя isEmpty, head и tail и исключение throw для пустого списка:

def max(xs: List[Int]): Int =
  if (xs.isEmpty) throw new NoSuchElementException("max of empty list")
  else if (xs.tail.isEmpty) xs.head
  else if (xs.head > xs.tail.head) max(xs.head :: xs.tail.tail)
  else max(xs.tail)

если вы должны использовать функцию max в списке, просто (вам не нужно писать собственную рекурсивную функцию):

val maxInt = List(1, 2, 3, 4).max

Ответ 12

def max(xs: List[Int]): Int = {
  def _max(xs: List[Int], maxAcc:Int): Int = {
    if ( xs.isEmpty ) 
      maxAcc 
    else 
      _max( xs.tail, Math.max( maxAcc, xs.head ) ) // tail call recursive
  }

  if ( xs.isEmpty ) 
    throw new NoSuchElementException() 
  else 
    _max( xs, Int.MinValue );
}

Ответ 13

С хвостовой рекурсией

  @tailrec
  def findMax(x: List[Int]):Int =  x match {
    case a :: Nil => a
    case a :: b :: c =>  findMax( (if (a > b) a else b) ::c)
  }

Ответ 14

С сопоставлением с образцом, чтобы найти максимум и вернуть ноль в случае пустого

  def findMax(list: List[Int]) = {
    def max(list: List[Int], n: Int) : Int = list match {
      case h :: t => max(t, if(h > n) h else n)
      case _ => n
    }
    max(list,0)
  }

Ответ 15

 def max(xs: List[Int]): Int = xs match {
    case Nil => throw new NoSuchElementException("empty list!")
    case x :: Nil => x
    case x :: tail => if (x > max(tail)) x else max(tail)
  }