Scala 中的递增 (++) 运算符
Posted
技术标签:
【中文标题】Scala 中的递增 (++) 运算符【英文标题】:Increment (++) operator in Scala 【发布时间】:2011-04-28 21:20:43 【问题描述】:Scala 是否有任何理由不支持 ++ 运算符默认增加原始类型? 例如,你不能写:
var i=0
i++
谢谢
【问题讨论】:
只是好奇,你能在 Scala 中发出 i += 1 吗? 是的,你可以,但前提是它是var
而不是 val
。当 Scala 编译器发现在一个 var 上调用了一个以 = 结尾的方法并且该类没有该方法 (variable method= arg
) 时,它会将其扩展为 variable = variable.method(arg)
。
这是 Note that Java's ++i and i++ don't work in Scala. To increment in Scala, you need to say either i = i + 1 or i += 1
如果你定义你自己的类来模拟所需的输出是可能的,但是如果你想使用普通的“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】:主要原因是在 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 时,我后来发现了一种更好的方法,它不需要它。
【讨论】:
【参考方案3】:让我们定义一个变量:
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;
当然,风格可能取决于。
【讨论】:
这没有回答问题,也没有提供使用 Scala 的良好风格。请在您的回答中提及这一点。 运算符链接似乎是 Scala 方式的一个漂亮应用,我认为。如果您能更准确地指出可能的陷阱,我很乐意更新我的答案。【参考方案4】:我的猜测是这被省略了,因为它只适用于可变变量,而对于不可变值没有意义。也许已经决定 ++
运算符不会尖叫赋值,因此包含它可能会导致您是否正在改变变量的错误。
我觉得这样做是安全的(单行):
i++
但这将是一种不好的做法(在任何语言中):
var x = i++
您不想混合赋值语句和副作用/突变。
【讨论】:
那么我猜你不是 C/C++ 的忠实粉丝。那里有各种各样的*(a++) = ++*(b++)
东西......
@Rex Kerr : 现在我觉得我的第一门编程语言是 Java 很好:)
我是一名 C++ 首席开发人员:如果我能提供帮助,我的代码库中就没有这些废话了。
“你不想混合赋值语句和副作用/突变。”是的,我喜欢,哈哈,我讨厌这种语言【参考方案5】:
当然,如果你真的想要,你可以在 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
【讨论】:
当心:如果你想给 C 或 Java 程序员留下深刻印象,你需要更加努力。在这个序列中,x
的值应该是 2,而不是 3,因为后缀 ++
是后增量。给这群人留下深刻印象的挑战是:您能否实现前缀和后缀形式以在可变数字上按预期运行? ;)【参考方案6】:
我喜欢Craig 的answer,但我认为必须更加强调这一点。
没有“原语”——如果Int
可以做到,那么用户制作的Complex
也可以(例如)。
++
的基本用法如下:
var x = 1 // or Complex(1, 0)
x++
如何在Complex
类中实现++
?假设像Int
一样,对象是不可变的,那么++
方法需要返回一个new对象,但是这个新对象必须是赋值的。
这需要新的语言功能。例如,假设我们创建了一个 assign
关键字。类型签名也需要更改,以表明++
不是返回 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 传统语法与通过多个函数流水线化输入以获得最终结果的函数式编程概念很好地结合在一起。这种风格最近也被称为“流畅的界面”。
问题在于,通过赋予这种风格特权,它削弱了后缀运算符(和前缀运算符,但无论如何 Scala 几乎没有它们)。所以,到最后,Scala 将不得不做出大的改变,而且无论如何它都能够与 C/Java 的递增和递减运算符的优雅相媲美——除非它真的背离了它所做的那种事情 支持。
【讨论】:
【参考方案7】:在 Scala 中,++ 是有效的方法,没有方法意味着赋值。只有=
可以做到这一点。
更长的答案是,像 C++ 和 Java 这样的语言特别对待 ++
,而 Scala 特别对待 =
,而且方式不一致。
在 Scala 中,当您编写 i += 1
时,编译器首先会在 Int 上查找名为 +=
的方法。它不在那里,所以接下来它在=
上很神奇,并尝试编译该行,就好像它读取i = i + 1
。如果你写 i++
,那么 Scala 将在 i
上调用方法 ++
并将结果分配给......什么都没有。因为只有=
表示赋值。你可以写i ++= 1
,但这样就达不到目的了。
Scala 支持像+=
这样的方法名称这一事实已经存在争议,有些人认为这是运算符重载。他们本可以为 ++
添加特殊行为,但随后它将不再是有效的方法名称(如 =
),而且还需要记住一件事。
【讨论】:
【参考方案8】:不包括在内是因为 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 的语法。
【讨论】:
我想你可以添加一个++:
方法来支持前缀运算符,比如val a = ++:5
。但我想这看起来有点奇怪。
坏主意。这不适用于 i++
应该工作的规范方式——i++
应该等同于 val temp = i; i += 1; temp
。
我也不是说这是个好主意!在我看来,Scala 的语言设计非常完美(不包括自动将整数转换为浮点数之类的****-ups),在实践中使用+=
更具可读性。
@Rex Kerr:但这只是一个小问题。主要的是你不能做var a = 5; a++; assert(a == 6)
因为a
本身实际上并没有改变。
@Debilski - 我认为我的回答涵盖了这一点。我想这太不透明了,因为不止一件事是错的。【参考方案9】:
我认为部分原因是+=1
只是多了一个字符,而++
在集合代码中大量用于连接。所以它使代码更干净。
此外,Scala 鼓励不可变变量,++
本质上是一种变异操作。如果你需要+=
,至少你可以强制你的所有突变都通过一个通用的分配过程(例如def a_=
)。
【讨论】:
我同意 - 你不需要使用 ++。经过 1.5 年的经验,我才发现这个运算符不可用,这只是因为我试图混淆某些东西来惹恼我的同行。以上是关于Scala 中的递增 (++) 运算符的主要内容,如果未能解决你的问题,请参考以下文章