Как выразить контекстно-свободную грамматику дизайна как внутренний DSL в Python? - PullRequest
5 голосов
/ 01 июля 2011

[Примечание: перечитав это перед отправкой, я понял, что этот вопрос стал чем-то вроде эпоса.Спасибо, что потворствовали моему длинному объяснению причин этого преследования.Я чувствую, что если бы я был в состоянии помочь другому предпринять аналогичный проект, я бы с большей вероятностью оказался на борту, если бы знал причину вопроса.]

Я попал в Структура Synth Микаэль Хвидтфельдт Кристенсен в последнее время.Это инструмент для генерации трехмерной геометрии из (в основном) контекстно-свободной грамматики, называемой Eisenscript.Структура Synth сама по себе вдохновлена ​​Context Free Art.Контекстно-свободные грамматики могут создавать потрясающие результаты из удивительно простых наборов правил.

Мой текущий рабочий процесс Structure Synth включает в себя экспорт файла OBJ из Structure Synth, импорт его в Blender, настройку источников света, материалов и т. Д., А затем рендеринг с помощью Luxrender.,К сожалению, импорт этих OBJ-файлов часто приводит к остановке Blender, поскольку могут существовать тысячи объектов с довольно сложной геометрией.Я говорю «честно», потому что Structure Synth генерирует только базовые формы, но сфера, представленная треугольниками, все еще имеет много граней.

Таким образом, генерация структур непосредственно в Blender была бы предпочтительнее текущего процесса (глубокая поддержка Blender дляСценарии Python должны сделать это возможным).Интеллектуальная библиотека Python может использовать возможности экземпляров Blender, чтобы использовать одну сетку для создания множества объектов, тем самым экономя память.Plus Blender - это полнофункциональный 3D-пакет, и его способность интерпретировать CFDG обеспечит творческие возможности, намного превосходящие возможности Structure Synth.

И поэтому мой вопрос заключается в том, как лучше всего перевести грамматику Eisenscript в Python DSL.,Вот как выглядит простой Eisenscript:

set maxdepth 2000
{ a 0.9 hue 30 } R1 

rule R1 { 
  { x 1  rz 3 ry 5  } R1
  { s 1 1 0.1 sat 0.9 } box
}

rule R1 { 
  { x 1  rz -3 ry 5  } R1
  { s 1 1 0.1 } box
}

Чтобы объяснить, первый вызов R1 (строка 2) будет случайным образом вызывать одно из двух определений R1.Каждое определение R1 рекурсивно вызывает R1 (случайным образом вызывая одно из двух определений), а также создает блок.Первая строка убивает поколение после того, как рекурсия прошла 2000 уровней.

Джереми Ашкенас (из известности CoffeeScript) успешно реализовал контекстно-независимый DSL в Ruby , используя блоки.Внутренне он работает путем создания ключа хеш-функции для каждого «имени» правила и сохраняет блоки для каждого определения этого правила в массиве, который выбирается случайным образом при вызове правила.

Предыдущее правило Eisenscriptопределения будут переводиться в Ruby DSL следующим образом:

rule :r1 do
  r1 :x => 1, :rz => 3, :ry => 5
  box :s => [1, 1, 0.1], :sat => 0.9
end

rule :r1 do
  r1 :x => 1, :rz => -3, :ry => 5
  box :s => [1, 1, 0.1]
end

Я начинающий пользователь Python и поэтому провёл некоторое исследование возможностей функционального программирования Python.Кажется, что лямбда слишком ограничена, чтобы создавать что-то похожее на Ruby DSL Джереми, и, насколько я могу судить, лямбда - единственный вариант для анонимных функций?

Как опытный Pythonista может подойти к дизайну?

Ответы [ 2 ]

4 голосов
/ 01 июля 2011

Написание парсера для контекстно-свободной грамматики сложно. Возможно, вам лучше использовать какую-нибудь библиотеку, чтобы упростить себе задачу.

Я бы проверил модуль PyParsing . Загрузка сопровождается несколькими примерами, один из которых - простой анализатор SQL, который может показаться полезным, по крайней мере, в качестве первого шага.

3 голосов
/ 03 июля 2011

Я решил построить первоначальный прототип, переведя блоки библиотеки Ruby в предопределенные функции, которые передаются в экземпляр ContextFree, дополнены условием, позволяющим избежать бесконечных циклов, и добавлены в экземпляр как методы экземпляра.Вот текущее состояние.Критика приветствуется;это мой первый код на Python, и я готов и хочу переучить мои идиомы на Ruby до идиом Python.

import random

class ContextFree(object):
  def __init__(self):
    self.rules = {}
    # grab any instancemethod to get an instance of the instancemethod class
    self.instancemethod = type(self.add_rule)
    self.max_depth = 100
    self.depth = 0

  def add_rule(self, func, prob=1):
    rule_name = func.__name__

    if not rule_name in self.rules:
      self.rules[rule_name] = { 'funcs' : [], 'total' : 0 }

    total = self.rules[rule_name]['total']
    self.rules[rule_name]['funcs'].append([range(total,(prob+total)), func])
    self.rules[rule_name]['total'] += prob

    def augmented_func(self, options={}):
      if not self.depth >= self.max_depth:
        self.depth += 1
        pick = self.determine_rule(rule_name)
        print('Generation', self.depth)
        pick(self)

    self.__dict__[rule_name] = self.instancemethod(augmented_func, self)

  def determine_rule(self, rule_name):
    rule = self.rules[rule_name]
    winning_number = random.randrange(0, self.rules[rule_name]['total'])
    for func in rule['funcs']:
      if winning_number in func[0]:
        return func[1]

cf = ContextFree()

def box(self):
  print('Rule for box1')
  self.box()

cf.add_rule(box)

def box(self):
  print('Rule for box2')
  self.box()

cf.add_rule(box)

cf.box()

# Output:
## Generation 1
## Rule for box2
## Generation 2
## Rule for box2
## Generation 3
## Rule for box1
## Generation 4
## Rule for box2
## Generation 5
## Rule for box1
## Generation 6
## Rule for box2
## Generation 7
## Rule for box2
## Generation 8
## Rule for box1
## Generation 9
## Rule for box2
## Generation 10
## Rule for box2
## Generation 11
## Rule for box1
## Generation 12
## Rule for box1
## Generation 13
## Rule for box1
## Generation 14
## Rule for box1
## Generation 15
## Rule for box2
## Generation 16
## Rule for box1
## Generation 17
## Rule for box1
## Generation 18
## Rule for box1
## Generation 19
## Rule for box1
## Generation 20
## Rule for box1
## Generation 21
## Rule for box2
## Generation 22
## Rule for box2
## Generation 23
## Rule for box1
## Generation 24
## Rule for box2
## Generation 25
## Rule for box1
## Generation 26
## Rule for box2
## Generation 27
## Rule for box2
## Generation 28
## Rule for box1
## Generation 29
## Rule for box2
## Generation 30
## Rule for box2
## Generation 31
## Rule for box2
## Generation 32
## Rule for box2
## Generation 33
## Rule for box2
## Generation 34
## Rule for box1
## Generation 35
## Rule for box2
## Generation 36
## Rule for box1
## Generation 37
## Rule for box1
## Generation 38
## Rule for box1
## Generation 39
## Rule for box1
## Generation 40
## Rule for box2
## Generation 41
## Rule for box1
## Generation 42
## Rule for box1
## Generation 43
## Rule for box1
## Generation 44
## Rule for box1
## Generation 45
## Rule for box2
## Generation 46
## Rule for box1
## Generation 47
## Rule for box2
## Generation 48
## Rule for box1
## Generation 49
## Rule for box2
## Generation 50
## Rule for box1
## Generation 51
## Rule for box1
## Generation 52
## Rule for box1
## Generation 53
## Rule for box2
## Generation 54
## Rule for box2
## Generation 55
## Rule for box2
## Generation 56
## Rule for box2
## Generation 57
## Rule for box2
## Generation 58
## Rule for box1
## Generation 59
## Rule for box1
## Generation 60
## Rule for box1
## Generation 61
## Rule for box2
## Generation 62
## Rule for box2
## Generation 63
## Rule for box2
## Generation 64
## Rule for box1
## Generation 65
## Rule for box2
## Generation 66
## Rule for box2
## Generation 67
## Rule for box2
## Generation 68
## Rule for box2
## Generation 69
## Rule for box2
## Generation 70
## Rule for box1
## Generation 71
## Rule for box2
## Generation 72
## Rule for box2
## Generation 73
## Rule for box2
## Generation 74
## Rule for box1
## Generation 75
## Rule for box2
## Generation 76
## Rule for box1
## Generation 77
## Rule for box1
## Generation 78
## Rule for box2
## Generation 79
## Rule for box1
## Generation 80
## Rule for box2
## Generation 81
## Rule for box1
## Generation 82
## Rule for box1
## Generation 83
## Rule for box1
## Generation 84
## Rule for box1
## Generation 85
## Rule for box2
## Generation 86
## Rule for box1
## Generation 87
## Rule for box1
## Generation 88
## Rule for box2
## Generation 89
## Rule for box2
## Generation 90
## Rule for box1
## Generation 91
## Rule for box1
## Generation 92
## Rule for box1
## Generation 93
## Rule for box1
## Generation 94
## Rule for box1
## Generation 95
## Rule for box1
## Generation 96
## Rule for box2
## Generation 97
## Rule for box1
## Generation 98
## Rule for box2
## Generation 99
## Rule for box1
## Generation 100
## Rule for box2
...