Самомодифицирующийся код в Ruby

Меня беспокоит написание самомодифицирующегося кода на Ruby. И под самоизменением я имею в виду возможность писать функции, которые принимают блок кода в качестве входного значения и выводят другой блок кода на основе этого. (Я не спрашиваю об основах, таких как переопределение методов во время выполнения.)

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

_x_ = lambda { |a, b, c, d| b + c }

можно заметить, что аргументы a и d вообще не используются в теле, поэтому я хотел бы, чтобы функция, например. #strip чтобы удалить их,

x = _x_.strip

который должен дать тот же результат, что и запись:

x = lambda { |b, c| b + c }

Теперь в Лиспе это было бы легко, поскольку код Лиспа — это данные, которыми легко манипулировать. Но я не знаю, как манипулировать кодом Ruby. Я могу разобрать его, например. от

RubyVM::InstructionSequence.disassemble( x )

Но как, исходя из этого, написать измененный блок? Другими примерами того, что я хотел бы сделать, являются, например.

y = lambda { A + B }
y.deconstantize
# should give block same as saying
lambda { |_A, _B| _A + _B }

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


person Boris Stitnicky    schedule 02.02.2013    source источник
comment
У вас есть какие-то конкретные варианты использования для этого?   -  person Nevir    schedule 02.02.2013
comment
Чтобы обнаружить неиспользуемые переменные, вам придется перейти на уровень AST; вероятно, через жемчужину Мельбурна. Тем не менее, есть вероятность, что есть другой/более удобный подход, чтобы охватить ваш вариант использования.   -  person Nevir    schedule 02.02.2013
comment
@Nevir: Но, конечно, знаю. Я не турист, я не езжу туда, где мне нечего делать :-). Представьте себе следующий код: ChemicalReaction.new( name: "A_and_B_assembly", rate: proc { A * B * 0.189 } ). Я хочу облегчить задачу пользователю, поэтому я не хочу, чтобы он писал proc { |_A, _B| _A * _B * 0.189 }, что это и будет означать (0,189 - константа скорости)...   -  person Boris Stitnicky    schedule 02.02.2013
comment
Почему бы вместо этого не использовать привязки, instance_eval и друзей? В конечном итоге вы можете сказать { @a * @b * 0.189 }, но вы, вероятно, могли бы обойти это с помощью методов a и b, не так ли?   -  person mu is too short    schedule 02.02.2013
comment
Из упрямства. Некоторое время назад я решил, что имена моих сущностей будут писаться с большой буквы, например ATP, Adenosine, DeoxyCytidine. Затем я написал фальшивую константную магию, которая позволяет мне писать ATP = Chemical Species.new. А теперь из технического интереса или упрямства я считаю некрасивым писать rate: lambda { _ATP * _GDP * NDPK_constant }. Я хочу быть особенным, разрешив lambda { ATP * GDP * NDPK_constant } или хотя бы lambda { [ATP] * [GDP] * NDPK_constant }, как это делают химики, чего бы это ни стоило. Как вы должны знать, константы, написанные с заглавной буквы, фиксируются в блоках по-разному.   -  person Boris Stitnicky    schedule 02.02.2013
comment
Борис, попробуйте отредактировать их в вопросе, потому что такие большие блоки комментариев трудно читать, потому что вы не можете использовать новые строки или вкладки.   -  person sunnyrjuneja    schedule 02.02.2013
comment
@muistooshort: Итак, вы правы, практическое решение моей практической проблемы на самом деле довольно простое. Но меня бесит, что это не совсем то, что я хочу.   -  person Boris Stitnicky    schedule 02.02.2013
comment
@SunnyJuneja: Думаю, мне придется задать новый вопрос. Все это обсуждение помогло мне понять, что я не совсем правильно понял.   -  person Boris Stitnicky    schedule 02.02.2013
comment
Борис, я все еще не могу не думать об этой проблеме, так как я нахожу ее довольно интересной :) Я нашел это, которое, я думаю, может быть вам полезно, и вы могли бы придумать решение, используя это: github.com/ngty/sourcify   -  person Casper    schedule 02.02.2013
comment
Также это: github.com/seattlerb/ruby_parser   -  person Casper    schedule 02.02.2013
comment
@Casper: Я тоже нахожу это интересным, потому что функция block -> block была бы первой вещью, которую невозможно сделать в Ruby, о которой я знаю.   -  person Boris Stitnicky    schedule 03.02.2013


Ответы (2)


Борис, вам обязательно нужно полагаться на Ruby для начала?

Почему бы просто не создать свой собственный ситуационный язык, который химики могли бы использовать только для того, чтобы выражать свои формулы наиболее удобным способом. Затем вы создаете простой синтаксический анализатор и компилятор для этого «химического языка выражений».

Я имею в виду, что этот синтаксический анализатор и компилятор будут анализировать и компилировать выражения, которые химики пишут в своем коде Ruby. Тогда вы могли бы:

