![](/img/trans.png)
[英]Scala - multiple mutable parameter reference or pass-by-reference of pointers to objects
[英]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)中是不可變的。 我能想到的替代方案是:
在第二種情況下,你會有類似的東西:
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.