0

我正在使用 Scala 来尝试掌握它,所以这个代码示例只是学术性的。

我正在尝试将一个可变列表传递给一个函数,让该函数对其执行工作,然后在函数调用之后我可以使用更新的列表。

var data: List[Int] = List()

// Call to function to fill a list
data = initList(data)

// Output only 0-100
data.foreach( num => if (num < 100) { println(num) })

def initList(var data: List[Int]) : List[Int] = {

    for ( i <- 0 to 1000 )
    {
        data = i :: data
    }

    data = data.reverse
    data
}

上面唯一不能编译的代码是varin def initList(),因为 data 是 aval我不能在函数中对其执行任何突变。

首先让我说我知道在 Scala 中,mutators 通常是不受欢迎的,所以我不仅愿意直接回答我的问题,而且愿意接受更好的方法来完全做到这一点。偶尔在项目中会有一段数据从一个地方到另一个地方进行更新,但是如果您不能将数据传递给要更改的函数,那么有什么好的选择呢?

我已经阅读了教程和谷歌搜索,我假设我找不到太多关于它的信息,因为在 Scala 中通常不会这样做。

4

4 回答 4

4

首先要意识到的是,虽然data是一个 var,但列表本身仍然是不可变的。虽然您可以将新列表分配给data,但您不能更改实际列表本身。这也是为什么您不能将列表传递给函数并更改列表的原因。所以实际上你的for循环是在每次迭代时创建一个新列表。

幸运的是,Scala 使得编写函数式代码来创建列表和其他不可变数据结构变得非常容易。这是执行您想要的一般“功能”方式:

def initList(data: List[Int]) = {
  def initLoop(num: Int, buildList: List[Int]): List[Int] = num match {
    case 0 => 0 :: buildList
    case n => initLoop(n - 1, n :: buildList)
  }
  initLoop(1000, data)
}

基本上这里发生的事情是我用尾递归函数替换了 for 循环。每次调用时,内部函数都会通过获取当前列表并添加下一个数字来构建一个新列表,直到它变为 0 并返回完成的列表。由于函数从 1000 开始并返回到 0,因此不必反转列表。

为了让您了解它是如何工作的,这里是每次递归调用时内部函数的参数值(尽管让我们从 3 而不是 1000 开始):

initLoop(3, data)
initLoop(2, 3 :: data)
initLoop(1, 2 :: 3 :: data)
initLoop(0, 1 :: 2 :: 3 :: data)

所以当它最终变为 0 时,它会返回(假设数据为空)List(0, 1, 2, 3)

这种方法实际上比使用 for 循环更快,因为您不需要在最后反转列表。Scala 能够优化尾递归函数,因此您不必担心堆栈溢出或内存不足错误。

还有很多其他方法可以创建和转换列表,我也可以这样做:

val data: List[Int] = List()
(0 to 1000).foldRight(data){ (num, buildList) => num :: buildList}

甚至只是这个:

(0 to 1000).toList
于 2012-01-21T04:28:12.737 回答
4

您应该更喜欢其他答案中建议的功能/不可变解决方案。然而,当你真的需要这个工具时——通过引用传递一个值,你可以使用ML 风格的 mutable reference cells

这是您声明它们的方式:

val cell: Ref[SomeClass] = Ref(value)

这是您访问其价值的方式:

!cell

这就是你如何改变它们的价值:

cell := !cell + 1
// OR
cell.modify(_ + 1)

一个简单的参考单元实现:

final class Ref[A] private(private var a: A) {
  def :=(newValue: A): Unit = {
    a = newValue
  }

  def unary_! : A = a

  def modify(f: A => A): Unit = {
    a = f(a)
  }
}

object Ref {
  def apply[A](value: A) = new Ref(value)
}

您可以为此添加许多有用的方法。例如,整数值的增量、减量。

这是您使用参考单元格重写的代码(按预期工作):

def initList(data: Ref[List[Int]]): Unit = {
  for(i <- 0 to 1000)
    data := i :: !data
  data := (!data).reverse
}

val data = Ref(List.empty[Int])
initList(data)    
for(num <- !data; if num < 100)
  println(num)
于 2012-01-23T12:54:49.263 回答
2

像这样的东西怎么样:

def initList(inData: List[Int]) : List[Int] = {
    var data = inData  // <----The one added line
    for ( i <- 0 to 1000 )
    {
        data = i :: data
    }
    data.reverse
}
于 2012-01-21T04:02:51.180 回答
2

什么可能更好的代码是

val data = 1000 to 0 by -1

在(imo)中更快更易于阅读。

于 2012-01-21T04:19:46.980 回答