web-dev-qa-db-ja.com

Scalaのインクリメント(++)演算子

Scalaデフォルトでプリミティブ型をインクリメントする++演算子をサポートしていない理由はありますか?たとえば、次のように書くことはできません。

var i=0
i++

ありがとう

54
adelarsq

私の推測では、これは変更可能な変数に対してのみ機能し、不変の値に対しては意味をなさないため、省略されたと思われます。おそらく++演算子は代入を叫ぶことはありません。そのため、これを含めると、変数を変更しているかどうかに関して間違いが生じる可能性があります。

私はこのようなことが安全であると感じています(1行で):

i++

しかし、これは(どの言語でも)悪い習慣です。

var x = i++

割り当てステートメントと副作用/突然変異を混在させたくありません。

34
pkaeding

私は Craiganswer が好きですが、その点をもっと強くする必要があると思います。

  1. 「プリミティブ」はありません-Intができれば、ユーザー作成のComplexもできます(たとえば)。

  2. _++_の基本的な使用法は次のようになります。

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

    _x++_

  3. クラスComplexに_++_をどのように実装しますか? Intのように、オブジェクトが不変であると仮定すると、_++_メソッドはnewオブジェクトを返す必要がありますが、その新しいオブジェクトはassigned

新しい言語機能が必要です。たとえば、assignキーワードを作成するとします。 _++_がreturningではなくComplexではなくであることを示すために、型シグネチャも変更する必要があります。現在のオブジェクトを保持しているフィールドに割り当てます。プログラマの名前空間に侵入しないというScala精神では、タイプの前に_@_を付けることでそれを行うとしましょう。

それは次のようになります:

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

次の問題は、接尾辞演算子がScalaルールを使用することです。例えば:

_def inc(x: Int) = {
  x++
  x
}
_

Scalaルールのため、それは次と同じです:

_def inc(x: Int) = { x ++ x }
_

これは意図ではありませんでした。これで、Scalaがフロースタイルに特権を与えます:_obj method param method param method param ..._。これは、C++/Javaの従来の_object method parameter_の構文と、最終結果を得るために複数の関数を介して入力をパイプライン化する関数型プログラミングの概念をうまく組み合わせています。このスタイルは最近「流れるようなインターフェース」とも呼ばれています。

問題は、そのスタイルを特権化することで、後置演算子(および前置演算子を無効にしますが、Scala_はほとんどありません)です。そのため、最終的にScalaは大きな変更を行う必要があり、C/Javaの増分演算子と減分演算子の優雅さまで測定することができます。 Thing itdoessupport。

33

理由の一部は、+=1がもう1つだけの文字であり、++が連結用のコレクションコードでかなり頻繁に使用されるためだと思います。そのため、コードがきれいになります。

また、Scalaは不変の変数を推奨し、++は本質的に変更操作です。+=が必要な場合は、少なくともすべての変更を強制的に共通の割り当て手順(たとえばdef a_=)に進めることができます)。

12
Rex Kerr

主な理由は、CのようにScalaに必要がないことです。Cでは、常に次のようになります。

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

C++は、明示的なループを回避するための高レベルのメソッドを追加しましたが、Scalaはさらにforeach、map、flatMap foldLeftなどを提供します。実際に整数のシーケンスではなく、非整数オブジェクトのコレクションを循環するだけで、Scala range。

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

++演算子はコレクションライブラリで使用されるため、コレクション以外のクラスでは使用しないほうが良いと思います。私はUtilパッケージパッケージオブジェクトの値を返すメソッドとして++を使用していました。

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     
}

しかし、私はそれを削除しました。整数で++または+ 1を使用した場合、ほとんどの場合、後でそれを必要としないより良い方法を見つけました。

2
Rich Oliver

もちろん、本当に必要な場合は、Scalaで使用できます。

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

そしてここに行きます:

scala> runProgram
runProgram: Int = 3
2
Apocalisp

目的の出力をシミュレートできる独自のクラスを定義することは可能ですが、常に*()を使用する必要があるため、通常の「Int」メソッドも使用する場合は苦痛になる場合があります。

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
}

いくつかの可能なテストケース

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

変数を定義しましょう:

var i = 0

++ iはすでに十分に短い:

{i+=1;i}

これで、i ++は次のようになります。

i(i+=1)

上記の構文を使用するには、パッケージオブジェクト内のどこかに定義してからインポートします。

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

演算子の連鎖も可能です。

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

上記の例は、このJava(C++?)コードに似ています:

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

もちろん、スタイルは依存する可能性があります。

0
idonnie