簡體   English   中英

Scala - mutable(var)方法參數引用

[英]Scala - mutable (var) method parameter reference

編輯:我一直在這里投票。 僅僅為了記錄,我不再認為這很重要。 自從我發布之后我就不需要了。

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

你不能。

你必須聲明一個額外的var (或使用更多功能的樣式:-))。

簡單的例子:

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

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"))這將丟棄改變,但總是如此使用pass-by-reference函數參數。)


編輯:你提出的建議是非專業程序員之間最大的困惑之一。 也就是說,當您修改函數的參數時,您是修改本地副本(按值傳遞)還是原始(按引用傳遞)? 如果您甚至無法修改它 ,很明顯您所做的任何事情都是本地副本。

就這樣做吧。

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

值得對實際發生的事情缺乏困惑。

也許您可以讓類型系統為您完成工作,因此您甚至不必擔心每次都添加斜杠:

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版本中的隱式類,這將會更少 - 並且它會消除方法中的所有混亂,這是您所擔心的。

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

  1. 返回結果字符串作為返回值。
  2. 而不是使用String參數,使用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
    //
}

我知道這是一個老問題,但如果您只是想重用參數名稱:

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

以下是一些建議:

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不允許這樣做。 其他人已經描述了一些低級別的解決方法(都很好),但我會添加更高級別的解決方法。 對於這種字符串規范化目的,我使用后綴,前綴,removeSuffix和removePrefix等方法保留了對scala.String的pimped擴展。 后綴和前綴追加或前置一個字符串到另一個字符串,除非后綴或前綴已經存在。 removeSuffix和removePrefix做的很明顯,從另一個字符串的末尾或開頭刪除一個字符串(如果它存在)。 你的用例將被寫入

val normalizedPath = destPath.addSuffix("/") 

如果你做了大量的數據分析或文件操作,這些方法非常方便,你不會相信你沒有它們。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM