Определить по параметру, какой элемент изменить
Следующий код Scala дает ошибку компиляции, утверждая, что я не могу присвоить val:
Упрощенный пример:
class State {
val a = 1
val b = 2
def compute( res: =>Int, add : Int ): Unit = {
res = add + 123456
}
compute(a,b)
compute(b,a)
}
Пример ближе к моему реальному использованию:
class Editor {
var str:String = ""
var cursor:Int = 0
case class UndoState(str:String, cursor:Int)
var undoState = Seq[UndoState]()
var redoState = Seq[UndoState]()
def undo(): Unit = if (undoState.nonEmpty) {
redoState = UndoState(str,cursor) +: redoState
str = undoState.head.str
cursor = undoState.head.cursor
undoState = undoState.tail
}
def redo(): Unit = if (redoState.nonEmpty) {
undoState = UndoState(str,cursor) +: undoState
str = redoState.head.str
cursor = redoState.head.cursor
redoState = redoState.tail
}
}
Так как оба отмены / повторения очень похожи, я хотел бы извлечь общий код в функцию, которую я хотел передать как пары источник / цель как redoState
/undoState
и наоборот.
Есть ли какой-то способ, как сказать, где функция должна хранить что-то? (В C++ я бы передал указатель на член в этой ситуации).
3 ответа
Используйте возвращаемое значение:
def compute( add : Int ): Int = {
add + 123456
}
val a = compute(b)
val b = compute(a)
передача по ссылке, как вы делали бы это в C++, не может быть выполнена в Scala и, как правило, это не то, что вы хотите делать. Однако вы можете передать контейнер, который содержит ссылку на изменяемое поле:
class Box(var value: Int)
def compute( box: Box, add : Box): Unit = {
box.value = add.value + 123456
}
val a = new Box(1)
val b = new Box(2)
compute(a, b)
compute(b, a)
Или (небольшое изменение) сделать compute
член Box
:
class Box(var value: Int) {
def compute(add: Box): Unit = {
value = add.value + 123456
}
}
val a = new Box(1)
val b = new Box(2)
a.compute(b)
b.compute(a)
Вы можете создавать и передавать функции для установки нового состояния (отменить или повторить):
...
var undoState = Seq[UndoState]()
var redoState = Seq[UndoState]()
def anywaydo(set: (Seq[UndoState]) => Unit) {
set(...)
...
}
def undo {
anywaydo((state) => undoState = state)
}
Вы можете сделать свои состояния (изменяемые) стеками вместо (неизменяемых) последовательностей и просто передать их в общую функцию для манипуляции:
def undoredo(states: (Stack[UndoState], Stack[UndoState])): Unit = states match {
case (Stack(), _) => ()
case (from, to) =>
to.push(UndoState(str,cursor))
val state = from.pop
str = state.str
cursor = state.cursor
}
def undo() = undoredo(undoState -> redoState)
def redo() = undoredo(redoState -> undoState)
Или, если вам нравятся необычные "DSL-подобные" функции scala, вы можете сделать это в увлекательной игровой форме, например:
implicit class StateStack(from: Stack[UndoState]) {
def ->(to: Stack[UndoState]): Unit = if(from.nonEmpty) {
to.push(UndoState(str,cursor))
val state = from.pop
str = state.str
cursor = state.cursor
}
}
Затем вы можете делать такие вещи, как undoState -> redoState
для "отмены" или redoState -> undoState
для "повторить" ...