Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/18.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Scala中将参数传递给mutator函数_Scala_Mutators - Fatal编程技术网

在Scala中将参数传递给mutator函数

在Scala中将参数传递给mutator函数,scala,mutators,Scala,Mutators,我正在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)

我正在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
}
上面唯一没有编译的代码是def initList中的var,因为数据是val,所以我无法在函数中对其执行任何突变

首先我要说的是,我知道在Scala中,突变通常是不受欢迎的,所以我不仅愿意直接回答我的问题,而且愿意完全采用更好的方法。在项目中,偶尔会有一段数据从一个地方传到另一个地方进行更新,但如果您无法将数据传递给要更改的函数,那么什么是好的替代方案


我已经阅读了很多教程,谷歌也为这一点做了介绍,我想我找不到太多关于它的内容,因为在Scala中通常不是这样做的。

像这样的东西怎么样:

def initList(inData: List[Int]) : List[Int] = {
    var data = inData  // <----The one added line
    for ( i <- 0 to 1000 )
    {
        data = i :: data
    }
    data.reverse
}

更好的代码是

val data = 1000 to 0 by -1

在imo中速度更快,更易于阅读。

首先要意识到的是,虽然数据是一个变量,但列表本身仍然是不可变的。虽然可以为数据分配新列表,但不能更改实际列表本身。这也是为什么不能将列表传递给函数并更改列表的原因。所以实际上,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时,它返回假设数据是空的List0,1,2,3

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

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

val data: List[Int] = List()
(0 to 1000).foldRight(data){ (num, buildList) => num :: buildList}
甚至只是这样:

(0 to 1000).toList

您应该更喜欢其他答案中建议的功能性/不可变解决方案。但是,当您确实需要此功能时(通过引用传递值),您可以使用

这是您声明它们的方式:

val cell: Ref[SomeClass] = Ref(value)
这是您访问其价值的方式:

!cell
cell := !cell + 1
// OR
cell.modify(_ + 1)
这就是你如何改变它们的价值:

!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)
}
您可以为此添加许多有用的方法。e、 g.整数值的增量和减量

这是使用参考单元格重写的代码,其工作原理与预期一致:

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)

这就是诀窍,我不敢相信问题的答案是制作可变变量的可变副本。有没有办法不用复印件就能做到这一点?或者这被认为是Scala的标准实践吗?实际上,这很少复制;将“data”声明为var就是说数据指向的引用可以更改inData'在默认情况下基本上是一个val,因此它在其生命周期中与'data'共享相同的引用,或者,至少与最初传递到函数中的引用相同,然后initListdata最初也采用相同的引用这是同时具有相同引用的三件事-但是它会被for{}循环不断地覆盖。。。还有别的办法吗?是 啊还有其他方法,但我认为没有比这更实际的了。它的成本很低,而且可以满足您的需求。