Scala - ссылка на параметр изменяемого (var) метода - PullRequest
40 голосов
/ 02 марта 2012

РЕДАКТИРОВАТЬ: Я продолжаю получать голосов здесь. Просто для протокола, я больше не думаю, что это важно. Я не нуждался в этом с тех пор, как отправил.

Я бы хотел следовать в 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.

Ответы [ 7 ]

29 голосов
/ 02 марта 2012

Вы не можете.

Вам придется объявить дополнительный var (или использовать более функциональный стиль: -)).

Упрощенный пример:

def save(srcPath: String, destPath: String) {
    val normalizedDestPath =
      if (destPath.endsWith('/')) destPath
      else destPath + '/'
    // do something with normalizedDestPath 
}
9 голосов
/ 02 марта 2012

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")), которое отбросит изменения, но это всегда имеет место с аргументами функции передачи по ссылке.)


Редактировать: то, что вы предлагаете, является одним из самых большихисточники путаницы среди неопытных программистов.То есть, когда вы изменяете аргумент функции, вы модифицируете локальную копию (передача по значению) или оригинал (передача по ссылке)?Если вы не можете даже изменить его , совершенно очевидно, что все, что вы делаете, является локальной копией.

Просто сделайте это таким образом.

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

Оно того стоитпутаницы в том, что на самом деле происходит.

6 голосов
/ 03 марта 2012

Может быть, вы могли бы заставить систему типов выполнить эту работу за вас, так что вам даже не нужно беспокоиться о добавлении косой черты каждый раз:

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, и он удаляет весь беспорядок из метода, о чем вы беспокоились.

1 голос
/ 02 марта 2012

Строковые объекты неизменны в Scala (и Java).Я могу придумать следующие варианты:

  1. Возвращать строку результата в качестве возвращаемого значения.
  2. Вместо использования параметра String используйте StringBuffer или StringBuilder, которые не являются неизменяемыми.1006 *

Во втором сценарии у вас будет что-то вроде:

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
    //
}
0 голосов
/ 02 февраля 2018

Я знаю, что это старый вопрос, но если вы просто хотите повторно использовать имя аргумента, возможно:

def save(srcPath: String, destPath: String) {
  ((destPath: String) => {
    // do something
  })(if (!destPath.endsWith('/')) destPath + '/' else destPath)
}
0 голосов
/ 02 марта 2012

Нет, это не разрешено в Scala. Другие описали некоторые обходные пути низкого уровня (все хорошо), но я добавлю высокоуровневый. Для целей такого рода нормализации строк я использую расширение scala.String с такими методами, как суффикс, префикс, removeSuffix и removePrefix. суффикс и префикс добавляют или добавляют одну строку к другой, если суффикс или префикс уже там. removeSuffix и removePrefix делают очевидное, удаляя одну строку из конца или начала другой, если она присутствует. Ваш вариант использования будет написан

val normalizedPath = destPath.addSuffix("/") 

Если вы выполняете кучу анализа данных или файловых операций, эти методы настолько удобны, что вы даже не поверите, что когда-либо обходились без них.

0 голосов
/ 02 марта 2012

Вот несколько советов:

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))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...