Как рекурсивно найти самый большой элемент в списке целых чисел?
Я пытаюсь написать функцию, которая рекурсивно найдет самый большой элемент в списке целых чисел. Я знаю, как это сделать на Java, но не могу понять, как это сделать в Scala.
Вот что у меня есть до сих пор, но без рекурсии:
def max(xs: List[Int]): Int = {
if (xs.isEmpty) throw new java.util.NoSuchElementException();
else xs.max;
}
Как мы можем найти его рекурсивно с семантикой Scala.
14 ответов:
Это самая минимальная рекурсивная реализация 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и так далее, потому что скала.Предопределенных определяет обменами для их.Мы можем стать еще более общими, как это:
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(и пока мы на этом, давайте перейдем к "проходимым" и обслуживаем все коллекции):Но если вы не считаете reduceLeft рекурсивным, мы можем сделать следующее: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 }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.scala> max(Map(1 -> "two", 3 -> "Nine", 8 -> "carrot")) res1: Option[(Int, String)] = Some((8,carrot)) scala> max("Supercalifragilisticexpialidocious") res2: Option[Char] = Some(x)Также помните, что основная черта
Traversableобеспечивает методmax, поэтому все это только для практики ;)Примечание: Я надеюсь, что все мои примеры показывают, насколько тщательный выбор последовательности выражений вашего случая может сделать каждый выражение индивидуального случая максимально простое.
более важное примечание: О, кроме того, хотя мне очень удобно возвращать
Noneдля вводаNil, на практике я был бы сильно склонен к исключению дляhasDefiniteSize == false. Во-первых, конечный поток может иметь определенный или неопределенный размер, зависящий исключительно от последовательности оценки, и эта функция будет эффективно случайным образом возвращатьOptionв тех случаях, которые могут занять много времени для отслеживания. Во вторых, Я бы хотел, чтобы люди могли различать, пройдяNil, и пройдя действительно рисковый вход (то есть бесконечный поток). Я только вернулсяOptionВ этих демонстрациях, чтобы сохранить код как можно более простым.
Проще всего было бы использовать функцию max признака
TraversableOnceследующим образом:Чтобы защититься от пустоты, вы можете сделать что-то вроде этого,val list = (1 to 10).toList list.maxif(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. Поэтому стек также сохраняется.
Если вы хотите функциональный подход к этой проблеме, то используйте
reduceLeft:def max(xs: List[Int]) = { if (xs.isEmpty) throw new NoSuchElementException xs.reduceLeft((x, y) => if (x > y) x else y) }Эта функция специфична для списка int, если вам нужен более общий подход, то используйте
Orderingtypeclass: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, в этом случае она берет два Инта, сравнивает их и возвращает больший.
Вы можете использовать сопоставление шаблонов, как это
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's class method }
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) }
Я использовал только 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) }
Складывание может помочь:
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каждый раз. Кроме того, она является хвостовой рекурсивной и будет такой же быстрой, как императивное решение.)
Похоже, вы только начинаете со 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), чтобы заботиться о рекурсивности. Он потерпит неудачу (исключение), если вы дадите ему пустой список (в пустом списке нет максимума)
Вот мой код (я новичок в функциональном программировании), и я предполагаю, что тот, кто попадет под этот вопрос, будет таким же, как я. Лучший ответ, хотя и отличный, немного слишком много для новичков, чтобы принять! Итак, вот мой простой ответ. Обратите внимание, что меня попросили (в рамках курса) сделать это, используя только голову и хвост.
/** * 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) }
Список.sortWith(_ > ).глава и список.sortWith( > _).обратный.голова для наибольшего и наименьшего числа
Если требуется написать рекурсивную функцию max в списке, используя isEmpty, head and 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
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 ); }
С хвостовой рекурсией
@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) }
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) }
Comments