it-swarm.com.de

Wie finde ich das größte Element in einer Liste von Ganzzahlen rekursiv?

Ich versuche, eine Funktion zu schreiben, die rekursiv das größte Element in einer Liste von ganzen Zahlen findet. Ich weiß, wie man dies in Java macht, aber ich kann nicht verstehen, wie man dies bei Scala macht.

Folgendes habe ich bisher, aber ohne Rekursion: 

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

Wie können wir es mit Scala Semantic rekursiv finden?.

15
nazar_art

Dies ist die minimalste rekursive Implementierung von max, die ich mir jemals ausgedacht habe:

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 )
} 

Es vergleicht die ersten beiden Elemente in der Liste, verwirft das kleinere (oder das erste, wenn beide gleich sind) und ruft sich dann in der verbleibenden Liste auf. Dadurch wird die Liste möglicherweise auf ein Element reduziert, das das größte sein muss.

Ich gebe eine Option zurück, um den Fall zu behandeln, dass eine leere Liste ohne Auslösen einer Ausnahme ausgegeben wird - wodurch der aufrufende Code gezwungen wird, die Möglichkeit zu erkennen und damit umzugehen (bis zum Aufrufer, wenn sie werfen möchten eine Ausnahme).

Wenn es allgemeiner sein soll, sollte es so geschrieben werden:

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 )
}

Was mit jedem Typ funktioniert, der entweder das Merkmal Ordered erweitert oder für den es eine implizite Konvertierung von A in Ordered[A] im Gültigkeitsbereich gibt. Es funktioniert also standardmäßig für Int, BigInt, Char, String und so weiter, da scala.Predef Konvertierungen für sie definiert.

Wir können noch allgemeiner werden:

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)))
}

Das funktioniert nicht nur mit Listen, sondern auch mit Vektoren und jeder anderen Sammlung, die das Merkmal Seq erweitert. Beachten Sie, dass ich ein Häkchen setzen musste, um festzustellen, ob die Sequenz tatsächlich eine bestimmte Größe hat - es könnte sich um einen unendlichen Stream handeln. Wenn Sie sicher sind, dass Ihr Stream eine bestimmte Größe hat, können Sie ihn immer erzwingen, bevor Sie diese Funktion aufrufen - er wird sowieso den gesamten Stream durchlaufen. Siehe Anmerkungen am Ende, warum ich wirklich nicht None für einen unbestimmten Stream zurückgeben möchte. Ich mache es hier nur der Einfachheit halber.

