42 votes

Scala: forme abrégée de correspondance de modèle renvoyant une valeur booléenne

J'ai trouvé moi-même écrit quelque chose comme ça assez souvent:

a match {     
  case `b` => // do stuff
  case _ => // do nothing
}

Est-il une façon plus simple pour vérifier si une valeur correspond à un modèle? Je veux dire, dans ce cas, je pourrais juste écrire if (a == b) // do stuff, mais si le modèle est plus complexe? Comme lors de l'adaptation à l'encontre d'une liste ou d'un schéma de complexité arbitraire. J'aimerais être capable d'écrire quelque chose comme ceci:

if (a matches b) // do stuff

Je suis relativement nouveau à la Scala, donc pardonnez, si je suis absent quelque chose de grand :)

63voto

extempore Points 8016

C’est précisément pour cette raison que j’ai écrit ces fonctions, qui sont apparemment obscurément impressionnantes puisque personne ne les a mentionnées.

 scala> import PartialFunction._
import PartialFunction._

scala> cond("abc") { case "def" => true }
res0: Boolean = false

scala> condOpt("abc") { case x if x.length == 3 => x + x }
res1: Option[java.lang.String] = Some(abcabc)

scala> condOpt("abc") { case x if x.length == 4 => x + x }
res2: Option[java.lang.String] = None
 

12voto

Madoc Points 3217

L' match opérateur de Scala est le plus puissant lorsqu'il est utilisé dans le style fonctionnel. Cela signifie que, plutôt que de "faire quelque chose" dans l' case des déclarations, vous seriez de retour à une valeur d'utilité. Voici un exemple pour un impératif de style:

var value:Int = 23
val command:String = ... // we get this from somewhere
command match {
  case "duplicate" => value = value * 2
  case "negate" => value = -value
  case "increment" => value = value + 1
  // etc.
  case _ => // do nothing
}
println("Result: " + value)

Il est très compréhensible que le "ne rien faire" au-dessus un peu mal, parce qu'il semble superflu. Toutefois, cela est dû au fait que le ci-dessus est écrit en impératif de style. Tandis que les constructions comme il peut parfois être nécessaire, dans de nombreux cas, vous pouvez restructurer le code de style fonctionnel:

val value:Int = 23
val command:String = ... // we get this from somewhere
val result:Int = command match {
   case "duplicate" => value * 2
   case "negate" => -value
   case "increment" => value + 1
   // etc.
   case _ => value
}
println("Result: " + result)

Dans ce cas, vous utilisez l'ensemble de l' match déclaration de valeur que vous pouvez, par exemple, d'affecter à une variable. Et il est aussi beaucoup plus évident que l' match déclaration doit retourner une valeur dans tous les cas; si le dernier cas serait absente, le compilateur ne peut pas faire quelque chose.

C'est une question de goût, mais certains développeurs considèrent ce style pour être plus transparent et plus facile à gérer en plus des exemples du monde réel. Je serais prêt à parier que les inventeurs du langage de programmation Scala avait plus l'utilisation fonctionnelle dans l'esprit de match, et en effet l' if déclaration a plus de sens si vous avez seulement besoin de décider si oui ou non une action doit être prise. (D'un autre côté, vous pouvez également utiliser if dans la manière fonctionnelle, car il a aussi une valeur de retour...)

12voto

Daniel C. Sobral Points 159554

Cela peut vous aider:

class Matches(m: Any) {
    def matches[R](f: PartialFunction[Any, R]) { if (f.isDefinedAt(m)) f(m) }
}
implicit def any2matches(m: Any) = new Matches(m)

scala> 'c' matches { case x: Int => println("Int") }                                

scala> 2 matches { case x: Int => println("Int") }  
Int

Maintenant, quelques explications sur la nature générale du problème.

Où peut un match se produire?

Il y a trois endroits où la correspondance de modèle qui pourrait arriver: val, case et for. Les règles sont les suivantes:

// throws an exception if it fails
val pattern = value 

// filters for pattern, but pattern cannot be "identifier: Type",
// though that can be replaced by "id1 @ (id2: Type)" for the same effect
for (pattern <- object providing map/flatMap/filter/withFilter/foreach) ...

// throws an exception if none of the cases match
value match { case ... => ... }

Il est, cependant, une autre situation où l' case peuvent apparaître, ce qui est la fonction et la fonction partielle de littéraux. Par exemple:

val f: Any => Unit = { case i: Int => println(i) }
val pf: PartialFunction[Any, Unit] = { case i: Int => println(i) }

Les deux fonctions et partielles des fonctions de lever une exception si elle est appelée avec un argument qui ne correspond à aucune de l'affaire états. Cependant, partielle fonctions fournissent également une méthode appelée isDefinedAt qui peut tester si un match ne peut être faite ou non, ainsi que d'une méthode appelée lift, ce qui permettra de tourner PartialFunction[T, R] en Function[T, Option[R]], ce qui signifie non-correspondance des valeurs en None au lieu de lancer une exception.

Qu'est ce qu'un match?

Un match est une combinaison de plusieurs tests différents:

// assign anything to x
case x

// only accepts values of type X
case x: X

// only accepts values matches by pattern
case x @ pattern

// only accepts a value equal to the value X (upper case here makes a difference)
case X

// only accepts a value equal to the value of x
case `x`

// only accept a tuple of the same arity
case (x, y, ..., z)

// only accepts if extractor(value) returns true of Some(Seq()) (some empty sequence)
case extractor()

// only accepts if extractor(value) returns Some something
case extractor(x)

// only accepts if extractor(value) returns Some Seq or Tuple of the same arity
case extractor(x, y, ...,  z)

// only accepts if extractor(value) returns Some Tuple2 or Some Seq with arity 2
case x extractor y

// accepts if any of the patterns is accepted (patterns may not contain assignable identifiers)
case x | y | ... | z

Maintenant, les extracteurs sont les méthodes d' unapply ou unapplySeq, la première revenant Boolean ou Option[T], et le deuxième retour Option[Seq[T]]None signifie qu'aucune correspondance n'est fait, et Some(result) va essayer de faire correspondre result comme décrit ci-dessus.

Donc, il ya toutes sortes de syntaxique solutions, qui ne sont tout simplement pas possible sans l'utilisation de l'un des trois constructions où le motif correspond peut arriver. Vous pouvez émuler certaines fonctionnalités, comme la valeur de l'égalité et des extracteurs, mais pas tous d'entre eux.

7voto

Maarten Bynens Points 71

Les modèles peuvent également être utilisés dans les expressions. Votre exemple de code

 a match {     
  case b => // do stuff
  case _ => // do nothing
}
 

peut alors être exprimé comme

 for(b <- Some(a)) //do stuff
 

L'astuce consiste à envelopper un pour en faire un énumérateur valide. Par exemple, la liste (a) fonctionnerait également, mais je pense que certaines (a) sont plus proches de votre sens.

5voto

Kim Stebel Points 22873

Le mieux que je puisse trouver est la suivante:

 def matches[A](a:A)(f:PartialFunction[A, Unit]) = f.isDefinedAt(a)

if (matches(a){case ... =>}) {
    //do stuff
}
 

Cela ne vous rapportera cependant aucun point de style.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X