Как я могу получить синтаксическое дерево из coderef в Perl?

Я хотел бы проверять и управлять кодом произвольных процедур Perl (полученных с помощью coderefs) в Perl. Есть ли для этого инструмент / модуль / библиотека? Что-то похожее на B :: Concise, за исключением того, что B :: Concise печатает код на выходе, но я хотел бы проверить его программно.

Я бы хотел использовать его вот так. Учитывая кодовую ссылку F, которая называется, например. с 10 аргументами:

$ret = &$F(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10);

Я хочу создать функцию F1, ул.

&$F(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10) == 
  &$F1(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10)*
  &$C(x2, x3, x4, x5, x6, x7, x8, x9, x10)

то есть «разложить» его на две части, где вторая не зависит от x1, а первая максимально проста (я предполагаю, что F сконструирован как огромный продукт).

Приложение, для которого я хочу это сделать, - это оптимизация алгоритма выборки метрополии - предположим, я Пробую раздачу p(x1 | x2 = X1, x3 = X3, ...) = f(x1, x2, x3, ...). Сам алгоритм инвариантен относительно. мультипликативные постоянные множители и другие переменные не изменяются с помощью алгоритмов, поэтому часть, не зависящая от x1 (т.е. $c сверху), вообще не нуждается в оценке).

Совместная вероятность может иметь, например. следующая форма:

  p(x1, x2, x3, x4, x5) = g1(x1, x2)*g2(x2, x3)*g3(x3, x4)*g4(x4, x5)*g5(x4, x1)*g6(x5, x1)

Я также рассматриваю создание p как объекта, состоящего из факторов с аннотациями переменных, от которых зависит тот или иной фактор. Даже это выиграет от самоанализа кода (автоматического определения переменных).


person jpalecek    schedule 05.10.2010    source источник


Ответы (3)


Для самоанализа оптодеревьев обычно используется семейство модулей B.

Учитывая ссылку на код $cv, сначала создайте для этого объект B:

my $b_cv = B::svref_2object($cv);

Теперь вы можете вызывать различные методы, описанные в B, для получения различных данных из optree.

Используя только оптоволоконный самоанализ, вы уже можете добиться удивительных результатов. См. DBIx::Perlish, где приведен довольно сложный пример этого.

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

Фактическое создание optree и манипуляции с ним обычно лучше всего выполнять с помощью Perl C api, как описано в perlapi, _ 10_ и perlhack и другие. Вам, вероятно, также придется изучить некоторые XS, чтобы предоставить функции манипулирования optree, которые вы написали обратно в пространство Perl, но это простая часть В самом деле.

Построение дополнительных деревьев (не обязательно основанных на других существующих деревьях, которые подвергаются интроспекции), похоже, в последнее время стало довольно популярным, особенно после того, как Syntax Plugins были добавлены в ядро ​​в perl 5.12.0. Вы можете найти различные примеры, такие как Scope::Escape::Sugar на cpan.

Однако работа с дополнительными деревьями Perl по-прежнему несколько сложна и не совсем удобна для новичков. В этом не должно быть необходимости ни для каких самых загадочных вещей. Что-то вроде использования B::Deparse->new->coderef2text($cv) и затем, возможно, очень небольшого искажения оцененного исходного кода, действительно настолько, насколько я хотел бы пойти с интроспекцией optree из пространства чистого Perl.

Возможно, вы захотите немного отступить и объяснить реальную проблему, которую вы пытаетесь решить. Может быть, есть гораздо более простое решение, которое вообще не связано с опциями.

person rafl    schedule 05.10.2010
comment
+1 хороший ответ, и Scope::Escape::* выглядит очень интересно. Какие еще хорошие вы порекомендуете? - person Eric Strom; 05.10.2010
comment
Спасибо. Хотя, к сожалению, это не очень помогло мне в понимании вашей реальной проблемы, и это полностью моя вина - ваши разъяснения кажутся хорошими для человека с правильным опытом. Так что, не имея никаких предложений о том, как лучше подойти к вашей проблеме, я был бы рад помочь вам проанализировать любой код, с которым вы столкнетесь. Но для этого вам нужно будет показать реальный код. - person rafl; 05.10.2010
comment
Я не могу сейчас вспомнить других пользователей синтаксических плагинов на CPAN. Тем не менее, переключение оптоволоконного дерева в целом является относительно распространенным явлением. Вам может показаться интересным Parse :: Perl и многие B::Hooks::OP::Check зависимые. Предыдущая попытка сделать то, что сейчас предоставляют плагины синтаксиса, - это Devel :: Declare. Вы также найдете множество интересных модулей, предоставляющих в основном новый синтаксис, но также и основанную на нем новую семантику. - person rafl; 05.10.2010
comment
@Eric Strom: Другой пример модуля может быть Text::Xslate. Я считаю, что это компилирует шаблон прямо до кода операции. - person draegtun; 05.10.2010

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

  1. Создайте объект, представляющий экземпляр вашего вычисления.
  2. Напишите методы этого объекта, необходимые для оценки значения вычисления. Никакого кодогенератора, просто делайте это долгим и медленным путем. Это просто для того, чтобы дать вам базовый план кода для следующих шагов, который легко протестировать и, надеюсь, легко понять.
  3. Напишите тесты, чтобы убедиться в правильности того, что вы сделали на шаге 2. (Замените это перед шагом 2, если вы такой человек).
  4. Реализуйте то, о чем вы спрашиваете в этом вопросе, написав методы для преобразования объекта вычисления в новый, который представляет более оптимизированную форму того же вычисления. Используйте свои тесты, чтобы убедиться, что вычисления по-прежнему дают правильный результат после оптимизации.
  5. Напишите код, который принимает объект вычисления и генерирует подпрограмму (либо с помощью строки eval, либо с использованием B), которая выполняет это вычисление. Используйте свои тесты, чтобы убедиться, что вычисления по-прежнему дают правильный результат после того, как они были скомпилированы.

Необязательный шаг для вставки от 2 до 5:

  • Напишите немного синтаксического сахара (возможно, используя overload, но возможны и другие инструменты), чтобы вы могли создавать «вычислительные объекты», используя красивые выражения, напоминающие само вычисление, вместо множества конструкторов объектов.
person hobbs    schedule 05.10.2010

Perl 5 не позволяет вам манипулировать байт-кодом на лету таким образом, но вы можете создавать анонимные функции. Если я правильно понимаю ваш пример, а я сомневаюсь, что это так, у вас уже есть две функции, на которые ссылаются $f1 и $c, и вы хотите создать новую ссылку $f, которая содержит результаты первых двух, умноженные друг на друга. Это просто:

my $f = sub { $f1->(@_) * $c->(@_[1 .. 9]) };

$f->(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

Обратите внимание на использование оператора стрелки, а не & для разыменования кодовых ссылок. Этот стиль гораздо более распространен (и, на мой взгляд, более читабелен).

person Chas. Owens    schedule 05.10.2010
comment
На самом деле, я хочу обратное - учитывая f, определите, что такое f1. - person jpalecek; 05.10.2010