Scala - ссылка параметра метода mutable (var)
РЕДАКТИРОВАТЬ: Я продолжаю получать upvotes здесь. Для записи я больше не думаю, что это важно. Я не нуждался в нем, так как я разместил его.
Я хотел бы сделать следующее в 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 same: " + i);
}
Мне не нужно создавать новую переменную, и мне не нужно дважды вычислять я + 1.
Ответы
Ответ 1
Вы не можете.
Вам нужно объявить дополнительный var
(или использовать более функциональный стиль: -)).
Упрощенный пример:
def save(srcPath: String, destPath: String) {
val normalizedDestPath =
if (destPath.endsWith('/')) destPath
else destPath + '/'
// do something with normalizedDestPath
}
Ответ 2
JVM не позволяет передавать по ссылке указатели на объекты (как это делается на С++), поэтому вы не можете делать именно то, что хотите.
Один из вариантов - вернуть новое значение:
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. )
Изменить: то, что вы предлагаете, является одним из самых больших источников путаницы среди не-экспертных программистов. То есть, когда вы изменяете аргумент функции, изменяете ли вы локальную копию (pass-by-value) или оригинал (pass-by-reference)? Если вы даже не можете его изменить, довольно ясно, что все, что вы делаете, это локальная копия.
Просто сделайте это.
val destWithSlash = destPath + (if (!destPath.endsWith("/")) "/" else "")
Не стоит путаться в том, что происходит на самом деле.
Ответ 3
Возможно, вы могли бы получить систему типов, чтобы выполнить эту работу за вас, поэтому вам даже не нужно беспокоиться о добавлении косой черты каждый раз:
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, и он удаляет весь беспорядок из метода, о чем вас беспокоило.
Ответ 4
Объекты String неизменяемы в Scala (и Java). Альтернативы, о которых я могу думать, следующие:
- Вернуть строку результата в качестве возвращаемого значения.
- Вместо использования параметра String используйте StringBuffer или StringBuilder, которые не являются неизменяемыми.
Во втором сценарии у вас будет что-то вроде:
def save(srcPath: String, destPath: StringBuilder) {
if (!destPath.toString().endsWith("/"))
destPath.append("/")
// do something
//
}
ИЗМЕНИТЬ
Если я правильно понял, вы хотите использовать аргумент как локальную переменную. Вы не можете, потому что все аргументы метода - val в Scala.
Единственное, что нужно сделать, это сначала скопировать его в локальную переменную:
def save(srcPath: String, destPath: String) {
var destP = destPath
if (!destP.endsWith("/"))
destP += "/"
// do something
//
}
Ответ 5
Вот несколько предложений:
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))
Ответ 6
Нет, это не разрешено в Scala. Другие описали некоторые низкоуровневые обходные пути (все хорошо), но я добавлю более высокий уровень. Для такого рода нормализации строки я держу вокруг сутенерного расширения до Scala.String с помощью таких методов, как суффикс, префикс, removeSuffix и removePrefix. суффикс и префикс присоединяют или добавляют одну строку к другой, если суффикс или префикс уже есть. removeSuffix и removePrefix делают очевидное, удаляя одну строку с конца или начала другого, если она присутствует. Ваш прецедент будет написан
val normalizedPath = destPath.addSuffix("/")
Если вы выполняете кучу анализа данных или операции с файлами, эти методы настолько удобны, что вы не поверите, что вы когда-либо делали без них.