为什么没有我++在斯卡拉?

发布于 2024-10-09 10:30:53 字数 249 浏览 1 评论 0原文

我只是想知道为什么没有 i++ 来增加数字。据我所知,Ruby 或 Python 等语言不支持它,因为它们是动态类型的。所以很明显我们不能编写像 i++ 这样的代码,因为 i 可能是一个字符串或其他东西。但 Scala 是静态类型的——编译器绝对可以推断出将 ++ 放在变量后面是否合法。

那么,为什么 Scala 中不存在 i++ 呢?

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 i is 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.

So, why doesn't i++ exist in Scala?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

顾铮苏瑾 2024-10-16 10:30:53

Scala 没有 i++ 因为它是一种函数式语言,并且在函数式语言中,避免了有副作用的操作(在纯函数式语言中,根本不允许有任何副作用)。 i++ 的副作用是 i 现在比以前大 1。相反,您应该尝试使用不可变对象(例如 val 而不是 var)。

此外,Scala 并不真正需要 i++,因为它提供了控制流结构。在 Java 和其他语言中,您经常需要 i++ 来构造迭代数组的 whilefor 循环。然而,在 Scala 中,您可以直接说出您的意思:for(x <- someArray)someArray.foreach 或类似的内容。 i++ 在命令式编程中很有用,但是当你达到更高的水平时,它很少是必要的(在 Python 中,我从来没有发现自己需要它一次)。

您发现 ++ 可以 出现在 Scala 中,但这并不是因为它没有必要,而且只会阻塞语法。如果您确实需要它,请说 i += 1,但由于 Scala 更频繁地调用不可变和丰富的控制流进行编程,因此您应该很少需要这样做。你当然可以自己定义它,因为运算符实际上只是 Scala 中的方法。

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 i is now 1 larger than it was before. Instead, you should try to use immutable objects (e.g. val not 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 while and for loops that iterate over arrays. However, in Scala, you can just say what you mean: for(x <- someArray) or someArray.foreach or 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).

You're spot on that ++ could be 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.

琴流音 2024-10-16 10:30:53

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

就这样:

scala> runProgram
res26: scalaz.Id.Id[Int] = 3

无需对变量诉诸暴力。

Of course you can have that in Scala, if you really want:

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.

南…巷孤猫 2024-10-16 10:30:53

Scala 完全能够解析 i++,并且只需对语言进行少量修改,就可以修改变量。但有各种各样的理由不这样做。

首先,它只节省了一个字符,i++i+=1 相比,这对于添加新的语言功能来说并不算节省太多。

其次,++ 运算符在集合库中广泛使用,其中 xs ++ ys 接受集合 xsys 并生成一个包含两者的新集合。

第三,Scala 试图鼓励您而不是强迫您以函数式方式编写代码。 i++ 是一个可变操作,因此与 Scala 让它变得特别简单的思想不一致。 (同样,语言功能允许 ++ 改变变量。)

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.

First, it saves only one character, i++ vs. i+=1, which is not very much savings for adding a new language feature.

Second, the ++ operator is widely used in the collections library, where xs ++ ys takes collection xs and ys and produces a new collection that contains both.

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.)

猫弦 2024-10-16 10:30:53

Scala 没有 ++ 运算符,因为无法在其中实现。

编辑:正如刚才针对此答案所指出的,Scala 2.10.0 可以通过使用宏来实现增量运算符。有关详细信息,请参阅此答案,并将以下所有内容视为 Scala 2.10.0 之前的版本。

让我详细说明一下,我将严重依赖 Java,因为它实际上也遇到同样的问题,但如果我使用 Java 示例,人们可能会更容易理解它。

首先,需要注意的是,Scala 的目标之一是“内置”类不得具有库无法复制的任何功能。当然,在 Scala 中,Int 是一个类,而在 Java 中,int 是一个基元——一种完全不同于类的类型。

因此,为了让 Scala 支持 i++ 对于 Int 类型的 i,我应该能够创建自己的类 MyInt 也支持同样的方法。这是 Scala 的驱动设计目标之一。

现在,Java 自然不支持符号作为方法名称,因此我们将其称为 incr()。我们的目的是尝试创建一个方法 incr(),使得 y.incr() 的工作方式与 i++ 类似。

这是第一步:

public class Incrementable {
    private int n;

    public Incrementable(int n) {
        this.n = n;
    }

    public void incr() {
        n++;
    }

    @Override
    public String toString() {
        return "Incrementable("+n+")";
    }
}

我们可以用这个来测试它:

public class DemoIncrementable {
    static public void main(String[] args) {
        Incrementable i = new Incrementable(0);
        System.out.println(i);
        i.incr();
        System.out.println(i);
    }
}

一切似乎也都有效:

Incrementable(0)
Incrementable(1)

现在,我将展示问题所在。让我们更改演示程序,并将 Incrementableint 进行比较:

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);
    }
}

