首页 文章

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

提问于
浏览
35

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

我想在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 回答

  • 9

    你不能 .

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

    简单的例子:

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

    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 "")
    

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

  • 27

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

    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版本中的隐式类,这将会更少 - 并且它会消除方法中的所有混乱,这是您所担心的 .

  • 0

    字符串对象在Scala(和Java)中是不可变的 . 我能想到的替代方案是:

    • 返回结果字符串作为返回值 .

    • 使用不是不可变的StringBuffer或StringBuilder,而不是使用String参数 .

    在第二种情况下,你会有类似的东西:

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

    EDIT

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

    def save(srcPath: String, destPath: String) {
        var destP = destPath
        if (!destP.endsWith("/"))
           destP += "/"
        // do something
        //
    }
    
  • 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

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

    val normalizedPath = destPath.addSuffix("/")
    

    如果你做了大量的数据分析或文件操作,这些方法非常方便,你不会相信你没有它们 .

  • 1

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

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

相关问题