Scala - mutable(var)方法参数引用

wok*_*oky 40 variables parameters methods scala

编辑:我一直在这里投票.仅仅为了记录,我不再认为这很重要.自从我发布之后我就不需要了.

我想在Scala中做以下...

def save(srcPath: String, destPath: String) {
    if (!destPath.endsWith('/'))
        destPath += '/'
    // do something
}
Run Code Online (Sandbox Code Playgroud)

......但我不能说destPath是val.有没有办法声明destPath为var?

注意:有类似的问题,但在所有这些问题中OP只是想修改数组.

请不要建议以下内容:

改变输入参数通常被认为是糟糕的风格,并且使得更难以推理代码.

我认为它在命令式编程中是有效的(Scala允许两者,对吗?)并添加类似tmpDestPath添加杂乱的东西.

编辑:不要误解.我知道字符串不可变,我不想引用引用,因为我不想修改调用者的数据.我只想修改调用者给我的字符串的本地引用(例如,orig +'/').我想仅在当前方法的范围内修改该值.看,这在Java中是完全有效的:

void printPlusOne(int i) {
    i++;
    System.out.println("i is: " + i);
    System.out.println("and now it's same: " + i);
}
Run Code Online (Sandbox Code Playgroud)

我不必创建新变量,我不必两次计算i + 1.

Jea*_*let 29

你不能.

你必须声明一个额外的var(或使用更实用的风格:-)).

简单的例子:

def save(srcPath: String, destPath: String) {
    val normalizedDestPath =
      if (destPath.endsWith('/')) destPath
      else destPath + '/'
    // do something with normalizedDestPath 
}
Run Code Online (Sandbox Code Playgroud)


Rex*_*err 9

JVM不允许指向对象的指针传递(这是你在C++中这样做的方式),所以你不能完全按照自己的意愿去做.

一种选择是返回新值:

def save(srcPath: String, destPath: String): String = {
  val newPath = (if (!destPath.endsWith("/")) destPath+'/' else destPath)
  // do something
  newPath
}
Run Code Online (Sandbox Code Playgroud)

另一个是创建一个包装器:

case class Mut[A](var value: A) {}

def save(srcPath: String, destPath: Mut[String]) {
  if (!destPath.value.endsWith("/")) destPath.value += '/'
  // do something
}
Run Code Online (Sandbox Code Playgroud)

那些用户将在进入的路上必须使用.(当然,他们会被诱惑save("/here",Mut("/there"))哪些会丢弃更改,但是传递引用函数参数总是如此.)


编辑:你提出的建议是非专业程序员之间最大的困惑之一.也就是说,当您修改函数的参数时,您是修改本地副本(按值传递)还是原始(按引用传递)?如果您甚至无法修改它,很明显您所做的任何事情都是本地副本.

就这样做吧.

val destWithSlash = destPath + (if (!destPath.endsWith("/")) "/" else "")
Run Code Online (Sandbox Code Playgroud)

值得对实际发生的事情缺乏困惑.

  • 你好。谢谢。恐怕您误解了我的问题。我已经更新了。 (2认同)
  • “ JVM不允许传递指向对象的指针的引用”。这一点需要一些非常仔细的说明。在Java中传递基元时:我们确实是在按值传递。但是,当传递一个对象时:实际上是在传递对该对象的引用(我们可以声称它也已经完成:按值)。因此,“ Java仅按值传递”语句实际上引起了很多混乱,但是实际上,断言要求理解基元是按值传递的,而对象*引用*是按值传递的。 (2认同)

Lui*_*hys 6

也许您可以让类型系统为您完成工作,因此您甚至不必担心每次都添加斜杠:

class SlashString(s: String) {
  override val toString = if (s endsWith "/") s else s + "/"
}
implicit def toSlashString(s: String) = new SlashString(s)
Run Code Online (Sandbox Code Playgroud)

现在您根本不需要任何代码来更改输入String:

def save(srcPath: String, destPath: SlashString) {
  printf("saving from %s to %s", srcPath, destPath)
}

val src: String = "abc"
val dst: String = "xyz"

scala> save(src, dst)
saving from abc to xyz/
Run Code Online (Sandbox Code Playgroud)

确实,在开始时有一些设置,但是对于2.10版本中的隐式类,这将会更少 - 并且它会消除方法中的所有混乱,这是您所担心的.