Dies funktioniert jedoch nicht für Sets und Maps. Was ist zu tun? Der nächste gebräuchliche Supertyp ist Iterable, aber das unterstützt updated oder etwas Äquivalentes nicht. Alles, was wir konstruieren, kann für den tatsächlichen Typ sehr schlecht abschneiden. Also bricht meine saubere No-Helper-Funktion-Rekursion zusammen. Wir könnten auf die Verwendung einer Hilfsfunktion umstellen, aber es gibt viele Beispiele in den anderen Antworten, und ich werde bei einem Ansatz mit einer einfachen Funktion bleiben. An diesem Punkt können wir also zu reduceLeft wechseln (und während wir dabei sind, gehen wir zu `Traversable 'und berücksichtigen alle Sammlungen):

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
}

aber wenn Sie reduLeft nicht als rekursiv betrachten, können wir dies tun:

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 })
}

Es verwendet den Kombinator collect, um eine umständliche Methode zu vermeiden, einen neuen Iterator aus xs.head und xs drop 2 herauszuholen.

Beides funktioniert sicher mit fast jeder Sammlung von Objekten, für die eine Bestellung vorliegt. Beispiele:

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

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

Ich gebe diese anderen normalerweise nicht als Beispiele an, da dies mehr Expertenwissen über Scala erfordert.

Denken Sie auch daran, dass das Grundmerkmal Traversable eine max -Methode bietet.

Hinweis: Ich hoffe, dass alle meine Beispiele zeigen, wie sorgfältig die Reihenfolge Ihrer Fallausdrücke gewählt werden kann, um jeden einzelnen Fallausdruck so einfach wie möglich zu gestalten.

Wichtigerer Hinweis: Oh, auch wenn ich es sehr bequem finde, None für eine Eingabe von Nil zurückzugeben, wäre ich in der Praxis stark geneigt, eine Ausnahme für hasDefiniteSize == false zu werfen. . Erstens könnte ein endlicher Datenstrom eine bestimmte oder nicht bestimmte Größe haben, die rein von der Reihenfolge der Auswertung abhängt, und diese Funktion würde in diesen Fällen effektiv zufällig Option zurückgeben - was eine lange Zeit in Anspruch nehmen könnte, um aufzuspüren. Zweitens möchte ich, dass die Leute unterscheiden können, ob sie Nil bestanden haben oder ob sie wirklich riskante Eingaben (dh einen unendlichen Strom) bestanden haben. Ich habe in diesen Demonstrationen nur Option zurückgegeben, um den Code so einfach wie möglich zu halten.

34
itsbruce

Der einfachste Ansatz wäre die Verwendung der max-Funktion von TraversableOnce wie folgt:

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

um sich gegen die Leere zu schützen, kannst du so etwas tun,

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

Oben erhalten Sie einen Option[Int]

Mein zweiter Ansatz wäre foldLeft

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

wenn Sie wissen, dass bei einer leeren Liste ein Standardwert zurückgegeben werden soll, wird dies einfacher.

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

Da Sie jedoch rekursiv vorgehen müssen, schlage ich Folgendes vor:

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))))
}

oder als Standardfall,

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)))
}

Beachten Sie, dass dies tail recursive ist. Daher wird auch der Stapel gespeichert.

16
tiran

Wenn Sie einen funktionalen Ansatz für dieses Problem wünschen, verwenden Sie reduceLeft:

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

Diese Funktion ist spezifisch für eine Liste von Ints. Wenn Sie einen allgemeineren Ansatz benötigen, verwenden Sie 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 ist eine Funktion höherer Ordnung, die eine Funktion des Typs (A, A) => A übernimmt. In diesem Fall werden zwei Ints verwendet, die beiden verglichen und der größere Wert zurückgegeben.

13
4lex1v

Sie könnten Musterabgleich so verwenden

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
}
8
Nam Vo

Scala ist eine funktionale Sprache, in der man rekursiv denken soll. Meine Lösung wie unten. Ich wiederhole es basierend auf Ihrer gegebenen Methode.

  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     
    }
  }

Meine Lösung wurde durch Vorschläge auf rekursive Weise aktualisiert.

  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
Tay Wee Wen

Ich habe nur head () und tail () verwendet

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
    }
  }

Hier ist der Test:

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)
  }
1
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 );
}
0
Andreas

list.sortWith (_>) .head & list.sortWith (> _). reverse.head für die größte und kleinste Zahl

0
tejas
  1. Falten kann helfen:

    if(xs.isEmpty)
      throw new NoSuchElementException
    else
      (Int.MinValue /: xs)((max, value) => math.max(max, value))
    
  2. Listen- und Musterabgleich ( aktualisiert dank @ 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)
    }
    

(Diese Lösung erstellt nicht jedes Mal eine Option-Instanz. Außerdem ist sie rekursiv und wird so schnell wie eine zwingende Lösung sein.)

0

Hier ist mein Code (ich bin ein Neuling in der funktionalen Programmierung) und ich gehe davon aus, wer unter dieser Frage landet, wird Leute wie ich sein. Die beste Antwort ist zwar großartig, aber für Neulinge etwas zu viel! Hier ist meine einfache Antwort. Beachten Sie, dass ich (als Teil eines Kurses) gebeten wurde, dies nur mit Kopf und Schwanz zu tun.

/**
   * 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)

Einige Tests:

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)
  }
0
Anand

Wenn Sie eine rekursive Max-Funktion mit isEmpty, head und tail und einer Ausnahme für leere Liste in eine Liste schreiben müssen:

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)

wenn Sie die max-Funktion in der Liste verwenden sollten, ist dies einfach (Sie müssen keine eigene rekursive Funktion schreiben):

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

Mit Pattern Matching, um Max zu finden und Null zurückzugeben, falls leer

  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)
  }

Sieht so aus, als würden Sie gerade mit scala anfangen, also versuche ich Ihnen die einfachste Antwort auf Ihre Antwort zu geben. 

 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)
 }

Diese Methode definiert eine eigene innere Methode (maxrec), um auf die Rekursivität zu achten. Es wird fehlschlagen (Ausnahme), wenn Sie ihm eine leere Liste geben (es gibt kein Maximum auf einer leeren Liste)

0
Chirlo

Mit Schwanzrekursion

  @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)
  }
0
BiN