/gist:682d4108405ab7249c7a Secret
Created
April 3, 2012 15:17
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from collections import namedtuple | |
import functools | |
from copy import copy | |
ListInsert = namedtuple('ListInsert', ['index']) | |
ListDelete = namedtuple('ListDelete', ['index']) | |
TextReplace = namedtuple('TextReplace', ['index', 'old', 'new']) | |
Compound = namedtuple('Compound', ['elements']) | |
inverses = {} | |
def inverts(ty): | |
def wrapper(fn): | |
inverses[ty] = fn | |
return fn | |
return wrapper | |
evaluators = {} | |
def evaluates(ty): | |
def wrapper(fn): | |
evaluators[ty] = fn | |
return fn | |
return wrapper | |
def inverse(value): | |
return inverses[type(value)](**value._asdict()) | |
def evaluate(value, list): | |
return evaluators[type(value)](copy(list), **value._asdict()) | |
# Inverses | |
@inverts(ListInsert) | |
def list_insert_inverse(index): | |
return ListDelete(index=index) | |
@inverts(ListDelete) | |
def list_delete_inverse(index): | |
return ListInsert(index=index) | |
@inverts(TextReplace) | |
def text_replace_inverse(index, old, new): | |
return TextReplace(index=index, | |
old=new, | |
new=old) | |
@inverts(Compound) | |
def compound_inverse(elements): | |
return Compound(elements=list(reversed([inverse(el) for el in elements]))) | |
# Evaluators | |
@evaluates(ListInsert) | |
def list_insert_eval(list, index): | |
assert index <= len(list) | |
list.insert(index, '') | |
return list | |
@evaluates(ListDelete) | |
def list_delete_eval(list, index): | |
assert index < len(list) | |
assert list[index] == '' | |
list.pop(index) | |
return list | |
@evaluates(TextReplace) | |
def text_replace_eval(list, index, old, new): | |
assert index < len(list) | |
assert list[index] == old | |
list[index] = new | |
return list | |
@evaluates(Compound) | |
def compound_eval(list, elements): | |
for el in elements: | |
list = evaluate(el, list) | |
return list | |
class ElementList(object): | |
def __init__(self): | |
self.list = [] | |
self.undo_stack = [] | |
self.redo_stack = [] | |
def append(self, value): | |
index = len(self.list) | |
self.apply(Compound(elements=[ListInsert(index=index), | |
TextReplace(index=index, | |
old='', | |
new=value)])) | |
def delete(self, index): | |
self.apply(Compound(elements=[TextReplace(index=index, | |
old=self.list[index], | |
new=''), | |
ListDelete(index=index)])) | |
def edit(self, index, new_value): | |
self.apply(TextReplace(index=index, | |
old=self.list[index], | |
new=new_value)) | |
def apply(self, action): | |
self.list = evaluate(action, self.list) | |
self.undo_stack.append(action) | |
self.redo_stack = [] | |
def undo(self): | |
action = self.undo_stack.pop() | |
self.redo_stack.append(action) | |
self.list = evaluate(inverse(action), self.list) | |
def redo(self): | |
action = self.redo_stack.pop() | |
self.undo_stack.append(action) | |
self.list = evaluate(action, self.list) | |
commands = {} | |
def handler(command): | |
def wrapper(fn): | |
commands[command] = fn | |
return fn | |
return wrapper | |
shared_list = ElementList() | |
@handler('A') | |
def cmd_add(): | |
text = raw_input('Enter text to add: ') | |
shared_list.append(text) | |
@handler('E') | |
def cmd_edit(): | |
index = int(raw_input('Enter index to edit: ')) | |
text = raw_input('Enter text to edit: ') | |
shared_list.edit(index, text) | |
@handler('D') | |
def cmd_delete(): | |
index = int(raw_input('Enter index to delete: ')) | |
shared_list.delete(index) | |
@handler('U') | |
def cmd_undo(): | |
shared_list.undo() | |
@handler('R') | |
def cmd_redo(): | |
shared_list.redo() | |
try: | |
while True: | |
command = raw_input("Enter command ('A'dd, 'E'dit, 'D'elete, 'U'ndo, 'R'edo): ").upper() | |
if command in commands: | |
commands[command]() | |
print '\n'.join(shared_list.list) | |
else: | |
print 'Unknown command' | |
except EOFError: | |
pass |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment