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

时间:2012-03-02 15:25:12

标签: scala variables methods parameters

编辑:我一直在这里投票。仅仅为了记录,我不再认为这很重要。我发布以来就没需要它了。

我想在Scala中关注...

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

...但我不能认为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);
}

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

7 个答案:

答案 0 :(得分:29)

你不能。

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

简单示例:

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

答案 1 :(得分:9)

JVM不允许指向对象的指针传递(这是你在C ++中这样做的方式),因此你无法完全按照自己的意愿行事。

一种选择是返回新值:

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

另一种方法是创建一个包装器:

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

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

然后,用户必须在途中使用。(当然,他们会被save("/here",Mut("/there"))诱惑,这会抛弃改变,但是传递的情况总是这样。参考函数参数。)


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

就这样做。

val destWithSlash = destPath + (if (!destPath.endsWith("/")) "/" else "")

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

答案 2 :(得分:6)

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

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

现在您根本不需要任何代码来更改输入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/

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

答案 3 :(得分:1)

String对象在Scala(和Java)中是不可变的。我能想到的替代方案是:

  1. 返回结果字符串作为返回值。
  2. 使用不是不可变的StringBuffer或StringBuilder,而不是使用String参数。
  3. 在第二种情况中,你会有类似的东西:

    def save(srcPath: String, destPath: StringBuilder) {
        if (!destPath.toString().endsWith("/"))
           destPath.append("/")
        // do something
        //
    }
    

    修改

    如果我理解正确,您希望将该参数用作局部变量。你不能,因为所有方法参数都是Scala中的val。 唯一要做的就是先将它复制到局部变量:

    def save(srcPath: String, destPath: String) {
        var destP = destPath
        if (!destP.endsWith("/"))
           destP += "/"
        // do something
        //
    }
    

答案 4 :(得分:0)

以下是一些建议:

1)稍微更新你的功能

def save(srcPath: String, destPath: String) {
  var dp = destPath
  if (!dp.endsWith('/'))
    dp+= '/'
  // do something, but with dp instead of destPath
}

2)在调用save

之前创建一个实用程序函数
def savedPath(path: String) = 
  if(path.endsWith("/")) 
    path
  else
    path + "/"

//call your save method on some path
val myDestPath = ...
val srcPath = ...
save(srcPath, savedPath(myDestPath))

答案 5 :(得分:0)

不,Scala不允许这样做。其他人已经描述了一些低级别的解决方法(都很好),但我会添加更高级别的解决方法。对于这种字符串规范化目的,我使用后缀,前缀,removeSuffix和removePrefix等方法保留了对scala.String的pimped扩展。后缀和前缀追加或前置一个字符串到另一个字符串,除非后缀或前缀已经存在。 removeSuffix和removePrefix做的很明显,从另一个字符串的末尾或开头删除一个字符串(如果它存在)。您的用例将被编写

val normalizedPath = destPath.addSuffix("/") 

如果您进行大量的数据分析或文件操作,这些方法非常方便,您不会相信没有它们。

答案 6 :(得分:0)

我知道这是一个老问题,但如果您只是想重用参数名称:

def save(srcPath: String, destPath: String) {
  ((destPath: String) => {
    // do something
  })(if (!destPath.endsWith('/')) destPath + '/' else destPath)
}