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.
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.
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.
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.
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
}
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)
}
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)
}
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 );
}
list.sortWith (_>) .head & list.sortWith (> _). reverse.head pour le plus grand et le plus petit nombre
Le pliage peut aider:
if(xs.isEmpty)
throw new NoSuchElementException
else
(Int.MinValue /: xs)((max, value) => math.max(max, value))
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.)
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)
}
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
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)
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)
}