Scala中的递增(++)运算符

时间:2010-10-21 22:09:15

标签: scala increment

Scala是否有任何理由不支持++运算符默认增加基元类型? 例如,你不能写:

var i=0
i++

由于

9 个答案:

答案 0 :(得分:34)

我猜这是省略的,因为它只适用于可变变量,对于不可变值没有意义。也许决定++运算符不会尖叫赋值,因此包含它可能会导致错误,无论您是否正在变量变量。

我觉得这样的事情是安全的(在一条线上):

i++

但这是一种不好的做法(用任何语言):

var x = i++

您不希望混合赋值语句和副作用/变异。

答案 1 :(得分:33)

我喜欢Craiganswer,但我认为必须更加强烈。

  1. 没有“原语” - 如果Int可以做到,那么用户制造的Complex也是如此(例如)。

  2. ++的基本用法如下:

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

    x++

  3. 如何在课程++中实施Complex?假设像Int一样,对象是不可变的,那么++方法需要返回 new 对象,但是新对象必须分配

  4. 这需要一种新的语言功能。例如,假设我们创建了一个assign关键字。类型签名也需要更改,以指示++ 返回 a 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 ...。这很好地混合了object method parameter的C ++ / Java传统语法和通过多个函数管道输入以获得最终结果的函数式编程概念。这种风格最近也称为“流畅的界面”。

    问题在于,通过对该样式进行特权保护,它会削弱后缀运算符(并且前缀为1,但Scala几乎没有它们)。所以,最后,Scala必须做出重大改变,并且无论如何都能够衡量C / Java增量和减量运算符的优雅 - 除非它真的偏离了所做的事情。 支持。

答案 2 :(得分:19)

在Scala中,++是一种有效的方法,没有方法意味着赋值。只有=可以做到这一点。

更长的答案是,像C ++和Java这样的语言专门处理++,而Scala特别以不一致的方式处理=

在Scala中编写i += 1时,编译器首先在Int上查找名为+=的方法。它不存在,所以接下来它会在=上发挥作用,并尝试编译该行,就像它读取i = i + 1一样。如果你写i++,那么Scala会调用++上的方法i,并将结果分配给......什么都没有。因为只有=表示分配。你可以写i ++= 1,但那种方法会失败。

Scala支持像+=这样的方法名称的事实已经引起争议,有些人认为它是运算符重载。他们本可以为++添加特殊行为,但它不再是一个有效的方法名称(如=),这将是另一件需要记住的事情。

答案 3 :(得分:12)

我认为部分原因是+=1只有一个字符,而++在集合代码中用得很多,用于连接。所以它使代码更清晰。

此外,Scala鼓励不可变变量,++本质上是一个变异操作。如果您需要+=,至少可以强制所有突变通过一个共同的分配程序(例如def a_=)。

答案 4 :(得分:2)

当然,如果你真的想要,你可以在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

答案 5 :(得分:2)

主要原因是Scala没有必要,就像C中一样。在C中,你经常这样:

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

C ++已经添加了更高级别的方法来避免显式循环,但Scala已经进一步提供foreach,map,flatMap foldLeft等。即使你真的想要操作一系列整数而不是仅仅循环通过一个集合非整数对象,可以使用Scala范围。

(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时,我后来发现了一种更好的方法,它不需要它。

答案 6 :(得分:2)

如果您定义自己的类可以模拟所需的输出,那么如果您想要使用普通的“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

答案 7 :(得分:0)

让我们定义一个var:

var i = 0

++我已经足够短了:

{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;

当然,风格可能取决于。

答案 8 :(得分:-1)

它不包括在内,因为Scala开发人员认为它使规范更加复杂,同时只能获得可忽略不计的好处,并且因为Scala根本没有运营商。

你可以这样写自己的:

class PlusPlusInt(i: Int){
  def ++ = i+1
  }

implicit def int2PlusPlusInt(i: Int) = new PlusPlusInt(i)

val a = 5++
// a is 6

但是我相信你会遇到一些优先事项,而不是按你的预期工作。另外,如果添加i ++,人们也会要求++ i,这并不适合Scala的语法。