web-dev-qa-db-fra.com

Comment trouver le plus grand élément dans une liste d'entiers de manière récursive?

J'essaie d'écrire une fonction qui trouvera récursivement le plus grand élément d'une liste d'entiers. Je sais comment faire cela en Java, mais je ne comprends pas comment faire cela à Scala.

Voici ce que j'ai jusqu'à présent, mais sans récursivité: 

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

Comment pouvons-nous le trouver récursivement avec Scala sémantique.

15
nazar_art

C'est l'implémentation récursive la plus minimale de max que j'ai jamais pu imaginer:

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

Cela fonctionne en comparant les deux premiers éléments de la liste, en éliminant le plus petit (ou le premier, si les deux sont égaux), puis en s’appelant lui-même sur la liste restante. Finalement, cela réduira la liste à un élément qui doit être le plus grand.

Je retourne une option pour traiter le cas où une liste vide est donnée sans lever une exception - ce qui oblige le code appelant à reconnaître la possibilité et à la gérer (jusqu'à l'appelant si ils} veulent lancer une exception).

Si vous voulez que ce soit plus générique, écrivez comme ceci:

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

Qui fonctionnera avec tout type qui étend le trait Ordered ou pour lequel il existe une conversion implicite de A à Ordered[A] dans la portée. Donc, par défaut, cela fonctionne pour Int, BigInt, Char, String et ainsi de suite, car scala.Predef définit les conversions pour eux.

Nous pouvons devenir encore plus génériques comme ceci:

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

Ce qui fonctionnera non seulement avec les listes, mais aussi avec les vecteurs et toute autre collection qui étend le trait Seq. Notez que je devais ajouter une vérification pour voir si la séquence avait réellement une taille définie - ce pourrait être un flux infini, donc nous reculons si cela pouvait être le cas. Si vous êtes sûr que votre flux aura une taille définie, vous pouvez toujours le forcer avant d'appeler cette fonction - il fonctionnera de toute façon dans tout le flux. Voir les notes à la fin pour savoir pourquoi je vraiment ne voudrais pas renvoyer None pour un flux indéfini, cependant. Je le fais ici purement pour la simplicité.

Mais cela ne fonctionne pas pour les ensembles et les cartes. Que faire? Le prochain supertype commun est Iterable, mais cela ne prend pas en charge updated ni rien d’équivalent. Tout ce que nous construisons peut être très peu performant pour le type actuel. Donc, ma récursion propre sans fonction d'assistance s'effondre. Nous pourrions utiliser une fonction d'assistance, mais les autres réponses renferment de nombreux exemples et je vais m'en tenir à une approche à fonction simple. Donc, à ce stade, nous pouvons passer à reduceLeft (et pendant que nous y sommes, passons à `Traversable 'et prenons en charge les toutes collections):

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
}

mais si vous ne considérez pas reductionLeft récursive, nous pouvons le faire:

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

Il utilise le combinateur collect pour éviter une méthode maladroite d’évincer un nouvel Iterator en xs.head et xs drop 2.

L'un ou l'autre fonctionnera en toute sécurité avec presque toutes les collections de commandes. Exemples:

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

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

Je ne donne généralement pas ces exemples comme exemples, car cela nécessite une connaissance plus approfondie de Scala.

De plus, rappelez-vous que le trait de base Traversable fournit une méthode max, c'est donc juste pour la pratique;)

Remarque: j'espère que tous mes exemples montrent à quel point le choix judicieux de la séquence des expressions de casse peut rendre chaque expression de casse aussi simple que possible.

Note plus importante:Oh, aussi, bien que je sois extrêmement à l'aise pour retourner None pour une entrée de Nil, en pratique, je serais fortement enclin à lancer une exception pour hasDefiniteSize == false. Tout d'abord, un flux fini pourrait avoir une taille définie ou non définie dépendant uniquement de la séquence d'évaluation et cette fonction renverrait effectivement de manière aléatoire Option dans les cas - ce qui pourrait prendre beaucoup de temps à localiser. Deuxièmement, je voudrais que les gens puissent faire la différence entre avoir passé Nil et avoir passé avec véritablement une entrée de risque (c'est-à-dire un flux infini). Je n’ai retourné que Option dans ces démonstrations pour que le code soit aussi simple que possible.

34
itsbruce

L’approche la plus simple consisterait à utiliser la fonction max du trait TraversableOnce, comme suit:

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

pour vous prémunir contre le vide, vous pouvez faire quelque chose comme ça,

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

Ci-dessus vous donnera un Option[Int]

Ma deuxième approche consisterait à utiliser foldLeft

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

ou si vous connaissez une valeur par défaut à renvoyer en cas de liste vide, cela deviendra plus simple.

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

Quoi qu’il en soit, puisque votre exigence est de le faire de manière récursive, je propose de suivre,

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

ou comme cas par défaut,

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

Notez que ceci est tail recursive. Par conséquent, la pile est également enregistrée.

16
tiran

Si vous voulez une approche fonctionnelle de ce problème, utilisez reduceLeft:

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

Cette fonction spécifique à la liste des entrées, si vous avez besoin d’une approche plus générale, utilisez la classe Ordering:

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 est une fonction d'ordre supérieur qui prend une fonction de type (A, A) => A. Dans ce cas, il faut deux ints, les compare et renvoie le plus grand.

13
4lex1v

Vous pouvez utiliser la correspondance de motif comme ç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
}
8
Nam Vo

Scala est un langage fonctionnel dans lequel on est encouragé à penser récursivement. Ma solution comme ci-dessous. Je le récite base sur votre méthode donnée.

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

Mise à jour de ma solution en queue récursive grâce aux suggestions.

  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

J'ai utilisé juste head () et 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
    }
  }

Voici le 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
Roman Kazanovskyi
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 pour le plus grand et le plus petit nombre

0
tejas
  1. Le pliage peut aider:

    if(xs.isEmpty)
      throw new NoSuchElementException
    else
      (Int.MinValue /: xs)((max, value) => math.max(max, value))
    
  2. Correspondance de liste et de motif ( mis à jour , grâce à @ 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)
    }
    

(Cette solution ne crée pas à chaque fois une instance Option. Elle est également récurrente et sera aussi rapide qu'une solution impérative.)

0
Arseniy Zhizhelev

Voici mon code (je suis un débutant en programmation fonctionnelle) et je suppose que quiconque tombera sous cette question sera un peuple comme moi. La meilleure réponse, bien que géniale, est un peu trop difficile à prendre pour les débutants! Donc, voici ma réponse simple. Notez que l’on m’a demandé (dans le cadre d’un cours) de le faire en utilisant uniquement la tête et la queue.

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

Quelques 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

Si vous devez écrire une fonction max récursive sur une liste en utilisant une exception isEmpty, head et tail and throw pour la liste vide:

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)

si vous utilisiez la fonction max sur la liste, c’est tout simplement (vous n’avez pas besoin d’écrire votre propre fonction récursive):

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

On dirait que vous commencez tout juste avec scala, alors j'essaie de vous donner la réponse la plus simple à votre réponse, comment procéder de manière récursive: 

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

Cette méthode définit une méthode interne (maxrec) propre à prendre en charge la récursivité. Il échouera (exception) si vous lui donnez une liste vide (il n'y a pas de maximum sur une liste vide)

0
Chirlo

Avec queue-récursion

  @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