Есть ли в Scala способ удалить изменяемые переменные или можно сохранить изменяемые переменные в приведенном ниже случае?

Я понимаю, что Scala полностью поддерживает неизменность.

Теперь я думаю о сценарии, в котором мне нужно удерживать некоторое состояние (через переменные) в классе или что-то в этом роде. Мне нужно будет обновить эти переменные позже; затем я могу вернуться к классу позже, чтобы получить доступ к обновленным переменным.

Я попытаюсь упростить это на одном очень простом примере:

class A {
  var x: Int
  def compute: Int = {calling some other processes or such using x as input}
}

......

def invoker() {
  val a: A = new A
  a.x = 1
  ......
  val res1 = a.compute
  a.x = 5
  ......
  val res2 = a.compute
  ......
}

Итак, вы видите, мне нужно продолжать изменять x и получать результаты. Если вы утверждаете, что я могу просто оставить x в качестве аргумента для вычислений, таких как

def compute(x: Int) 
......

Это хорошая идея, но я не могу сделать это в моем случае, так как мне нужно разделить значение параметра x и полностью вычислить результат. Другими словами, установка значения x не должна запускать «вычисление», скорее, мне нужно иметь возможность установить значение x в любое время в программе и иметь возможность повторно использовать значение для вычисления в любое другое время в программе, когда мне это нужно. .

В этом случае я использую переменную (var x: Int). Является ли это законным или есть еще какой-то неизменный способ справиться с этим?


person Xiao Bing Huang    schedule 30.05.2013    source источник
comment
Когда вам нужно установить значение x? Не могли бы вы сделать так, чтобы кусок кода, вызывающий compute, считывал значение x с того места, где оно обычно устанавливается? Обратите внимание, что есть некоторые случаи, когда вы не можете написать полностью неизменяемую программу, особенно когда задействован ввод-вывод.   -  person gzm0    schedule 31.05.2013
comment
Мне нужно иметь возможность установить значение x в любое время. Я бы оставил переменную x с методом вычисления в том же классе, чтобы я мог передать весь класс для других целей. Так что да, я думаю, вы правы, в некоторых случаях полностью неизменяемая программа нереалистична.   -  person Xiao Bing Huang    schedule 31.05.2013


Ответы (5)


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

В вашем случае вы хотите хранить x и вычислять отдельно. По сути, это означает, что требуется состояние, поскольку результаты вычисления зависят от состояния x.

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

person tsjnsn    schedule 30.05.2013
comment
Спасибо, что поделились идеей, я думаю так же, но просто хочу подтвердить :) - person Xiao Bing Huang; 31.05.2013

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

Затем, по определению, вы хотите, чтобы ваш класс сохранял состояние. Вы можете реструктурировать свою проблему так, чтобы этот конкретный класс не требовал состояния, но вам придется выяснить, полезно ли это и/или стоит ли хлопот.

person Cubic    schedule 30.05.2013
comment
Верно, я не собираюсь перерабатывать код, а просто хочу добиться наилучшего с минимальными усилиями по написанию кода. - person Xiao Bing Huang; 31.05.2013

Например, ваш шаблон используется в ListBuffersize в качестве вашей функции compute).

Так что да, могут быть случаи, когда вы можете использовать этот шаблон по уважительным причинам. Пример:

val l = List(1, 2, 3)

val lb = new ListBuffer[Int]
l.foreach(n => lb += n * n)
val result = lb.toList

println(result)

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

Так что другой вариант

val l = List(1, 2, 3)

val result = l.map(n => n * n)

println(result)

который делает то же самое с меньшим количеством строк. Я предпочитаю этот стиль, потому что вы просто смотрите на неизменяемые экземпляры и «функциональные» функции.

В вашем абстрактном примере вы можете попытаться разделить изменяемое состояние и функцию:

class X(var i: Int)

class A {
  def compute(x: X): Int = { ... }
}

возможно даже

class X(val i: Int)

Таким образом, compute становится функциональным: возвращаемое значение зависит только от параметра.

Мой личный фаворит в отношении «неожиданного» неизменного класса — scala.collection.immutable.Queue. С «императивным» фоном вы просто не ожидаете, что очередь будет неизменной.

Итак, если вы посмотрите на свой шаблон, вполне вероятно, что вы можете изменить его на неизменяемый.

person Beryllium    schedule 31.05.2013

Я бы создал неизменяемый класс A (здесь это класс case) и позволил бы объекту обрабатывать изменчивость. Для каждого изменения состояния мы создаем новый объект A и меняем ссылку в объекте. Это немного лучше обрабатывает параллелизм, если вы устанавливаете x из другого потока, вам просто нужно сделать переменную volatile или AtomicReference.

object A {
    private[this] var a = A(0)
    def setX(x: Int) { if (x != a.x) a = new A(x) }
    def getA: A = a
}

case class A(x: Int) {
    def compute: Int = { /*do your stuff*/ }
}
person lasantha    schedule 31.05.2013
comment
setX должен быть synchronized ? Кроме того, какова цель приватизации this, а не только private? - person Sudheer Aedama; 19.03.2016

После еще нескольких месяцев функционального программирования, вот мое переосмысление.

Каждый раз, когда переменная модифицируется/изменяется/обновляется/мутируется, императивным способом обработки этого является запись такого изменения прямо с этой переменной. Функциональный образ мышления заключается в том, чтобы деятельность (вызвавшая изменение) приносила вам новое состояние. Другими словами, это похоже на причинно-следственную связь. Функциональный образ мышления фокусируется на переходе деятельности между причиной и следствием.

При этом в любой момент времени выполнения программы наше достижение является промежуточным результатом. Нам нужно где-то хранить результат независимо от того, как мы это делаем. Таким промежуточным результатом является состояние, и да, нам нужна какая-то переменная для его хранения. Вот чем я хочу поделиться с просто абстрактным мышлением.

person Xiao Bing Huang    schedule 12.11.2013