Scala-可变(var)方法参数引用

Scala-可变(var)方法参数引用,scala,variables,methods,parameters,Scala,Variables,Methods,Parameters,编辑:我一直在这里获得选票。为了记录在案,我不再认为这很重要。自从我把它贴出来后,我就不需要它了 我想做以下在Scala def save(srcPath: String, destPath: String) { if (!destPath.endsWith('/')) destPath += '/' // do something } 。。。但是我不能因为destPath是val。有没有办法将destPath声明为var 注意:有类似的问题,但在所有这些问题中

编辑:我一直在这里获得选票。为了记录在案,我不再认为这很重要。自从我把它贴出来后,我就不需要它了

我想做以下在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。

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
}
哪些用户在进入时必须使用。(当然,他们会尝试
保存(“/here”,Mut(“/there”)
,这将丢弃更改,但对于传递引用函数参数,情况总是如此。)


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

就这样做吧

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

不必对实际发生的事情感到困惑,这是值得的。

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

  • 将结果字符串作为返回值返回
  • 使用StringBuffer或StringBuilder,而不是使用字符串参数,它们不是不可变的
  • 在第二个场景中,您可能会遇到如下情况:

    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
        //
    }
    
    你不能

    您必须声明一个额外的
    var
    (或者使用更具功能性的样式:-)

    简单的例子:

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

    以下是一些建议:

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

    不,这在Scala是不允许的。其他人描述了一些低级别的变通方法(都很好),但我将添加一个更高级别的变通方法。出于这种字符串规范化的目的,我保留了scala.string的一个pimped扩展,并使用了suffix、prefix、removeSuffix和removeSuffix等方法。后缀和前缀将一个字符串附加或前置到另一个字符串上,除非后缀或前缀已经存在。removeSuffix和removePrefix做的很明显,就是从另一个字符串的末尾或开头删除一个字符串(如果有)。您的用例将被编写出来

    val normalizedPath = destPath.addSuffix("/") 
    

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

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

    class SlashString(s: String) {
      override val toString = if (s endsWith "/") s else s + "/"
    }
    implicit def toSlashString(s: String) = new SlashString(s)
    
    现在,您根本不需要任何代码来更改输入
    字符串

    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版的隐式类中情况就不太一样了,而且它消除了方法中的所有混乱,这正是您所担心的。

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

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

    你好非常感谢。恐怕你误解了我的问题。我已经更新了它。“JVM不允许通过引用传递指向对象的指针”,这一点需要非常仔细的澄清。在Java中传递原语时:我们实际上是在传递值。但是,当传递一个对象时:您实际上是在传递一个对该对象的引用(我们可以声称这也是通过值实现的)。出于这个原因,“Java只通过值传递”语句实际上会引起很多混乱,但实际上该断言需要理解原语是通过值传递的,对象引用是通过值传递的。Hello。非常感谢。恐怕你误解了我的问题。我已经更新了。广告2。没有选择,那只是杂乱无章。StringBuilder暗示我将构建一些大字符串。如果我能做“字符串(->可能是新字符串)”,为什么要做“字符串->字符串生成器->字符串”?您的解决方案只是解决缺乏Scala知识或Scala局限性的一个解决方案。在Java中不会这样做,是吗?我并没有说使用StringBuilder很优雅,但是如果您不想将字符串作为返回值返回,那么您必须使用某种可变字符串类型的参数。实际上,您可以在Scala中的StringBuilder上直接使用endsWith()(您不需要调用toString())。关于最初的问题:Scala鼓励函数式编程,所以即使使用普通的变量(局部变量),也应该尽可能避免。@woky:来自Odersky(Scala的发明者)等:Scala中的编程,第二版,第52页:“Scala允许您以命令式风格编程,但鼓励您采用更具功能性的风格”以及后来的“Scala鼓励您倾向于VAL,但最终获得手头工作所需的最佳工具”。因此,根据其发明者的说法,Scala确实鼓励您进行功能性编程,