ChemicalReaction.new(..., "[ATP] * [GDP] * NDPK_constant")

Вуаля: максимальная гибкость.

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

person Casper    schedule 02.02.2013
comment
Я высоко ценю ваш ответ, он демонстрирует то, что фразеологи называют нестандартным мышлением. Но поверь мне, Каспер, я видел достаточно, чтобы быть уверенным, что Ruby DSL — это то, что мне нужно. Дэн Бернстайн говорит: не анализируйте. Я не хочу разбирать. Я хочу использовать парсер Ruby. Как видите, у меня уже есть несовершенное решение: использовать имена переменных, предсказуемо связанные с именами констант chem. видов за пределами блока и instance_eval блока в нужной среде. Но это не совсем идеально. Поэтому я надеюсь узнать больше, спросив всемогущего SO. - person Boris Stitnicky; 02.02.2013
comment
@BorisStitnicky Хорошо, круто. Спасибо за комментарий. Я восхищаюсь вашим упорством :) Мне нравится писать синтаксические анализаторы для таких ситуаций, но если вы можете заставить их красиво работать на Ruby, тогда, я признаю, это даже лучше. - person Casper; 02.02.2013
comment
Прочитав ваш ответ еще раз после редактирования, если никто не даст лучшего, я приму ваш. Вы правы насчет струн. Я просто как-то... я думаю, что страдаю от зависти к Лиспу :-) - person Boris Stitnicky; 02.02.2013
comment
@BorisStitnicky - Да, я подозревал, что вы тоже можете это делать :-) Но если вы МОЖЕТЕ заставить это работать. Это все еще довольно круто, но если вам не нужно прыгать через слишком много обручей, то это уже не так круто. - person Casper; 02.02.2013
comment
Спасибо за помощь в размышлениях. Кажется, я соглашусь на второй вариант, если не появится волшебник и не откроет нам глаза. - person Boris Stitnicky; 02.02.2013

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

Если у вас есть:

_x_ = ->a, b, c, d{b + c}

и предположим, что вы смогли использовать RubyVM и узнали, что a и d не используются, поэтому вы хотите создать

x = ->b, c{b + c}

из _x_. Тогда это просто:

x = ->b, c{_x_.call(nil, b, c, nil)}
person sawa    schedule 02.02.2013
comment
Хорошо, должен сказать, что мой пример с изменением арности не самый лучший, так как иногда его можно решить методом #curry, а это не то, что мне нужно. Я после того, как честно, Lisp любит самомодификацию. - person Boris Stitnicky; 02.02.2013
comment
Подождите, теперь, когда я прочитал ваш ответ ... Вы знаете, я не часто использую весь капитал, когда разговариваю в Интернете, но, КОНЕЧНО, ВЫ МОЖЕТЕ ИЗМЕНИТЬ КОД, ЖЕСТКО НАПИСАВ ДРУГОЙ КОД. ТО, О ЧЕМ Я СПРАШИВАЮ, ЭТО САМОМОДИФИЦИРУЮЩИЙСЯ КОД. ЭТО Я НЕ ПРОГРАММИСТ, А САМ ПРОГРАММА. ТЕПЕРЬ Я ЧУВСТВУЮ себя ТРУРЛОМ ИЗ РОМАНА СТЭНА ЛЕМА, ПЫТАЮЩЕГОСЯ ОБЪЯСНИТЬ АВТОМАТОНУ, КОТОРЫЙ СДЕЛАЛ ТОЛЬКО ОН, ЧТО 2 + 2 ЭТО НЕ 5, А 4. - person Boris Stitnicky; 02.02.2013
comment
Борис, я нахожу ваш вопрос интересным и полезным, но, пожалуйста, воздержитесь от написания заглавными буквами, так как это в любом случае не поможет разговору. - person sunnyrjuneja; 02.02.2013
comment
@BorisStitnicky Я не понимаю твоего комментария. - person sawa; 02.02.2013
comment
@SunnyJuneja Помимо его содержания, я также не понял намерения Бориса написать комментарий заглавными буквами. Я ждал ответа Бориса минут десять, но пока без надежды. Это выражение гнева или что? - person sawa; 02.02.2013
comment
Я думаю, он подразумевает, что вы пишете этот код вместо того, чтобы использовать для этого метапрограммирование, но я не уверен, так как думал, что вы правильно ответили на вопрос (но, возможно, я неправильно понял вопрос). Тем не менее, спасибо за все ваши вклады, Сава! Всегда приятно, когда вы отвечаете на вопрос. - person sunnyrjuneja; 02.02.2013
comment
@SunnyJuneja Спасибо. Приятно знать, что. - person sawa; 02.02.2013
comment
@SunnyJuneja: Да, именно это я и имел в виду :-) - person Boris Stitnicky; 02.02.2013
comment
@sawa: В любом случае плюс 1, спасибо, что читаете и пытаетесь помочь. Позднее Каспер указал мне несколько интересных направлений. - person Boris Stitnicky; 03.02.2013