正如我们在输出中看到的,Incrementableint< /code> 的行为有所不同:

i                   j                       k       l
Incrementable(0)    Incrementable(0)        0       0
Incrementable(1)    Incrementable(1)        1       0

问题是我们通过改变 Incrementable 来实现 incr(),这不是基元的工作方式。 Incrementable 需要是不可变的,这意味着 incr() 必须生成一个对象。让我们做一个天真的改变:

public Incrementable incr() {
    return new Incrementable(n + 1);
}

然而,这不起作用:

i                   j                       k       l
Incrementable(0)    Incrementable(0)        0       0
Incrementable(0)    Incrementable(0)        1       0

问题是,虽然 incr() 创建了一个新对象,但该新对象尚未被创建。 分配i。 Java 或 Scala 中没有现有的机制允许我们使用与 ++ 完全相同的语义来实现此方法。

现在,这并不意味着 Scala 不可能让这样的事情成为可能。如果 Scala 支持通过引用传递参数(请参阅这篇维基百科文章中的“通过引用调用”),就像 C++ 一样,那么我们就可以实现它!

这是一个虚构的实现,假设与 C++ 中的引用表示法相同。

implicit def toIncr(Int &n) = {
  def ++ = { val tmp = n; n += 1; tmp }
  def prefix_++ = { n += 1; n }
}

这要么需要 JVM 支持,要么需要 Scala 编译器上的一些严格的机制。

事实上,Scala 所做的事情与创建闭包时所需的类似 - 其后果之一是原始的 Int 被装箱,可能会带来严重的性能问题影响。

例如,考虑以下方法:

  def f(l: List[Int]): Int = {
    var sum = 0
    l foreach { n => sum += n }
    sum
  }

传递给 foreach, { n =>; 的代码sum += n } 不是此方法的一部分。 foreach 方法采用 Function1 类型的对象,其 apply 方法实现了这一小段代码。这意味着 { n =>; sum += n } 不仅在不同的方法上,而且在完全不同的类上!然而,它可以改变 sum 的值,就像 ++ 运算符需要的那样。

如果我们使用 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$1
   13:  dup
   14:  aload_0
   15:  aload_2
   16:  invokespecial   #17; //Method tst$anonfun$f$1."<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

请注意,它不是创建一个 int 局部变量,而是创建一个 IntRef在堆上(0 处),这是对 int 进行装箱。真正的 int 位于 IntRef.elem 内部,正如我们在 25 中看到的那样。让我们看看用 while 循环实现同样的事情,以明确区别:

  def f(l: List[Int]): Int = {
    var sum = 0
    var next = l
    while (next.nonEmpty) {
      sum += next.head
      next = next.tail
    }
    sum
  }

变成:

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

没有对象上面的创建,不需要从堆中获取东西。

因此,总而言之,Scala 需要额外的功能来支持用户可以定义的增量运算符,因为它避免提供外部库无法使用的自己的内置类功能。其中一项功能是按引用传递参数,但 JVM 不提供对此的支持。 Scala 执行类似于按引用调用的操作,为此它使用装箱,这会严重影响性能(很可能会出现增量运算符!)。因此,在缺乏 JVM 支持的情况下,这种可能性不大。

作为补充说明,Scala 具有明显的功能倾向,优先考虑不变性和引用透明性,而不是可变性和副作用。通过引用调用的唯一目的是对调用者造成副作用!虽然这样做可以在许多情况下带来性能优势,但它非常违背 Scala 的原则,因此我怀疑按引用调用是否会成为其中的一部分。

Scala doesn't have a ++ operator because it is not possible to implement one in it.

EDIT: As just pointed out in response to this answer, Scala 2.10.0 can implement an increment operator through use of macros. See this answer for details, and take everything below as being pre-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.

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 Int is a class, whereas in Java an int is a primitive -- a type entirely distinct from a class.

So, for Scala to support i++ for i of type Int, I should be able to create my own class MyInt also supporting the same method. This is one of the driving design goals of 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++.

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 Incrementable to 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, Incrementable and int are behaving differently:

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. Incrementable needs to be immutable, which means that incr() must produce a new object. Let's do a naive change:

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() created a new object, that new object hasn't been assigned to i. There's no existing mechanism in Java -- or Scala -- that would allow us to implement this method with the exact same semantics as ++.

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 could implement it!

Here's a fictitious implementation, assuming the same by-reference notation as in 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.

In fact, Scala does something similar to what would be needed that when it create closures -- and one of the consequences is that the original Int becomes boxed, with possibly serious performance impact.

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 not part of this method. The method foreach takes an object of the type Function1 whose apply method 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 sum just like a ++ operator would need to.

If we use javap to look at it, we'll see this:

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$1
   13:  dup
   14:  aload_0
   15:  aload_2
   16:  invokespecial   #17; //Method tst$anonfun$f$1."<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 int local variable, it creates an IntRef on the heap (at 0), which is boxing the int. The real int is inside IntRef.elem, as we see on 25. Let's see this same thing implemented with a while loop to make clear the difference:

  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.

