为什么在 Scala 中没有 i++?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4520463/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why no i++ in Scala?
提问by xiaowl
I just wonder why there is no i++to increase a number. As what I know, languages like Ruby or Python doesn't support it because they are dynamically typed. So it's obviously we cannot write code like i++because maybe iis a string or something else. But Scala is statically typed - the compiler absolutely can infer that if it is legal or not to put ++behind a variable.
我只是想知道为什么没有i++增加一个数字。据我所知,像 Ruby 或 Python 这样的语言不支持它,因为它们是动态类型的。所以很明显我们不能写代码,i++因为也许i是一个字符串或其他东西。但是 Scala 是静态类型的——编译器绝对可以推断出将++变量放在后面是否合法。
So, why doesn't i++exist in Scala?
那么,为什么i++在 Scala中不存在?
回答by Rafe Kettler
Scala doesn't have i++because it's a functional language, and in functional languages, operations with side effects are avoided (in a purely functional language, no side effects are permitted at all). The side effect of i++is that iis now 1 larger than it was before. Instead, you should try to use immutable objects (e.g. valnot var).
Scala 没有,i++因为它是一种函数式语言,并且在函数式语言中,避免了有副作用的操作(在纯函数式语言中,根本不允许有副作用)。的副作用i++是,i现在是1比以前更大。相反,您应该尝试使用不可变对象(例如valnot var)。
Also, Scala doesn't really need i++because of the control flow constructs it provides. In Java and others, you need i++often to construct whileand forloops that iterate over arrays. However, in Scala, you can just say what you mean: for(x <- someArray)or someArray.foreachor something along those lines. i++is useful in imperative programming, but when you get to a higher level, it's rarely necessary (in Python, I've never found myself needing it once).
此外,Scala 并不真正需要,i++因为它提供了控制流结构。在 Java 和其他语言中,您i++经常需要构造while和for循环遍历数组。然而,在Scala中,你可以说你的意思:for(x <- someArray)或someArray.foreach或类似的规定。i++在命令式编程中很有用,但是当你达到更高的水平时,它很少有必要(在 Python 中,我从来没有发现自己需要它一次)。
You're spot on that ++couldbe in Scala, but it's not because it's not necessary and would just clog up the syntax. If you really need it, say i += 1, but because Scala calls for programming with immutables and rich control flow more often, you should rarely need to. You certainly could define it yourself, as operators are indeed just methods in Scala.
你发现这++可能在 Scala 中,但这不是因为它没有必要,只会阻塞语法。如果你真的需要它,比如说i += 1,但是因为 Scala 更频繁地调用使用不可变和丰富的控制流进行编程,你应该很少需要。您当然可以自己定义它,因为运算符确实只是 Scala 中的方法。
回答by Apocalisp
Of course you can have that in Scala, if you really want:
当然,如果你真的想要,你可以在 Scala 中使用它:
import scalaz._, 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.lensu[Int,Int]((x, y) => y, identity)
val imperativeProgram = for {
_ <- i++;
_ <- i++;
x <- i++
} yield x
def runProgram = imperativeProgram exec 0
And here you go:
给你:
scala> runProgram
res26: scalaz.Id.Id[Int] = 3
No need to resort to violence against variables.
无需对变量诉诸暴力。
回答by Rex Kerr
Scala is perfectly capable of parsing i++and, with a small modification to the language, could be made to modify a variable. But there are a variety of reasons not to.
Scala 具有完美的解析能力i++,只要对语言稍作修改,就可以修改变量。但不这样做的原因有很多。
First, it saves only one character, i++vs. i+=1, which is not very much savings for adding a new language feature.
首先,它只节省了一个字符,i++而不是i+=1,这对于添加新的语言功能来说并没有节省多少。
Second, the ++operator is widely used in the collections library, where xs ++ ystakes collection xsand ysand produces a new collection that contains both.
其次,++运营商被广泛应用于集合库,其中xs ++ ys需要收集xs和ys并产生包含一个新的集合。
Third, Scala tries to encourage you, without forcing you, to write code in a functional way. i++is a mutable operation, so it's inconsistent with the idea of Scala to make it especially easy. (Likewise with a language feature that would allow ++to mutate a variable.)
第三,Scala 试图鼓励您而不是强迫您以函数式方式编写代码。 i++是一个可变操作,所以它与Scala的想法不一致,使它特别容易。(同样具有允许++改变变量的语言功能。)
回答by Daniel C. Sobral
Scala doesn't have a ++operator because it is not possible to implement one in it.
Scala 没有++运算符,因为它不可能在其中实现。
EDIT: As just pointed out in response to this answer, Scala 2.10.0 canimplement an increment operator through use of macros. See this answerfor details, and take everything below as being pre-Scala 2.10.0.
编辑:正如刚刚针对这个答案指出的那样,Scala 2.10.0可以通过使用宏来实现增量运算符。有关详细信息,请参阅此答案,并将以下所有内容视为 Scala 2.10.0 之前的版本。
Let me elaborate on this, and I'll rely heavily on Java, since it actually suffers from the same problem, but it might be easier for people to understand it if I use a Java example.
让我详细说明一下,我将非常依赖 Java,因为它实际上遇到了同样的问题,但如果我使用 Java 示例,人们可能更容易理解它。
To start, it is important to note that one of the goals of Scala is that the "built-in" classes must not have any capability that could not be duplicated by a library. And, of course, in Scala an Intis a class, whereas in Java an intis a primitive -- a type entirely distinct from a class.
首先,重要的是要注意 Scala 的目标之一是“内置”类不得具有库无法复制的任何功能。而且,当然,在 Scala 中 anInt是一个类,而在 Java 中 anint是一个原始类型——一种完全不同于类的类型。
So, for Scala to support i++for iof type Int, I should be able to create my own class MyIntalso supporting the same method. This is one of the driving design goals of Scala.
所以,斯卡拉支持i++的i类型Int,我应该能够创建自己的类MyInt也支持同样的方法。这是 Scala 的驱动设计目标之一。
Now, naturally, Java does not support symbols as method names, so let's just call it incr(). Our intent then is to try to create a method incr()such that y.incr()works just like i++.
现在,自然而然地,Java 不支持符号作为方法名称,所以我们就将其称为incr()。我们的目的是尝试创建一个方法,incr()这样y.incr()工作就像i++.
Here's a first pass at it:
这是它的第一遍:
public class Incrementable {
private int n;
public Incrementable(int n) {
this.n = n;
}
public void incr() {
n++;
}
@Override
public String toString() {
return "Incrementable("+n+")";
}
}
We can test it with this:
我们可以用这个来测试它:
public class DemoIncrementable {
static public void main(String[] args) {
Incrementable i = new Incrementable(0);
System.out.println(i);
i.incr();
System.out.println(i);
}
}
Everything seems to work, too:
一切似乎也都有效:
Incrementable(0)
Incrementable(1)
And, now, I'll show what the problem is. Let's change our demo program, and make it compare Incrementableto int:
现在,我将展示问题所在。让我们改变我们的演示程序,并把它比Incrementable到int:
public class DemoIncrementable {
static public void main(String[] args) {
Incrementable i = new Incrementable(0);
Incrementable j = i;
int k = 0;
int l = 0;
System.out.println("i\t\tj\t\tk\tl");
System.out.println(i+"\t"+j+"\t"+k+"\t"+l);
i.incr();
k++;
System.out.println(i+"\t"+j+"\t"+k+"\t"+l);
}
}
As we can see in the output, Incrementableand intare behaving differently:
正如我们在输出中看到,Incrementable并且int是表现不同:
i j k l
Incrementable(0) Incrementable(0) 0 0
Incrementable(1) Incrementable(1) 1 0
The problem is that we implemented incr()by mutating Incrementable, which is not how primitives work. Incrementableneeds to be immutable, which means that incr()must produce a newobject. Let's do a naive change:
问题是我们是incr()通过 mutating实现的Incrementable,这不是原语的工作方式。Incrementable需要是不可变的,这意味着incr()必须产生一个新对象。让我们做一个天真的改变:
public Incrementable incr() {
return new Incrementable(n + 1);
}
However, this doesn't work:
但是,这不起作用:
i j k l
Incrementable(0) Incrementable(0) 0 0
Incrementable(0) Incrementable(0) 1 0
The problem is that, while, incr()createda new object, that new object hasn't been assignedto i. There's no existing mechanism in Java -- or Scala -- that would allow us to implement this method with the exact same semantics as ++.
问题是,虽然incr()创建了一个新对象,但该新对象尚未分配给i. Java(或 Scala)中没有现有机制允许我们使用与++.
Now, that doesn't mean it would be impossible for Scala to make such a thing possible. If Scala supported parameter passing by reference (see "call by reference" in this wikipedia article), like C++ does, then we couldimplement it!
现在,这并不意味着 Scala 不可能使这样的事情成为可能。如果 Scala 支持按引用传递参数(请参阅此维基百科文章中的“按引用调用” ),就像 C++ 一样,那么我们就可以实现它!
Here's a fictitious implementation, assuming the same by-reference notation as in C++.
这是一个虚构的实现,假设与 C++ 中的引用符号相同。
implicit def toIncr(Int &n) = {
def ++ = { val tmp = n; n += 1; tmp }
def prefix_++ = { n += 1; n }
}
This would either require JVM support or some serious mechanics on the Scala compiler.
这要么需要 JVM 支持,要么需要 Scala 编译器上的一些严格机制。
In fact, Scala doessomething similar to what would be needed that when it create closures -- and one of the consequences is that the original Intbecomes boxed, with possibly serious performance impact.
事实上,Scala做了一些类似于创建闭包时所需要的事情——结果之一是原始文件Int被装箱,可能会严重影响性能。
For example, consider this method:
例如,考虑这个方法:
def f(l: List[Int]): Int = {
var sum = 0
l foreach { n => sum += n }
sum
}
The code being passed to foreach, { n => sum += n }, is notpart of this method. The method foreachtakes an objectof the type Function1whose applymethod implements that little code. That means { n => sum += n }is not only on a different method, it is on a different class altogether! And yet, it can change the value of sumjust like a ++operator would need to.
该代码被传递到foreach,{ n => sum += n }是不是该方法的一部分。该方法foreach采用一个对象类型Function1,该对象的apply方法实现了那个小代码。这意味着{ n => sum += n }不仅在不同的方法上,而且在完全不同的类上!然而,它可以sum像++操作员需要的那样更改 的值。
If we use javapto look at it, we'll see this:
如果我们javap过去看它,我们会看到:
public int f(scala.collection.immutable.List);
Code:
0: new #7; //class scala/runtime/IntRef
3: dup
4: iconst_0
5: invokespecial #12; //Method scala/runtime/IntRef."<init>":(I)V
8: astore_2
9: aload_1
10: new #14; //class tst$$anonfun$f
13: dup
14: aload_0
15: aload_2
16: invokespecial #17; //Method tst$$anonfun$f."<init>":(Ltst;Lscala/runtime/IntRef;)V
19: invokeinterface #23, 2; //InterfaceMethod scala/collection/LinearSeqOptimized.foreach:(Lscala/Function1;)V
24: aload_2
25: getfield #27; //Field scala/runtime/IntRef.elem:I
28: ireturn
Note that instead of creating an intlocal variable, it creates an IntRefon the heap (at 0), which is boxing the int. The real intis inside IntRef.elem, as we see on 25. Let's see this same thing implemented with a while loop to make clear the difference:
请注意,它不是创建int局部变量,而是IntRef在堆上(在 0 处)创建了一个,这将int. 正如我们在 25 上看到的那样,真实int在 inside 中IntRef.elem。让我们看一下用 while 循环实现的相同内容,以明确区别:
def f(l: List[Int]): Int = {
var sum = 0
var next = l
while (next.nonEmpty) {
sum += next.head
next = next.tail
}
sum
}
That becomes:
那变成:
public int f(scala.collection.immutable.List);
Code:
0: iconst_0
1: istore_2
2: aload_1
3: astore_3
4: aload_3
5: invokeinterface #12, 1; //InterfaceMethod scala/collection/TraversableOnce.nonEmpty:()Z
10: ifeq 38
13: iload_2
14: aload_3
15: invokeinterface #18, 1; //InterfaceMethod scala/collection/IterableLike.head:()Ljava/lang/Object;
20: invokestatic #24; //Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
23: iadd
24: istore_2
25: aload_3
26: invokeinterface #29, 1; //InterfaceMethod scala/collection/TraversableLike.tail:()Ljava/lang/Object;
31: checkcast #31; //class scala/collection/immutable/List
34: astore_3
35: goto 4
38: iload_2
39: ireturn
No object creation above, no need to get something from the heap.
上面没有对象创建,不需要从堆中获取一些东西。
So, to conclude, Scala would need additional capabilities to support an increment operator that could be defined by the user, as it avoids giving its own built-in classes capabilities not available to external libraries. One such capability is passing parameters by-reference, but JVM does not provide support for it. Scala does something similar to call by-reference, and to do so it uses boxing, which would seriously impact performance (something that would most likely come up with an increment operator!). In the absence of JVM support, therefore, that isn't much likely.
因此,总而言之,Scala 需要额外的功能来支持可由用户定义的增量运算符,因为它避免了提供外部库无法使用的自己的内置类功能。其中一项功能是通过引用传递参数,但 JVM 不提供支持。Scala 执行类似于按引用调用的操作,为此它使用装箱,这会严重影响性能(这很可能会产生增量运算符!)。因此,在没有 JVM 支持的情况下,这不太可能。
As an additional note, Scala has a distinct functional slant, privileging immutability and referential transparency over mutability and side effects. The solepurpose of call by-reference is to cause side effects on the caller! While doing so can bring performance advantages in a number of situations, it goes very much against the grain of Scala, so I doubt call by-reference will ever be part of it.
作为附加说明,Scala 具有明显的功能倾斜,优先于不变性和引用透明性,而不是可变性和副作用。该唯一通过引用调用的目的是为了引起副作用呼叫者!虽然这样做可以在许多情况下带来性能优势,但它与 Scala 的本质背道而驰,所以我怀疑按引用调用是否会成为其中的一部分。
回答by Kim Stebel
Other answers have already correctly pointed out that a ++operator is neither particularly useful nor desirable in a functional programming language. I would like to add that since Scala 2.10, you can add a ++operator, if you want to. Here is how:
其他答案已经正确指出,++运算符在函数式编程语言中既不是特别有用也不是可取的。我想补充一点,从 Scala 2.10 开始++,如果你愿意,你可以添加一个运算符。方法如下:
You need an implicit macro that converts ints to instances of something that has a ++method. The ++method is "written" by the macro, which has access to the variable (as opposed to its value) on which the ++method is called. Here is the macro implementation:
您需要一个隐式宏,将整数转换为具有++方法的事物的实例。该++方法由宏“编写”,宏可以访问++调用该方法的变量(与其值相反)。下面是宏实现:
trait Incrementer {
def ++ : Int
}
implicit def withPp(i:Int):Incrementer = macro withPpImpl
def withPpImpl(c:Context)(i:c.Expr[Int]):c.Expr[Incrementer] = {
import c.universe._
val id = i.tree
val f = c.Expr[()=>Unit](Function(
List(),
Assign(
id,
Apply(
Select(
id,
newTermName("$plus")
),
List(
Literal(Constant(1))
)
)
)
))
reify(new Incrementer {
def ++ = {
val res = i.splice
f.splice.apply
res
}
})
}
Now, as long as the implicit conversion macro is in scope, you can write
现在,只要隐式转换宏在范围内,您就可以编写
var i = 0
println(i++) //prints 0
println(i) //prints 1
回答by Craig P. Motlin
Rafe's answer is true about the rationale for why something like i++ doesn't belong in Scala. However I have one nitpick. It's actually not possible to implement i++ in Scala without changing the language.
关于为什么像 i++ 这样的东西不属于 Scala 的理由,Rafe 的回答是正确的。但是我有一个挑剔。实际上不可能在不改变语言的情况下在 Scala 中实现 i++。
In Scala, ++ is a valid method, and no method implies assignment. Only =can do that.
在 Scala 中,++ 是一个有效的方法,没有任何方法意味着赋值。只能=这样做。
Languages like C++ and Java treat ++specially to mean both increment and assign. Scala treats =specially, and in an inconsistent way.
像 C++ 和 Java 这样的语言++特别对待意味着增加和分配。Scala=以一种特殊的、不一致的方式对待。
In Scala when you write i += 1the compiler first looks for a method called +=on the Int. It's not there so next it does it's magic on =and tries to compile the line as if it read i = i + 1. If you write i++then Scala will call the method ++on iand assign the result to... nothing. Because only =means assignment. You could write i ++= 1but that kind of defeats the purpose.
在 Scala 中,当您编写i += 1编译器时,首先会查找+=Int 上调用的方法。它不在那里,所以接下来它会变魔术=并尝试编译该行,就好像它读取i = i + 1. 如果你写i++那么斯卡拉将调用方法++上i和结果分配给什么都没有。因为只=意味着赋值。你可以写,i ++= 1但那种违背了目的。
The fact that Scala supports method names like +=is already controversial and some people think it's operator overloading. They could have added special behavior for ++but then it would no longer be a valid method name (like =) and it would be one more thing to remember.
Scala 支持像这样的方法名称这一事实+=已经引起争议,有些人认为这是运算符重载。他们本可以为++它添加特殊行为,但它不再是有效的方法名称(如=),而且要记住另一件事。
回答by Puppy
Quite a few languages do not support the ++ notation, such as Lua. In languages in which it is supported, it is frequently a source of confusion and bugs, so it's quality as a language feature is dubious, and compared to the alternative of i += 1or even just i = i + 1, the saving of such minor characters is fairly pointless.
不少语言不支持++表示法,比如Lua。在支持它的语言中,它经常是混乱和错误的来源,因此它作为语言特性的质量是可疑的,与 的替代品i += 1甚至只是 相比i = i + 1,保存这种次要字符是相当没有意义的。
This is not at all relevant to the type system of the language. While it's true that most static type languages do offer and most dynamic types don't, that's a correlation and definitely not a cause.
这与语言的类型系统完全无关。虽然大多数静态类型语言确实提供而大多数动态类型没有提供,但这是一种相关性,绝对不是原因。
回答by fehu
Scala encourages using of FP style, which i++certainly is not.
Scala 鼓励使用 FP 风格,这i++当然不是。
回答by David Thornley
The question to ask is why there should be such an operator, not why there shouldn't be. Would Scala be improved by it?
要问的问题是为什么应该有这样的运营商,而不是为什么不应该有。Scala 会因此得到改进吗?
The ++operator is single-purpose, and having an operator that can change the value of a variable can cause problems. It's easy to write confusing expressions, and even if the language defines what i = i + i++means, for example, that's a lot of detailed rules to remember.
的++操作者是单一用途,以及具有操作者能够改变一个变量的值可能会导致问题。编写令人困惑的表达式很容易i = i + i++,例如,即使语言定义了含义,也需要记住很多详细规则。
Your reasoning on Python and Ruby is wrong, by the way. In Perl, you can write $i++or ++$ijust fine. If $iturns out to be something that can't be incremented, you get a run-time error. It isn't in Python or Ruby because the language designers didn't think it was a good idea, not because they're dynamically typed like Perl.
顺便说一下,您对 Python 和 Ruby 的推理是错误的。在 Perl 中,您可以编写$i++或++$i很好。如果$i结果是无法递增的内容,则会出现运行时错误。它不在 Python 或 Ruby 中,因为语言设计者认为这不是一个好主意,而不是因为它们像 Perl 一样是动态类型的。
回答by Knut Arne Vedaa
You could simulate it, though. As a trivial example:
不过你可以模拟一下。作为一个简单的例子:
scala> case class IncInt(var self: Int = 0) { def ++ { self += 1 } }
defined class IncInt
scala> val i = IncInt()
i: IncInt = IncInt(0)
scala> i++
scala> i++
scala> i
res28: IncInt = IncInt(2)
Add some implicit conversions and you're good to go. However, this sort of changes the question into: why isn't there a mutable RichInt with this functionality?
添加一些隐式转换,您就可以开始了。然而,这种类型的问题变成了:为什么没有具有此功能的可变 RichInt?

