Поиск…


замечания

Имена значений и переменных должны быть в нижнем верблюжьем корпусе

Константные имена должны быть в верхнем верблюжьем корпусе. То есть, если член является окончательным, неизменным и принадлежит объекту пакета или объекту, его можно считать константой

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

Источник: http://docs.scala-lang.org/style/naming-conventions.html

Этот компилятор:

val (a,b) = (1,2)
// a: Int = 1
// b: Int = 2

но это не так:

val (A,B) = (1,2)
// error: not found: value A
// error: not found: value B

Это не просто val vs. var

val и var

scala> val a = 123
a: Int = 123

scala> a = 456
<console>:8: error: reassignment to val
       a = 456

scala> var b = 123
b: Int = 123

scala> b = 321
b: Int = 321
  • ссылки val неизменяемы: как final переменная в Java , после ее инициализации вы не можете ее изменить
  • ссылки var переназначаются как объявление простой переменной в Java

Неизменяемые и взаимозаменяемые коллекции

  val mut = scala.collection.mutable.Map.empty[String, Int]
  mut += ("123" -> 123)
  mut += ("456" -> 456)
  mut += ("789" -> 789)

  val imm = scala.collection.immutable.Map.empty[String, Int]
  imm + ("123" -> 123)
  imm + ("456" -> 456)
  imm + ("789" -> 789)

  scala> mut
    Map(123 -> 123, 456 -> 456, 789 -> 789)

  scala> imm
    Map()

scala> imm + ("123" -> 123) + ("456" -> 456) + ("789" -> 789)
    Map(123 -> 123, 456 -> 456, 789 -> 789)

Стандартная библиотека Scala предлагает как неизменяемые, так и изменяемые структуры данных, а не ссылку на нее. Каждый раз, когда неизменяемая структура данных получает «изменение», создается новый экземпляр вместо изменения первоначальной коллекции на месте. Каждый экземпляр коллекции может иметь значительную структуру с другим экземпляром.

Mutable and Immutable Collection (Официальная документация Scala)

Но я не могу использовать неизменность в этом случае!

Давайте возьмем в качестве примера функцию, которая принимает 2 Map и возвращает Map содержащую каждый элемент в ma и mb :

def merge2Maps(ma: Map[String, Int], mb: Map[String, Int]): Map[String, Int]

Первая попытка может быть итерирована через элементы одного из карт, используя for ((k, v) <- map) и как-то вернуть объединенную карту.

def merge2Maps(ma: ..., mb: ...): Map[String, Int] = {

  for ((k, v) <- mb) {
    ???
  }

}

Это очень первый шаг немедленно добавить Ограничить: мутацию вне что for теперь требуется. Это более ясно , когда де-обсахаривания for :

// this:
for ((k, v) <- map) { ??? }

// is equivalent to:
map.foreach { case (k, v) => ??? }

«Почему мы должны мутировать?»

foreach полагается на побочные эффекты. Каждый раз, когда мы хотим, чтобы что-то произошло в рамках foreach нам нужно «что-то побочное», в этом случае мы могли бы мутировать переменный var result или мы можем использовать изменяемую структуру данных.

Создание и заполнение карты result

Предположим, что ma и mb являются scala.collection.immutable.Map , мы могли бы создать result Map from ma :

val result = mutable.Map() ++ ma

Затем итерации через mb добавляя его элементы, и если key текущего элемента на ma уже существует, давайте переопределим его с mb one.

mb.foreach { case (k, v) => result += (k -> v) }

Мутируемая реализация

Пока что так хорошо, нам «пришлось использовать изменчивые коллекции», и правильная реализация могла бы быть:

def merge2Maps(ma: Map[String, Int], mb: Map[String, Int]): Map[String, Int] = {
  val result = scala.collection.mutable.Map() ++ ma
  mb.foreach { case (k, v) => result += (k -> v) }
  result.toMap // to get back an immutable Map
}

Как и ожидалось:

scala> merge2Maps(Map("a" -> 11, "b" -> 12), Map("b" -> 22, "c" -> 23))
  Map(a -> 11, b -> 22, c -> 23)

Складывание на помощь

Как мы можем избавиться от foreach в этом сценарии? Если все, что нам нужно делать, это в основном перебирать элементы коллекции и применять функцию, тогда как при накоплении результата на опции может использоваться .foldLeft :

def merge2Maps(ma: Map[String, Int], mb: Map[String, Int]): Map[String, Int] = {
  mb.foldLeft(ma) { case (result, (k, v)) => result + (k -> v) }
  // or more concisely mb.foldLeft(ma) { _ + _ }
}

В этом случае наш «результат» - это накопленное значение, начиная с ma , zero .foldLeft .

Промежуточный результат

Очевидно, это непреложное решение создает и уничтожает многие экземпляры Map во время сворачивания, но стоит упомянуть, что эти экземпляры не являются полным клоном Map накопленной, но вместо этого разделяют значительную структуру (данные) с существующим экземпляром.

Простая разумность

Легче рассуждать о семантике, если она более декларативная, как подход .foldLeft . Использование неизменяемых структур данных может помочь упростить нашу реализацию.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow