1
votes

Comment vérifier si la fonction est partielle dans Scala?

J'ai une méthode qui reçoit une fonction, mais cette fonction peut être partielle, dans ce cas je ne veux pas qu'elle échoue avec MatchError.

doSoemthing(x){
case t=> otherMethod(t)
}

De cette façon, je peux utiliser le méthode comme celle-ci

doSomething(x){
case t if predicate(t) =>  otherMethod(t)
}

donc au cas où je n'ai pas de prédicat, je peux l'utiliser comme this doSomething (x) (otherMethod) au lieu de

def doSomething[X,Y](opt:Option[X])(f:X=>Y)={
  f match {
    case p:PartialFunction[X,Y]=> opt.flatMap(p.lift) //This doesn't seem to work
    case _ => opt.map(f)
  }
}

Remarque: vous recherchez une solution qui ne nécessite pas de capturer MatchError code> exceptions


7 commentaires

Pourquoi ne pas toujours recevoir une X => Option [Y] ?


D'un autre côté, vous pouvez faire quelque chose de sale comme if (p.is InstanceOf [PartialFunction [X, Y]]) opt.flatMap (p.asInstanceOf [PartialFunction [X, Y]]. Lift)


J'ai déjà essayé ça. Cela ne fonctionne tout simplement pas. Si le compilateur attend une fonction totale, peu importe qu'elle soit écrite comme une fonction partielle, il compilera une fonction totale, donc .isInstanceOf [PartialFunction [X, Y]] renvoie toujours false.


Pourquoi ne pas aller dans l'autre sens et n'accepter que des fonctions partielles: def doSomething [X, Y] (opt: Option [X]) (p: PartialFunction [X, Y]) = opt.flatMap (p.lift) < / code>? Pour "au cas où je n'ai pas de prédicat", vous pouvez écrire doSomething (x) (otherMethod (_)) .


Wow, je ne savais pas que c'était possible


@AlexeyRomanov, en effet, cela ne fonctionne pas.


Vous avez raison, j'aurais dû le tester. Je me suis mal souvenu des règles pertinentes.


3 Réponses :


-2
votes

Je ne suis pas sûr de ce que vous passez en tant que fonction partielle, mais vous devriez certainement la définir avec une signature spécifique comme celle-ci:

def doSomething[X,Y](opt:Option[X])(f:X=>Y)={
  f match {
    case p if p.isInstanceOf[PartialFunction[X,Y]] =>
    println("I'm a pf")
    println(s"Is it PartialFunction: ${p.isInstanceOf[PartialFunction[X,Y]]}")
    opt.map(p)
    case _ =>
    println("I'm not a pf")
    opt.map(f)
  }
}

doSomething[Int, Option[Int]](Some(5))(positive) // partial function case

doSomething[Int, String](Some(5)) { // tricky case
  case s => s.toString
}

Le code positif > La fonction est définie uniquement pour les nombres positifs. En cas de nombres négatifs, la fonction renvoie None et vous n'obtiendrez pas scala.MatchError à l'exécution.

Cette fonction spécifique vous permet d'accéder à la méthode isDefinedAt qui teste dynamiquement si une valeur est dans le domaine de la fonction.

postive (5) .isDefinedAt // true

poistive.isInstanceOf [PartialFunction [Int, Option [Int]]] // true

J'ai démontré ici pourquoi vous obtenez toujours false lorsque vous vérifiez p.isInstanceOf

val positive: PartialFunction[Int, Option[Int]] = {
  case x if x >= 0 => Some(x)
  case _ => None

Vous pouvez jouer avec ici:


4 commentaires

Pourquoi un vote négatif pourriez-vous s'il vous plaît expliquer la raison, si je comprends bien la question, comment pouvons-nous utiliser correctement isInstanceOf [PartialFunction [X, Y]].


Ceci n'est pas la question.


Cependant, ma réponse couvre les deux côtés, lisez les commentaires de l'auteur ci-dessous la question. Mais lisez le titre de la question!


1. Une fonction partielle qui renvoie des options est une redondance. 2. Si j'inline positif , votre exemple échoue. 3. J'essaye de supprimer le passe-partout (comme vu dans mon dernier exemple), donc extraire le lambda et ajouter des annotations de type explicite va à l'encontre de tout le but. 4. Je n'essaye pas de comprendre comment les PartialFunctions fonctionnent, mais j'essaie de trouver un moyen d'insérer une fonction partielle et de faire en sorte que le compilateur compile réellement une fonction partielle et non une "fonction totale" qui échoue sur certaines entrées.



2
votes

Ce n'est pas une réponse car je ne pense pas que ce que vous voulez soit possible dans Scala.

La méthode originale est correcte et fonctionne comme prévu, même si elle pourrait être un peu plus simple:

XXX

Le problème est ici:

x.collect{
  case ...
}

Scala crée une Function plutôt qu'une PartialFunction de cette expression match donc le test échoue. Si vous passez une vraie PartialFunction , la méthode fonctionne bien.

x.map(f)

Je ne pense pas qu'il y ait un moyen de faire ce que vous voulez, principalement parce que doSomething a plusieurs listes d'arguments qui gâchent la déduction de type pour la deuxième liste d'arguments.

Ma suggestion est simplement d'utiliser

val p: PartialFunction[Int, Int] = {
  case i: Int if i > 0 => i
}

doSomething(Some(0))(p) // Returns None

ou

doSomething(x){
  case t if predicate(t) =>  otherMethod(t)
}

selon le cas dans le code d'appel.


0 commentaires

1
votes

La syntaxe de la fonction partielle a été modifiée depuis 2.9 par SLS 8.5, de sorte que même si vous faites {case x => y} , cela NE signifie PAS que c'est une fonction partielle. Son type sera exact tel que vous le définissez comme.

Dans votre cas, vous l'avez défini comme X => Y (comme dans votre paramètre de fonction), il ne s'agit donc que d'un X => Y (il a été compilé dans une fonction régulière, et les cas de non-correspondance lèveront MatchError), et même vous faites isInstanceOf [PartialFunciton [_, _]] , ce ne sera pas le cas match.

Pour que votre scénario fonctionne, vous pouvez simplement convertir la fonction passée en PartialFunction, comme:

doSomething(Some(1)){case 2 => 0} //This gives MatchError and it is not recognized as PartialFunction inside the body

tandis que

doSomething(Some(1))({case 2 => 0}: PartialFunction[Int,Int]) //This returns None without MatchError

Ce n'est probablement pas aussi pratique que vous le pensiez, mais c'est la seule façon de le faire fonctionner. (ou vous définissez 2 fonctions distinctes pour les deux cas, comme collect et map dans la bibliothèque standard)


0 commentaires