Scala - mutable (var) method parameter reference

后端 未结 7 1425
天命终不由人
天命终不由人 2020-12-08 06:17

EDIT: I keep getting upvotes here. Just for the record, I no longer think this is important. I haven\'t needed it since I posted it.

I would like to do following in

相关标签:
7条回答
  • 2020-12-08 06:54

    String objects are immutable in Scala (and Java). The alternatives I can think of are:

    1. Return the result string as return value.
    2. Instead of using a String parameter, use a StringBuffer or StringBuilder, which are not immutable.

    In the second scenario you would have something like:

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

    EDIT

    If I understand correctly, you want to use the argument as a local variable. You can't, because all method arguments are val's in Scala. The only thing to do is to copy it to a local variable first:

    def save(srcPath: String, destPath: String) {
        var destP = destPath
        if (!destP.endsWith("/"))
           destP += "/"
        // do something
        //
    }
    
    0 讨论(0)
  • 2020-12-08 06:55

    Maybe you could get the type system to do the work for you, so you don't even need to worry about adding a slash each time:

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

    Now you don't need any code at all to change the input 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/
    

    True, there's a bit of setup at the start, but this will be less-so with implicit classes in version 2.10, and it removes all clutter from the method, which was what you were worried about.

    0 讨论(0)
  • 2020-12-08 07:00

    Here's a couple of suggestions:

    1) Update your function a bit

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

    2) Create a utility function to use before calling 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))
    
    0 讨论(0)
  • 2020-12-08 07:02

    The JVM does not allow pass-by-reference of pointers to objects (which is how you'd do this in C++), so you can't do exactly what you want.

    One option is to return the new value:

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

    Another is to create a wrapper:

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

    which users will then have to use on the way in. (Of course, they'll be tempted to save("/here",Mut("/there")) which will throw away the alterations, but this is always the case with pass-by-reference function arguments.)


    Edit: what you're proposing is one of the biggest sources of confusion among non-expert programmers. That is, when you modify the argument of a function, are you modifying a local copy (pass-by-value) or the original (pass-by-reference)? If you cannot even modify it it is pretty clear that anything you do is a local copy.

    Just do it that way.

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

    It's worth the lack of confusion about what is actually going on.

    0 讨论(0)
  • 2020-12-08 07:18

    You can't.

    You'll have to declare an extra var (or use a more functional style :-)).

    Simplistic example:

    def save(srcPath: String, destPath: String) {
        val normalizedDestPath =
          if (destPath.endsWith('/')) destPath
          else destPath + '/'
        // do something with normalizedDestPath 
    }
    
    0 讨论(0)
  • 2020-12-08 07:18

    I know this is an old question, but if you just want to reuse the argument name perhaps:

    def save(srcPath: String, destPath: String) {
      ((destPath: String) => {
        // do something
      })(if (!destPath.endsWith('/')) destPath + '/' else destPath)
    }
    
    0 讨论(0)
提交回复
热议问题