为什么我要避免在Scala中使用本地可修改的变量?

Ole*_*hyi 6 functional-programming scala immutability

在使用Java之前,我对Scala很新,而且大部分时间都是.现在,我的代码中都有警告说我应该"避免可变的局部变量",我有一个简单的问题 - 为什么?

假设我有一个小问题 - 确定四个中的最大值.我的第一个方法是:

def max4(a: Int, b: Int,c: Int, d: Int): Int = {
  var subMax1 = a
  if (b > a) subMax1 = b

  var subMax2 = c
  if (d > c) subMax2 = d

  if (subMax1 > subMax2) subMax1
  else subMax2
}
Run Code Online (Sandbox Code Playgroud)

在考虑此警告消息后,我找到了另一种解决方案:

def max4(a: Int, b: Int,c: Int, d: Int): Int = {
  max(max(a, b), max(c, d))
}

def max(a: Int, b: Int): Int = {
  if (a > b) a
  else b
}
Run Code Online (Sandbox Code Playgroud)

它看起来更漂亮,但这背后的意识形态是什么?

每当我遇到问题时,我都会这样想:"好吧,我们从这开始,然后我们逐步改变事情并得到答案".我明白问题在于我试图改变一些初始状态以获得答案,并且不明白为什么至少在本地改变事情是不好的?如何在Scala等函数式语言中迭代集合?

就像一个例子:假设我们有一个int列表,如何编写一个函数来返回可被6整除的int的子列表?没有局部可变变量就无法想到解决方案.

om-*_*nom 5

在您的特定情况下,有另一种解决方案:

def max4(a: Int, b: Int,c: Int, d: Int): Int = {
  val submax1 = if (a > b) a else b
  val submax2 = if (c > d) c else d

  if (submax1 > submax2) submax1 else submax2
}
Run Code Online (Sandbox Code Playgroud)

是不是更容易遵循?当然我有点偏颇,但我倾向于认为,不要盲目遵循这个规则.如果你看到某些代码可能以可变的方式更易读和简洁地编写,那就这样做吧 - scala的强大之处在于你既不需要既不可变也不可变的方法,你可以在它们之间摆动(顺便说一句同样适用于return关键字使用).

就像一个例子:假设我们有一个整数列表,如何编写一个返回可被6整除的整数子列表的函数?没有局部可变变量就无法想到解决方案.

使用递归来编写这样的函数当然是可能的,但是,如果可变解决方案看起来并且工作正常,为什么不呢?


Álv*_*ses 5

它与Scala和一般的函数式编程方法没有太大关系.这个想法如下:如果你有常量变量(Java中的final),你可以使用它们而不用担心它们会改变.以同样的方式,您可以并行化代码,而无需担心竞争条件或线程不安全的代码.

在您的示例中并不是那么重要,但请想象以下示例:

val variable = ...
new Future { function1(variable) }
new Future { function2(variable) }
Run Code Online (Sandbox Code Playgroud)

使用最终变量可以确保不会出现任何问题.否则,您必须检查主线程以及function1和function2.

当然,如果你不改变它们,可以用可变变量获得相同的结果.但是使用不可变的那些你可以肯定会出现这种情况.

编辑以回答您的修改:

本地变量也不错,这就是你可以使用它们的原因.但是,如果您尝试在没有它们的情况下思考方法,您可以按照发布的方式到达解决方案,这样更清晰,并且可以非常容易地并行化.

如何在Scala等函数式语言中迭代集合?

您可以随时迭代一个不可变的集合,而不会更改任何内容.例如:

val list = Seq(1,2,3)
for (n <- list)
  println n
Run Code Online (Sandbox Code Playgroud)

关于你说的第二件事:你必须停止以传统的方式思考.在函数式编程中,Map,Filter,Reduce等的使用是正常的; 以及模式匹配和OOP中不常见的其他概念.对于您给出的示例:

就像一个例子:假设我们有一个int列表,如何编写一个函数来返回可被6整除的int的子列表?

val list = Seq(1,6,10,12,18,20)
val result = list.filter(_ % 6 == 0)
Run Code Online (Sandbox Code Playgroud)