Reduce Commands¶
Reduction is a simplification of commands that came from the same function but with (possibly) different arguments.
Why reduction, and how?¶
Reduction is needed in several cases. For example, if a parameter changes continuously, such as moving a point by pushing an arrow key for a long time, you should not record all the intermediate state.
from collections_undo import UndoManager, arguments as args
class A:
mgr = UndoManager()
def __init__(self):
self.pos = 0
def move(self, x):
self._move_to(x, self.pos)
@mgr.undoable
def _move_to(self, x, x_old):
self.pos = x
@_move_to.undo_def
def _move_to(self, x, x_old):
self.pos = x_old
@_move_to.reduce_rule
def _move_to_reduce_rule(self, args_old, args_new):
return args(args_new["x"], args_old["x_old"])
The function wrapped by reduce_rule() will be called to concatenate two commands,
in which args_old and args_new are the arguments of the two commands.
Reduction rule must return a new argument that will be used to create a new command. In
the example above, args_new["x"] and args_old["x_old"] correspond to x and
x_old respectively.
The type of args_old (and args_new) is an immutable, sequential mapping object.
You can also get any of the arguments by the key, index or unpacking.
def _move_to_reduce_rule(self, args_old, args_new):
return args(args_new["x"], args_old["x_old"])
def _move_to_reduce_rule(self, args_old, args_new):
return args(args_new[0], args_old[1])
def _move_to_reduce_rule(self, args_old, args_new):
x_old, _ = args_old
_, x_new= args_new
return args(x_new, x_old)
This reduction mode will be activated by calling mgr.set_reducing(True) or temporarily
by with mgr.reducing().
a = A()
with a.mgr.reducing():
# moving from 0 to 1, 2, and finally 3
a.move(1)
a.move(2)
a.move(3)
Here pos is set to 3, but single undo will revert pos to 0.
Default reduction rule¶
In the server/receiver framework and property-like framework, reduction rule is defined by default.
from collections_undo import UndoManager, arguments as args
class A:
mgr = UndoManager()
def __init__(self):
self.pos = 0
@mgr.interface
def move(self, x):
self.pos = x
@move.server
def move(self, x):
return args(x)
and the code below works
a = A()
with a.mgr.reducing():
a.move(1)
a.move(2)
a.move(3)