0
votes

Comment renvoyer une valeur produite à partir d'une boucle for dans scala?

Dans mon projet, nous migrons de Java vers Scala. J'ai une fonction en Java qui utilise continue dans la boucle for et renvoie une valeur basée sur une condition if dans la boucle for comme ci-dessous.

type mismatch;
Found   : IndexedSeq[TSourceToken]
Required: TSourceToken

Pour convertir cela en Scala, j'ai utilisé l'option Yield afin de filtrer les valeurs qui satisfont la condition else de l'expression if comme ci-dessous.

def getBeforeToken(token: TSourceToken): TSourceToken = {
  val tokens = token.container
  val index = token.posinlist

  for { i <- index -1 to 0 by -1
        currentToken = tokens.get(i)
        if(currentToken.toString.trim.length != 0)
        } yield currentToken

}

Je ne suis pas en mesure de savoir comment renvoyer la valeur générée par la boucle for. Le message d'erreur est

private TSourceToken getBeforeToken(TSourceToken token) {
  TSourceTokenList tokens = token.container;
  int index = token.posinlist;     
  for ( int i = index - 1; i >= 0; i-- ) {
    TSourceToken currentToken = tokens.get( i );
    if ( currentToken.toString( ).trim( ).length( ) == 0 ) {
      continue;
    }
    else {
      return currentToken;
    }
  }
  return token;
}

Je comprends que le yield collecte toutes les valeurs de la boucle for et sa condition à l'intérieur et entraîne une collection: IndexedSeq et donc le message d'erreur. Je ne suis pas capable de penser à une logique à former telle qu'elle a été écrite dans le code Java. Quelqu'un pourrait-il me dire comment puis-je encadrer le code dans Scala sans utiliser yield et casser la boucle for une fois que la condition else est satisfaite?


2 commentaires

oh ok .. Compris maintenant. Il s'agit d'un Iterator Java <TSourceToken>.


pourquoi ne pas le convertir également en type Scala?


3 Réponses :


7
votes

Il n'y a pas for-loops habituelles comme en java dans scala. for-yield n'est pas la même chose que for-loop . C'est juste du sucre syntaxique pour les combinaisons de fonctions flatMap , map et withFilter . Aussi, dans scala, il est préférable de ne pas utiliser de mot-clé de return . Le langage basé sur des expressions qui a une valeur de retour et un mot de return peut casser la logique et un autre développeur ne peut pas s'y attendre.

Dans votre cas, il sera préférable d'utiliser find pour rechercher un élément spécifique dans une collection par prédicat:

def getBeforeToken(token: TSourceToken): TSourceToken = {
  val tokens = token.container
  val index = token.posinlist

  (index - 1 to 0 by -1)
    .find(i => tokens.get(i).toString.trim.length != 0)
    .fold(token)(i => tokens.get(i))
}


3 commentaires

Donc, dans ce cas, find simplement casser la boucle pour la première valeur, la condition est satisfaite? Ma compréhension est-elle correcte?


@Metadata oui, il trouvera le premier jeton satisfait ou utilisera la valeur par défaut passée à getOrElse


J'ai l'impression que cela pourrait être plus facile en l'inversant, quelque chose comme token.container.view.take(index).reverse.find(_.toString.tri‌​m.nonEmpty).getOrEls‌​e(token)



1
votes

Vous pouvez en lire beaucoup sur le rendement Scala. Voici quelques bons articles:

Mais je pense que ce n'est pas la bonne façon d'aborder cela dans Scala. Scala est un langage de programmation fonctionnel, et vous pouvez utiliser ses capacités.

En supposant que TSourceToken est une classe de cas ayant deux membres:

val tokens = TSourceToken(Seq("", "   ", "  \n   ", "real token", "\t\t", "   ", "  \n\n  ", "token to ignore"), 5)
val token = tokens.container.take(tokens.posinlist).filter(_.trim.nonEmpty).last

Où vous voulez choisir le dernier token qui n'est pas vide parmi les derniers posinlist. Il existe de nombreuses façons d'y parvenir. Une des façons dont je pensais est:

case class TSourceToken(container: Seq[String], posinlist: Int)

token aura un real token , comme vous vous y attendez. L'exécution de code peut être trouvée sur Scastie .


0 commentaires

3
votes

un pour la compréhension n'est pas le bon outil pour ce problème: c'est pour la cartographie, pas pour la recherche.

Pour une réécriture simple qui réécrit simplement votre code java sur scala, utilisez une boucle while:

def getBeforeToken(token: TSoruceToken): TSourceToken =
  token.container.reverseIterator.collectFirst {
    case t if t.toString.trim.length != 0 => t
  }.getOrElse(token)

si token.container est une collection, vous l' token.container probablement sans itération explicite:

def getBeforeToken(token: TSourceToken): TSourceToken = {
  val tokens = token.container
  var i = index - 1
  while (i >= 0) {
    val currentToken = tokens.get(i)
    if (currentToken.toString.trim.length != 0) return currentToken
    i -= 1
  }
  token
}

Pendant le processus de migration, il peut être judicieux de s'en tenir au premier, pour que la traduction presque mécanique soit effectuée le plus rapidement possible, avant d'essayer d'améliorer la qualité du code scala.


0 commentaires