How to undo the last method?

class Minobot:
    def __init__(self):
        self.x,self.y = 0, 0
        self.angle = 90

    def forward(self, d):
        angle = radians(90-self.angle)
        nx, ny = self.x + d * cos(angle), self.y + d * sin(angle)
        self.x, self.y = nx, ny

    def right(self):
        self.angle += 90

    def left(self):
        self.angle -= 90

    def coordinates(self):
        return round(self.x, 1), round(self.y, 1)

    def manhattan_distance(self):
        return int(abs(round(self.x, 1))+abs(round(self.y, 1)))

    def cancel(self):
        ????

Now I need to add another method to this class that cancels the call to the last method. For example: a.forward (2) => a.right () => a.cancel () This will install Minobot before using a.right ().

+4
source share
6 answers

You cannot undo the last action if you did not save it.

If you saved the last action, you can invert it. Since you know the reverse action after the action is completed, you can simply save the reverse action directly.

Let your instance Minibothave an attribute .reverse_actionthat is a tuple of the method to call and arguments to pass.

So,

def left(self):
   # Note how methods can just be stored in variables.
   self.reverse_action = (self.right, ())
   ...

def forward(self, distance):
   # Forward is its own reverse with a negative distance.
   self.reverse_action = (self.forward, (-distance,))

def revert_last(self):
   if self.reverse_action:
      (method, args) = self.reverse_action
      method(*args)  # Call the stored method, passing stored args.
      self.reverse_action = None  # Used it up.

. , , .pop() , .

, . ( google up: "Undo buffer", "Circular buffer", "Event sourcing".)

, , .. :

def save_state(self):
  self.previous_state = (self.x, self.y, self.angle)
  # Or: self.previous_states_list.append(...)

def restore_previous_state(self):
  (self.x, self.y, self.angle) = self.previous_state
  # Or: ... = self.previous_states_list.pop()

def left(self): 
  self.save_state()
  ...

.. , , , .

+4

, , , cancel .

class Minobot:
    def __init__(self):
        self.x,self.y, self.oldx, self.oldy = 0, 0
        self.angle, self.oldangle = 90

    def forward(self, d):
        self.oldx, self.oldy = self.x, self.y
        angle = radians(90-self.angle)
        nx, ny = self.x + d * cos(angle), self.y + d * sin(angle)
        self.x, self.y = nx, ny

    def right(self):
        self.oldangle = self.angle
        self.angle += 90

    def left(self):
        self.oldangle = self.angle
        self.angle -= 90

    def coordinates(self):
        return round(self.x, 1), round(self.y, 1)

    def manhattan_distance(self):
        return int(abs(round(self.x, 1))+abs(round(self.y, 1)))

    def cancel(self):
        self.angle, self.x, self.y = self.oldangle, self.oldx, self.oldy
+3

: ( , )

class Minobot:
def __init__(self):
    self.x,self.y = 0, 0
    self.angle = 90
def forward(self, d):
    angle = radians(90-self.angle)
    nx, ny = self.x + d * cos(angle), self.y + d * sin(angle)
    self.x, self.y = nx, ny
    self.undo = lambda:self.forward(-d)

def right(self):
    self.angle += 90
    self.undo = lambda:self.left()

def left(self):
    self.angle -= 90
    self.undo = lambda:self.right()

def coordinates(self):
    return round(self.x, 1), round(self.y, 1)

def manhattan_distance(self):
    return int(abs(round(self.x, 1))+abs(round(self.y, 1)))

def cancel(self):
    self.undo()

: , ,

+1

, , . , , pickle . , , . , .

#! /usr/bin/env python3
import math


class MinnowBot:
    __slots__ = '__x', '__y', '__angle', '__history'

    def __init__(self):
        self.__x = 0
        self.__y = 0
        self.__angle = 90

    def forward(self, distance):
        self.history.append(self.__getstate__())
        angle = math.radians(90 - self.__angle)
        self.__x += distance * math.cos(angle)
        self.__y += distance * math.sin(angle)

    def right(self):
        self.history.append(self.__getstate__())
        self.__angle += 90

    def left(self):
        self.history.append(self.__getstate__())
        self.__angle -= 90

    @property
    def coordinates(self):
        return round(self.__x, 1), round(self.__y, 1)

    @property
    def manhattan_distance(self):
        return round(abs(self.__x) + abs(self.__y))

    def cancel(self):
        self.__setstate__(self.history.pop())

    def __getstate__(self):
        return self.__x, self.__y, self.__angle

    def __setstate__(self, state):
        self.__x, self.__y, self.__angle = state

    @property
    def history(self):
        try:
            history = self.__history
        except AttributeError:
            # noinspection PyAttributeOutsideInit
            history = self.__history = []
        return history
+1

, , :

class Minobot:
  def __init__(self):
      self.x,self.y = 0, 0
      self.angle = 90
      self.state = self.__init__
      self.d = 0

  def forward(self, d=None):
      if d is None:
          d = self.d
      angle = radians(90-self.angle)
      nx, ny = self.x + d * cos(angle), self.y + d * sin(angle)
      self.x, self.y = nx, ny
      self.d = d
      self.state = self.forward

  def right(self):
      self.angle += 90
      self.state = self.left

  def left(self):
      self.angle -= 90
      self.state = self.right

  def coordinates(self):
      return round(self.x, 1), round(self.y, 1)

  def manhattan_distance(self):
      return int(abs(round(self.x, 1))+abs(round(self.y, 1)))

  def cancel(self):
      if self.state is None:
          pass
      else:
          state = self.state
          self.state = None
          return state()
0

, / , / .

, . , , . Stack.

In the example below I use deepcopy, but pickle.dumpscan also use. I prefer to use a stack type structure that allows you to store any number of objects / states that you want. But replacing with a rotating buffer would only allow the storage of the last n objects / states.

The advantage that I see in some other answers doesn't worry about which methods were called or the attributes were changed. It is also simple. Preserving states and frames / environments always remember me about voice recorders and drains:

from copy import deepcopy

class ObjectStack():

    def __init__(self):
        self.objects = []

    def push(self, obj):
        self.objects.append(deepcopy(obj))

    def pop(self):
        return self.objects.pop() if self.objects else None

#just an example class, not OP original class
class char():
    def __init__(self, char):
        self.char = char

stack = ObjectStack()

c = char('C')
stack.push(c) # save object

c = char('D') # somehow object changes
print(c.char)

last = stack.pop() # restore last object/state
if last is not None:
    c = last
    print(c.char)

last = stack.pop()
if last is None:
    print('No more objects/states saved')
0
source

Source: https://habr.com/ru/post/1691942/


All Articles