Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to cancel the last method called?

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 last method called. For example: a.forward(2) => a.right() => a.cancel() This would set the Minobot before the a.right() is used.

like image 551
Nejc Osterman Avatar asked Jan 03 '23 23:01

Nejc Osterman


2 Answers

You cannot cancel the last action unless you store it.

If you store the last action, you can invert it. Since you know the reverse action once you take an action, you can just store the reverse action directly.

Let your Minibot instance have a .reverse_action attribute which 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.

This has an obvious downside of being able to only revert one last action. If you store a list of reverse actions for each action you take, you can .pop() from it and revert actions as long as there are any stored reverse actions in the list.

You can store only last several actions if you're taking great many actions and are memory-constrained. (Terms to google up: "Undo buffer", "Circular buffer", "Event sourcing".)

Another approach would be storing the previous state, that is, coordinates, heading, etc. Undoing the last action would be then just switching to the previous state:

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()
  ...

This approach is free from rounding errors, etc. It takes more memory, though, especially as your state grows large, and when you want to save an entire history of previous states.

like image 173
9000 Avatar answered Jan 05 '23 17:01

9000


Rather than saving the reversing action as other answers have suggested, you can save all the properties, and cancel can restore them.

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
like image 21
Barmar Avatar answered Jan 05 '23 17:01

Barmar