Реализация функций с очень простым сценарием
Я некоторое время играл с python и решил улучшить свое обобщенное понимание языков программирования, написав пользовательский обработчик script в python. Я до сих пор успешно реализовал базовый обработчик памяти и подключил ординату адреса памяти к печати на экране. Мой вопрос может быть задан как:
Как можно реализовать функции здесь? Утверждение goto слишком просто, я хотел бы попробовать что-то более сложное. (edit) В конце концов я хочу иметь возможность делать:
f0(x, y, z):=ax^by^cz
... в оболочке, которая запускает script, который запускает этот модуль (глупый, eh?)
# notes: separate addresses from data lest the loop of doom cometh
class Interpreter:
def __init__(self):
self.memory = { }
self.dictionary = {"mov" : self.mov,
"put" : self.put,
"add" : self.add,
"sub" : self.sub,
"clr" : self.clr,
"cpy" : self.cpy,
"ref" : self.ref }
self.hooks = {self.val("0") : self.out }
def interpret(self, line):
x = line.split(" ")
vals = tuple(self.val(y) for y in x[1:])
dereferenced = []
keys_only = tuple(key for key in self.memory)
for val in vals:
while val in self.memory: val = self.memory[val]
dereferenced.append(val)
vals = tuple(y for y in dereferenced)
self.dictionary[x[0]](vals)
def val(self, x):
return tuple(int(y) for y in str(x).split("."))
def mov(self, value):
self.ptr = value[0]
def put(self, value):
self.memory[self.ptr] = value[0]
def clr(self, value):
if self.ptr in self.hooks and self.ptr in self.memory:
x = self.hooks[self.ptr]
y = self.memory[self.ptr]
for z in y: x(z)
del self.memory[self.ptr]
def add(self, values):
self.put(self.mat(values, lambda x, y: x + y))
def sub(self, values):
self.put(self.mat(values, lambda x, y: x - y))
def mat(self, values, op):
a, b = self.memory[values[0]], self.memory[values[1]]
if len(a) > len(b): a, b = b, a
c = [op(a[x], b[x]) for x in xrange(len(b))] + [x for x in a[len(a):]]
return [tuple(x for x in c)]
def cpy(self, value):
self.put(value)
def out(self, x):
print chr(x),
def ref(self, x):
self.put(x)
interp = Interpreter()
for x in file(__file__.split('/')[-1].split(".")[-2] + ".why"):
interp.interpret(x.strip())
образец script:
mov 1
put 104.101.108.108.111.10
mov 0
ref 1
clr 0
(EDIT) Я принял решение использовать эту попытку в качестве вдохновения и начать с нуля на этом проекте. (Надеюсь, я найду какое-то время, чтобы сесть и закодировать, прежде чем снова начнутся занятия.) Я намереваюсь получить лучший ответ за несколько дней. Я надеюсь, что эта информация не сможет отговорить потенциальных вкладчиков от подачи всего, что они считают полезным для такого рода проблем с кодированием.
Ответы
Ответ 1
Я немного пытаюсь понять, о чем вы спрашиваете. Где задается определение функции? В обработчике script или в script?
Если он находится в обработчике script, очевидным решением будет использование выражения lambda
. Используя пример, который вы использовали в вопросе f0(x, y, z):=x^2
, будет переведен следующим образом:
>>> f0 = lambda x, y, z : x**2
>>> f0(2,3,4)
4
Если определения функций должны быть помещены в script, вы можете уйти с комбинацией lambda
и eval
выражений. Вот краткий пример, который я только что собрал, чтобы проиллюстрировать эту идею.
class ScriptParser(object):
# See 'to_python' to check out what this does
mapping = {'^':'**', '!':' not ', '&':' and '}
def to_python(self, calc):
'''
Parse the calculation syntax from the script grammar to the python one.
This could be grown to a more complex parser, if needed. For now it will
simply assume any operator as defined in the grammar used for the script
has an equivalent in python.
'''
for k, v in self.mapping.items():
calc = calc.replace(k, v)
return calc
def feed(self, lfs):
'''
Parse a line of the script containing a function defintion
'''
signature, calc = lfs.split(':=')
funcname, variables = [s.strip() for s in signature.split('(')]
# as we stripped the strings, it now safe to do...'
variables = variables[:-1]
setattr(self, funcname,
eval('lambda ' + variables + ' : ' + self.to_python(calc)))
def main():
lines = ['f0(x, y, z) := x^2',
'f1(x) := x**2 + x**3 + x*1000']
sp = ScriptParser()
for line in lines:
sp.feed(line)
print('Script definition : %s' % line)
for i in range(5):
res0 = sp.f0(i, None, None)
res1 = sp.f1(i)
print('f0(%d) = %d' % (i, res0))
print('f1(%d) = %d' % (i, res1))
print('--------')
if __name__ == '__main__':
main()
Выполнение этих программных выходов:
Script definition : f0(x, y, z) := x^2
Script definition : f1(x) := x**2 + x**3 + x*1000
f0(0) = 0
f1(0) = 0
--------
f0(1) = 1
f1(1) = 1002
--------
f0(2) = 4
f1(2) = 2012
--------
f0(3) = 9
f1(3) = 3036
--------
f0(4) = 16
f1(4) = 4080
--------
Имейте в виду, что:
- Использование
eval
имеет последствия для безопасности, о которых вы должны знать.
- Написание собственного анализатора грамматики - это действительно классный опыт!:)
НТН,
Mac.
Ответ 2
Если вы перейдете к руководству по компилятору, он будет советовать использовать стеки при вызове методов. Это позволит вам создавать рекурсивные функции, функцию, которая вызывает другие функции, а также сохраняет переменные в правильной области.
Итак, вы используете стек для складывания переменных для каждого вызова функции, и да, используйте goto
, чтобы перейти к адресу функции. Затем используйте свой стек, чтобы получить обратный адрес функции, и состояние переменных при вызове функции. Что это.
Удачи!
Ответ 3
Не уверен, что я правильно понимаю вас, но если ваша цель - определить функцию, выполнив f0(x):=mov x
и другие сложные синтаксисы, тогда это звучит для меня как большие компоненты, которые вам не хватает, некоторые вид лексического анализа и анализатор грамматики. Как только вы уйдете от концепции "первый символ на линии, определяет, что делает строка", тогда ваш метод line.split(" ")
уже недостаточен. Это довольно сложные инструменты, и каждый язык, более сложный, чем сборка, требует этих инструментов (хотя они могут быть созданы вручную, в зависимости от языка и компилятора/интерпретатора).
В большинстве случаев проанализируйте свои входы двумя основными шагами:
1) Лексический анализ. Этот шаг принимает "x + 1/5" и переводит его в значащие символы, такие как "НОМЕР ОПЕРАТОРА НОМЕРА ПЕРЕМЕННОГО ОПЕРАТОРА". Результат этого шага используется как вход для анализатора грамматики
2) Разбор грамматики - это сложнее, и существует большая теория о лучших способах разбора грамматики. Это займет указанный выше вход и проанализирует его на дерево, которое можно оценить. Как:
Operator+
| |
| ----Variable x
Operator/
| |
1 5
У меня нет опыта использования ни одного из этих типов инструментов в Python. В С++ единственные инструменты, которые я использовал, называются flex и bison. Я уверен, что кто-то еще здесь использовал инструменты, подобные этим в python, и мог указать вам на некоторые ссылки. Похоже, в этом вопросе есть несколько: Эффективный анализатор грамматики без контекста, предпочтительно Python-friendly
Я попытался найти некоторые учебные пособия для я в концепциях, но придумал пустой. По каким-то причинам мои навыки работы с Google не включаются.
Ответ 4
Рассмотрите возможность использования pyparsing для определения вашей грамматики. В своей вики есть много examples, например интерактивный калькулятор .