As an additional note, Scala has a distinct functional slant, privileging immutability and referential transparency over mutability and side effects. The sole purpose 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.

甜嗑 2024-10-16 10:30:53

其他答案已经正确指出, ++ 运算符在函数式编程语言中既不是特别有用,也不理想。我想补充一点,从 Scala 2.10 开始,如果您愿意,您可以添加 ++ 运算符。方法如下:

您需要一个隐式宏,将整数转换为具有 ++ 方法的实例。 ++ 方法由宏“编写”,该宏可以访问调用 ++ 方法的变量(而不是其值)。这是宏的实现:

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
    }
  })
}

现在,只要隐式转换宏在作用域内,您就可以编写

var i = 0
println(i++) //prints 0
println(i) //prints 1

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:

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
维持三分热 2024-10-16 10:30:53

Rafe 的回答对于为什么像 i++ 这样的东西不属于 Scala 的基本原理是正确的。不过我有一点挑剔。实际上不可能在不改变语言的情况下在 Scala 中实现 i++。

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

C++ 和 Java 等语言将 ++ 特别视为递增和赋值。 Scala 以不一致的方式特殊对待 =

在 Scala 中,当您编写 i += 1 时,编译器首先在 Int 上查找名为 += 的方法。它不在那里,所以接下来它在 = 上发挥了魔力,并尝试编译该行,就好像它读取 i = i + 1 一样。如果您编写 i++,那么 Scala 将调用 i 上的方法 ++ 并将结果分配给...无。因为只有=表示赋值。您可以编写 i ++= 1 但这样就达不到目的了。

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

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.

In Scala, ++ is a valid method, and no method implies assignment. Only = can do that.

Languages like C++ and Java treat ++ specially to mean both increment and assign. Scala treats = specially, and in an inconsistent way.

In Scala when you write i += 1 the 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 i and assign the result to... nothing. Because only = means assignment. You could write i ++= 1 but that kind of defeats the purpose.

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.

入画浅相思 2024-10-16 10:30:53

有相当多的语言不支持++表示法,例如Lua。在支持它的语言中,它经常是混乱和错误的根源,因此它作为语言功能的质量是值得怀疑的,与 i += 1 甚至只是 的替代方案相比>i = i + 1,保存这些次要字符是相当没有意义的。

这与语言的类型系统完全无关。虽然大多数静态类型语言确实提供了这些功能,而大多数动态类型却没有提供,但这只是一种相关性,而绝对不是原因。

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 += 1 or even just i = i + 1, the saving of such minor characters is fairly pointless.

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.

能怎样 2024-10-16 10:30:53

Scala 鼓励使用 FP 风格,而 i++ 当然不是。

Scala encourages using of FP style, which i++ certainly is not.

眼波传意 2024-10-16 10:30:53

要问的问题是为什么应该有这样的运算符,而不是为什么不应该有。 Scala 会因此得到改进吗?

++ 运算符是单一用途的,使用可以更改变量值的运算符可能会导致问题。编写令人困惑的表达式很容易,即使该语言定义了 i = i + i++ 的含义,例如,也需要记住很多详细规则。

顺便说一句,你对 Python 和 Ruby 的推理是错误的。在 Perl 中,您可以编写 $i++++$i 就可以了。如果 $i 结果无法递增,则会出现运行时错误。它不在 Python 或 Ruby 中,因为语言设计者认为这不是一个好主意,而不是因为它们像 Perl 一样是动态类型的。

The question to ask is why there should be such an operator, not why there shouldn't be. Would Scala be improved by it?

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.

Your reasoning on Python and Ruby is wrong, by the way. In Perl, you can write $i++ or ++$i just fine. If $i turns 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.

倾`听者〃 2024-10-16 10:30:53

不过你可以模拟它。举一个简单的例子:

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)

添加一些隐式转换就可以了。然而,这种问题将问题转变为:为什么没有具有此功能的可变 RichInt?

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?

丢了幸福的猪 2024-10-16 10:30:53

正如另一个答案所暗示的那样,增量运算符(如i++中所示)是——

据称...由 Ken Thompson 添加到 B 语言[C 语言的前身],特别是因为[它]能够在编译后直接翻译为单个操作码

而不一定是因为这样的运算符与拥有一样有用,比如说一般的加法和减法。尽管某些面向对象语言(例如 Java 和 C#)也有增量运算符(通常借用自 C),但并非所有语言都有(例如 Ruby)。

As another answer suggests, the increment operator, as found in i++, was—

supposedly ... added to the B language [a predecessor of the C language] by Ken Thompson specifically because [it was] capable of translating directly to a single opcode once compiled

and not necessarily because such an operator is as useful to have as, say, general addition and subtraction. Although certain object-oriented languages (such as Java and C#) also have an increment operator (often borrowed from C), not all do (such as Ruby).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文