web-dev-qa-db-fra.com

Incrément (++) opérateur en Scala

Existe-t-il une raison pour laquelle Scala ne prend pas en charge l'opérateur ++ pour incrémenter les types primitifs par défaut?

var i=0
i++

Merci

51
adelarsq

Je suppose que cela a été omis parce que cela ne fonctionnerait que pour des variables mutables et que cela n'aurait pas de sens pour des valeurs immuables. Peut-être a-t-il été décidé que l'opérateur ++ ne crie pas l'affectation; l'inclure ainsi peut entraîner des erreurs quant à la mutation de la variable.

Je pense que quelque chose comme ceci est sans danger (sur une seule ligne):

i++

mais ce serait une mauvaise pratique (dans n'importe quelle langue):

var x = i++

Vous ne voulez pas mélanger les déclarations de mission et les effets secondaires/mutations.

34
pkaeding

J'aime Craigréponse , mais je pense que le point doit être mieux expliqué.

  1. Il n'y a pas de "primitives" - si Int peut le faire, il en va de même pour un Complex créé par l'utilisateur (par exemple).

  2. L'utilisation basique de ++ serait comme ceci:

    var x = 1 // or Complex(1, 0)

    x++

  3. Comment implémentez-vous ++ dans la classe Complex? En supposant que, comme Int, l'objet soit immuable, la méthode ++ doit renvoyer un nouvel nouvel objet , mais nouvel objet doit être attribué .

Cela nécessiterait une nouvelle fonctionnalité linguistique. Par exemple, supposons que nous créons un mot clé assign. La signature de type devra également être modifiée pour indiquer que ++ ne retourne pas a Complex, mais l'attribuant à n'importe quel champ contenant l'objet présent. Dans l'esprit Scala de ne pas s'immiscer dans l'espace de noms des programmeurs, supposons que nous le fassions en préfixant le type avec @.

Alors ça pourrait être comme ça:

case class Complex(real: Double = 0, imaginary: Double = 0) {
  def ++: @Complex = {
    assign copy(real = real + 1)
    // instead of return copy(real = real + 1)
}

Le problème suivant est que les opérateurs postfixes sucent avec les règles Scala. Par exemple:

def inc(x: Int) = {
  x++
  x
}

En raison de Scala règles, c'est la même chose que:

def inc(x: Int) = { x ++ x }

Ce qui n'était pas l'intention. Maintenant, Scala privilégie un style fluide: obj method param method param method param .... Cela mélange bien la syntaxe traditionnelle C++/Java de object method parameter et le concept de programmation fonctionnelle qui consiste à mettre en pipeline une entrée via plusieurs fonctions pour obtenir le résultat final. Ce style a récemment été appelé "interfaces fluides".

Le problème est que, en privilégiant ce style, il paralyse les opérateurs postfixés (et les préfixes, mais Scala les a à peine de toute façon). Donc, à la fin, Scala devrait faire de grands changements et il serait capable de se mesurer à l'élégance des opérateurs d'incrémentation et de décrémentation de C/Java, à moins que cela ne s'écarte vraiment du genre de chose ne supporte .

33
Daniel C. Sobral

En Scala, ++ est une méthode valide et aucune méthode n’implique d’affectation. Seul = peut le faire.

Une réponse plus longue est que les langages comme C++ et Java traitent ++ spécialement, et Scala traite = spécialement et de manière incohérente.

Dans Scala, lorsque vous écrivez i += 1, le compilateur recherche d’abord une méthode appelée += sur Int. Ce n'est pas là, ensuite il fait son effet magique sur = et tente de compiler la ligne comme si elle lisait i = i + 1. Si vous écrivez i++, Scala appellera la méthode ++ sur i et affectera le résultat à ... rien. Parce que seul = signifie affectation. Vous pouvez écrire i ++= 1, mais ce genre de chose va à l’encontre du but recherché.

Le fait que Scala prenne en charge des noms de méthode tels que += fait déjà l’objet d’une controverse et certaines personnes pensent que cela est une surcharge d’opérateur. Ils auraient pu ajouter un comportement spécial pour ++, mais alors ce ne serait plus un nom de méthode valide (comme =) et ce serait une dernière chose à retenir.

19
Craig P. Motlin

Je pense que le raisonnement en partie est que +=1 n’est qu’un caractère supplémentaire, et ++ est assez utilisé dans le code de collection pour la concaténation. Donc, cela garde le code plus propre.

De plus, Scala encourage les variables immuables et ++ est intrinsèquement une opération de mutation. Si vous avez besoin de +=, vous pouvez au moins forcer toutes vos mutations à suivre une procédure d’attribution commune (par exemple, def a_=).

12
Rex Kerr

La raison principale est qu'il n'y a pas de besoin dans Scala, comme dans C. Dans C, vous êtes constamment:

for(i = 0, i < 10; i++)
{
  //Do stuff
}

C++ a ajouté des méthodes de niveau supérieur pour éviter les boucles explicites, mais Scala est allé encore plus loin en fournissant foreach, map, flatMap foldLeft, etc. , vous pouvez utiliser la gamme Scala.

(1 to 5) map (_ * 3) //Vector(3, 6, 9, 12, 15)
(1 to 10 by 3) map (_ + 5)//Vector(6, 9, 12, 15)

Étant donné que l'opérateur ++ est utilisé par la bibliothèque de collections, je pense qu'il est préférable d'éviter son utilisation dans des classes autres que des collections. J'avais l'habitude d'utiliser ++ comme méthode de renvoi de valeur dans mon objet de package de package Util, de la manière suivante: 

implicit class RichInt2(n: Int)
{      
  def isOdd: Boolean = if (n % 2 == 1) true else false
  def isEven: Boolean = if (n % 2 == 0) true else false
  def ++ : Int = n + 1
  def -- : Int = n - 1     
}

Mais je l'ai enlevé. La plupart du temps, lorsque j'ai utilisé ++ ou +1 sur un entier, j'ai par la suite trouvé un meilleur moyen, qui ne l'exige pas. 

2
Rich Oliver

Bien sûr, vous pouvez avoir ça à Scala, si vous voulez vraiment:

import scalaz._
import Scalaz._

case class IncLens[S,N](lens: Lens[S,N], num : Numeric[N]) { 
  def ++ = lens.mods(num.plus(_, num.one))
}

implicit def incLens[S,N:Numeric](lens: Lens[S,N]) =
  IncLens[S,N](lens, implicitly[Numeric[N]])

val i = Lens[Int,Int](identity, (x, y) => y)

val imperativeProgram = for {
  _ <- i := 0;
  _ <- i++;
  _ <- i++;
  x <- i++
} yield x

def runProgram = imperativeProgram ! 0

Et voilà:

scala> runProgram
runProgram: Int = 3
2
Apocalisp

Cela est possible si vous définissez votre propre classe qui peut simuler la sortie désirée, mais cela peut être un problème si vous souhaitez utiliser les méthodes normales "Int", car vous devez toujours utiliser * ()

import scala.language.postfixOps //otherwise it will throw warning when trying to do num++

/*
 * my custom int class which can do ++ and --
 */
class int(value: Int) {

  var mValue = value

  //Post-increment
  def ++(): int = {

    val toReturn = new int(mValue)
    mValue += 1
    return toReturn 
  }

  //Post-decrement
  def --(): int = {

    val toReturn = new int(mValue)
    mValue -= 1
    return toReturn 
  }

  //a readable toString
  override def toString(): String = {
      return mValue.toString
  }
}

//Pre-increment
def ++(n: int): int = {
  n.mValue += 1
  return n;
}

//Pre-decrement
def --(n: int): int = {
  n.mValue -= 1
  return n;
}

//Something to get normal Int
def *(n: int): Int = {
  return n.mValue
}

Quelques cas de test possibles

scala>var num = new int(4)
num: int = 4

scala>num++
res0: int = 4

scala>num
res1: int = 5 // it works although scala always makes new resources

scala>++(num) //parentheses are required
res2: int = 6

scala>num
res3: int = 6

scala>++(num)++ //complex function
res4: int = 7

scala>num
res5: int = 8

scala>*(num) + *(num) //testing operator_*
res6: Int = 16
2
Dev_anon101

Permet de définir une var: 

var i = 0

++ i est déjà assez court: 

{i+=1;i}

Maintenant, i ++ peut ressembler à ceci: 

i(i+=1)

Pour utiliser la syntaxe ci-dessus, définissez quelque part dans un objet de package, puis importez: 

class IntPostOp(val i: Int) { def apply(op: Unit) = { op; i } } 
implicit def int2IntPostOp(i: Int): IntPostOp = new IntPostOp(i)

Le chaînage des opérateurs est également possible:

i(i+=1)(i%=array.size)(i&=3)

L'exemple ci-dessus est similaire à ce code Java (C++?): 

i=(i=i++ %array.length)&3;

Le style pourrait dépendre, bien sûr.

0
idonnie