2012-08-02 5 views
3

Я пытаюсь создать очень простой скрипт, который использует TkInter для рисования линий на основе ввода пользователем. Пользователь выполнит сценарий через командную строку, используя параметры для определения линий.Создание словаря синтаксического анализа для использования с sys.argv

Возможные команды:

(# signifies any whole number the user chooses) 

P # (selects a pen to draw with) 
D  (pen down/starts drawing) 
N # (moves the pen north # increments) 
E # (moves the pen east # increments) 
S # (moves the pen south # increments) 
W # (moves the pen west # increments) 
U  (pen up/stops drawing) 

Пример:

Parser.py P 3 D N 4 E 2 S 4 W 2 U 

Приведенный выше пример будет использовать высокий прямоугольник.

Я пытаюсь создать базовый парсер для sys.argv, который будет проходить через все аргументы и выполнять методы из словаря на основе указанных аргументов и его порядка.

Вот мой код, который проходит через sys.argv и создает массив на основе аргументов (я буду добавлять проверки ошибок, так что просто взять на себя счастливый день сценария сейчас):

class JoshSourceReader(object): 
    """ responsibe for providing source text for parsing and drawing 
     Initiates the Draw use-case. 
     Links to a parser and passes the source text onwards """ 
    def __init__(self): 
     self.parser = JoshParser() 
     self.source = [] 
    def go(self): 
     i = 1 
     x = len(sys.argv) 
     while i < x: 
      if sys.argv[i]=="P": 
       self.source.append("P " + str(sys.argv[i + 1])) 
       i += 2 
      elif sys.argv[i]=="D": 
       self.source.append("D") 
       i += 1 
      elif sys.argv[i]=="N": 
       self.source.append("N " + str(sys.argv[i + 1])) 
       i += 2 
      elif sys.argv[i]=="E": 
       self.source.append("E " + str(sys.argv[i + 1])) 
       i += 2 
      elif sys.argv[i]=="S": 
       self.source.append("S " + str(sys.argv[i + 1])) 
       i += 2 
      elif sys.argv[i]=="W": 
       self.source.append("W " + str(sys.argv[i + 1])) 
       i += 2 
      elif sys.argv[i]=="U": 
       self.source.append("U") 
       i += 1 
     self.parser.parse(self.source) 

Таким образом, массив, который будет генерироваться из моего примера выше будет выглядеть следующим образом:

source=['P 3', 'D', 'N 4', 'E 2', 'S 4', 'W 2', 'U'] 

Так что мой вопрос заключается в следующем: Как создать эффективный словарь методов, который работает с выше массивом и выполняет методы, основанные на лучей, один за другим? Я даже начинаю сомневаться, если это возможно из-за того, что некоторым методам потребуются свойства, прошедшие (число), а некоторые - нет. Кто-нибудь может пролить свет на этот вопрос?

И, пожалуйста, имейте в виду, что я очень новый для Python.

EDIT: Я забыл одну важную информацию. N, E, S, W все относятся к одному и тому же методу, но каждый раз он принимает разные аргументы. Вот метод (еще не рисовать):

def drawLine(self, direction, distance): 
    print("drawing line of length " + str(distance) + " at " 
      + str(direction)) 

Так что, если бы я вызвать этот метод с использованием «S 3», я бы назвал это так:

drawer.drawLine(180, 3) 

И если бы я вызвать его, используя «W 1», я бы назвал это так:

drawer.drawLine(270, 1) 

ответ

1

Это немного сложная проблема, которая, вероятно, лучше всего решена при использовании достойного синтаксического анализа. Обычно я рекомендую пираринг для таких вещей. Однако вот решение, которое не использует никаких внешних фреймворков.

Это несколько уродливое, но в основном оно анализирует входную строку, скажем, «P 1» в операцию и целое число. Словарь содержит метод вызова, а также любые дополнительные параметры, которые должны быть переданы методу (для случая drawLine()).

Я сделал это без использования класса, но просто добавив self, поскольку первым параметром для всего должен быть выбор.

def selectPen(pen): 
    print('called selectPen with pen', pen) 

def penDown(): 
    print('called penDown') 

def drawLine(direction, length): 
    print('called drawLine with direction', direction, 'and length', length) 

def penUp(): 
    print('called penUp') 

def parseSource(source): 
    tempSource = [op.split(' ') for op in source] 
    parsedSource = [] 
    for op in tempSource: 
     parsedOp = [] 
     for i, el in enumerate(op): 
      if i == 0: 
       parsedOp.append(el) 
      else: 
       try: 
        parsedOp.append(int(el)) 
       except ValueError: 
        parsedOp.append(el) 
     parsedSource.append(tuple(parsedOp)) 

    return parsedSource 

def dispatch(parsedSource): 
    opDict = {'P':(selectPen,), 'D':(penDown,), 'N': (drawLine, 0), 'S':(drawLine, 180), 
    'E': (drawLine, 90), 'W': (drawLine, 270), 'U': (penUp,)} 

    for op in parsedSource: 
     methodName = op[0] 
     methodToCall = opDict[methodName][0] 
     args = op[1:] 
     if len(opDict[methodName])>1: 
      args = opDict[methodName][1:] + args 

     methodToCall(*args) 

if __name__ == '__main__': 
    source=['P 3', 'D', 'N 4', 'E 2', 'S 4', 'W 2', 'U'] 
    parsedSource = parseSource(source) 
    dispatch(parsedSource) 
+0

Абсолютно совершенный. Я думаю, что это займет у меня некоторое время, прежде чем я полностью пойму код, но я стараюсь изучить его, пока не сделаю. И я использую Python 3.x, но легко менял заявления печати. – Djentleman

+0

Я изменил его в любом случае, для дальнейшего использования. –

0

Это не ответ, но расширенный комментарий на ваш код. Я предполагаю, что в прошлом вы работали на C или C-подобном языке (Java, JavaScript и т. Д.). Вы перебор sys.argv так:

i = 1 
x = len(sys.argv) 
while i < x: 
    if sys.argv[i]=="P": 

В Python, есть более простые способы перебора списка. Например:

for arg in sys.argv: 
    if arg == 'P': 
+1

Я знаю об этом, но, делая это таким образом, вы сделали бы самые важные части моего кода сломанными. 'self.source.append (« P »+ str (sys.argv [i + 1]))' больше не будет работать, так как 'i' больше не используется для итерации через' sys.argv'. – Djentleman

+0

Взгляните на мой другой ответ на альтернативу ... – larsks

+0

Пожалуйста, используйте 'для i, arg в enumerate (argv):' вместо любого из вышеупомянутых решений (или ваших собственных). –

2

Вы можете указать аргументы по умолчанию для функций. Итак, просто сделайте примерно следующее:

parsedict = {} 
def func_P(times=0): 
    #do whatever here 
parsedict["P"] = func_P 
#repeat however many times 
for x in source: 
    splitx = x.split(" ") 
    if len(splitx) >= 2: 
     parsedict[splitx[0]](splitx[1]) 
    else: 
     parsedict[splitx[0]]() 

Если вам нужно что-нибудь еще, просто прокомментируйте. Должен работать, но я не тестировал.Вы также можете использовать lambda functions, но вы сказали, что новичок в Python, поэтому я определил функцию вручную.

+0

Я забыл одну важную часть информации, которую я редактировал в нижней части вопроса. Сожалею! – Djentleman

0

Вы могли бы сделать что-то вроде этого, где словаря карты «команда» к числу аргументов, которые они принимают:

argdict = { 
    'P': 1, 
    'D': 0, 
    'N': 1, 
    'E': 1, 
    'S': 1, 
    'W': 1, 
    'U': 0 
} 

args = sys.argv[1:] 
source = [] 

while args: 
    command = [args.pop(0)] 
    nargs = argdict[command[0]] 
    command.extend(args[:nargs]) 
    args = args[nargs:] 
    source.append(' '.join(command)) 

print source 

Учитывая ваш вклад образца, это будет создать список, который выглядел как:

['P 3', 'D', 'N 4', 'E 2', 'S 4', 'W 2', 'U'] 
+0

Это не отвечает на мой вопрос, но по-прежнему очень полезный код, так что спасибо. – Djentleman

0
class Painter(object):                
    pencil_width = 1 
    is_drawing = False 

    def set_pencil(self, width=1): 
     self.pencil_width = int(width) 
    P = set_pencil 

    def draw_north(self, blocks=1): 
     if self.is_drawing: 
      ... 
    N = draw_north 

    def process_command(self, str_): 
     func_name = str_.split(' ')[0] 
     args = str_.split(' ')[1:] 
     try: 
      func = getattr(self, func_name) 
     except AttributeError: 
      raise ValueError('Method %s does not exists' 
           % func_name) 
     func(*args) 


painter = Painter() 
for command in source: 
    painter.process_command(command) 
Смежные вопросы