Scala - 可变(var)方法参数参考
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9535821/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Scala - mutable (var) method parameter reference
提问by woky
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 Scala ...
我想在 Scala 中进行以下操作...
def save(srcPath: String, destPath: String) {
if (!destPath.endsWith('/'))
destPath += '/'
// do something
}
... but I can't beacuse destPathis a val. Is there any way to declare destPathas var?
...但我不能因为destPath是 val。有没有办法声明destPath为var?
Note: there are similar questions but in all of them OP just wanted to modify array.
注意:有类似的问题,但在所有这些问题中,OP 只是想修改数组。
Please do not advise following:
请不要建议以下内容:
Mutating the input parameters is often seen as bad style and makes it harder to reason about code.
改变输入参数通常被视为不好的风格,并且使代码更难推理。
I think it's valid in imperative programming (Scala allows both, right?) and adding something like tmpDestPathwould just add clutter.
我认为它在命令式编程中是有效的(Scala 允许两者,对吗?)并且添加类似的东西tmpDestPath只会增加混乱。
EDIT: Don't misunderstand. I know that strings aren't mutable and I don't want a reference to reference because I don't want to modify data of caller. I just want to modify local reference to string that caller gave me with my string (eg. orig + '/'). I want to modify that value only in scope of current method. Look, this is perfectly valid in Java:
编辑:不要误解。我知道字符串是不可变的,我不想引用引用,因为我不想修改调用者的数据。我只想修改调用者给我的字符串的本地引用(例如,orig + '/')。我只想在当前方法的范围内修改该值。看,这在 Java 中完全有效:
void printPlusOne(int i) {
i++;
System.out.println("i is: " + i);
System.out.println("and now it's same: " + i);
}
I don't have to create new variable and i don't have to compute i+1 twice.
我不必创建新变量,也不必计算 i+1 两次。
回答by Jean-Philippe Pellet
You can't.
你不能。
You'll have to declare an extra var(or use a more functional style :-)).
您必须声明一个额外的var(或使用更实用的样式:-))。
Simplistic example:
简单的例子:
def save(srcPath: String, destPath: String) {
val normalizedDestPath =
if (destPath.endsWith('/')) destPath
else destPath + '/'
// do something with normalizedDestPath
}
回答by Rex Kerr
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.
JVM 不允许通过引用传递指向对象的指针(这就是您在 C++ 中执行此操作的方式),因此您无法完全按照自己的意愿进行操作。
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.)
然后用户将不得不在进入的途中使用。(当然,他们会很想save("/here",Mut("/there"))放弃更改,但对于传递引用函数参数总是如此。)
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 itit 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.
对实际发生的事情没有混淆是值得的。
回答by Luigi Plinge
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:
现在您根本不需要任何代码来更改输入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.
确实,一开始有一些设置,但是对于 2.10 版中的隐式类,这将不那么重要,并且它消除了方法中的所有混乱,这正是您所担心的。
回答by Giorgio
String objects are immutable in Scala (and Java). The alternatives I can think of are:
String 对象在 Scala(和 Java)中是不可变的。我能想到的替代方案是:
- Return the result string as return value.
- Instead of using a String parameter, use a StringBuffer or StringBuilder, which are not immutable.
- 返回结果字符串作为返回值。
- 不要使用 String 参数,而是使用 StringBuffer 或 StringBuilder,它们不是不可变的。
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:
如果我理解正确,您想将该参数用作局部变量。你不能,因为所有方法参数在 Scala 中都是 val。唯一要做的就是先将其复制到局部变量中:
def save(srcPath: String, destPath: String) {
var destP = destPath
if (!destP.endsWith("/"))
destP += "/"
// do something
//
}
回答by Dylan
Here's a couple of suggestions:
这里有一些建议:
1) Update your function a bit
1)稍微更新你的功能
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
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))
回答by Dave Griffith
No, that's not allowed in Scala. Others have described some low-level workarounds (all good), but I'll add a higher-level one. For just this sort of string normalization purposes, I keep around a pimped extension to scala.String with methods like suffix, prefix, removeSuffix, and removePrefix. suffix and prefix append or prepend one string onto another, unless the suffix or prefix is already there. removeSuffix and removePrefix do the obvious, removing one string from the end or beginning of another, if it's present. Your use case would be written
不,这在 Scala 中是不允许的。其他人已经描述了一些低级别的解决方法(都很好),但我会添加一个更高级别的解决方法。仅仅为了这种字符串规范化的目的,我保留了一个带有后缀、前缀、removeSuffix 和 removePrefix 之类的方法的 scala.String 的扩展。suffix and prefix 将一个字符串附加或添加到另一个字符串上,除非后缀或前缀已经存在。removeSuffix 和 removePrefix 做的很明显,从另一个字符串的末尾或开头删除一个字符串,如果它存在的话。您的用例将被写入
val normalizedPath = destPath.addSuffix("/")
If you do a bunch of data analysis or file operations, these methods are so handy that you won't believe that you ever did without them.
如果您进行大量数据分析或文件操作,这些方法非常方便,您将无法相信没有它们您曾经做过。
回答by Sledge
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)
}

