Самоизменяющийся код в Ruby перезагрузил
Следуя моему предыдущему вопросу, позвольте мне быть более точным и менее запутанным в том, что именно я хочу. Я написал химический пакет, в котором принял решение, что названия всех химических веществ и реакций будут написаны заглавными буквами, например, "АТФ", "Аденозин", "Дезоксицитидин" и т. Д. Это позволило мне написать, например:
ATP = ChemicalSpecies.new initial_concentration: 225.0 # in micromolars
GDP = ChemicalSpecies.new initial_concentration: 75.0 # in micromolars
Теперь, если АТФ используется для фосфорилирования ВВП, используется фермент НДПК с каталитической константой
NDPK_constant = 0.6
это я хочу записать как:
ChemicalReaction.new name: ATP_GDP_phosphate_exchange,
rate: lambda { ATP * GDP * NDPK_constant }
Я мог бы просто написать:
ChemicalReaction.new name: ATP_GDP_phosphate_exchange,
reactants: [ ATP, GDP ],
rate: lambda { |reactant_1, reactant_2| reactant_1 * reactant_2 * NDPK_constant }
Но это кажется слишком влажным для меня. Смотри как reactant_1
, reactant_2
повторить дважды, пока ATP
, GDP
имея в виду. Простое решение будет:
ChemicalReaction.new name: ATP_GDP_phosphate_exchange,
rate: lambda { _ATP * _GDP * NDPK_constant }
А также instance_eval
блок определения скорости в контексте, который определяет _ATP
а также _GDP
как концентрации ATP
, GDP
, Это очень, очень близко, но не совсем то, чего я хочу, и это бесит меня. Я мог бы даже использовать RubyVM
чтобы узнать, какие химикаты используются внутри блока, рассмотрите, например,.
require 'ap' # (awesome_print, like pretty_print but fancier, install if you don't have)
ap RubyVM::InstructionSequence.disassemble( lambda { _ATP * _GDP * NDPK_constant } ).split( "\n" )
#=> [ 0] "== disasm: <RubyVM::InstructionSequence:block in irb_binding@(irb)>=====",
[ 1] "== catch table",
[ 2] "| catch type: redo st: 0000 ed: 0027 sp: 0000 cont: 0000",
[ 3] "| catch type: next st: 0000 ed: 0027 sp: 0000 cont: 0027",
[ 4] "|------------------------------------------------------------------------",
[ 5] "0000 trace 1 ( 22)",
[ 6] "0002 putself ",
[ 7] "0003 send :_ATP, 0, nil, 24, <ic:0>",
[ 8] "0009 putself ",
[ 9] "0010 send :_GDP, 0, nil, 24, <ic:1>",
[10] "0016 opt_mult <ic:5>",
[11] "0018 getinlinecache 25, <ic:3>",
[12] "0021 getconstant :NDPK_constant",
[13] "0023 setinlinecache <ic:3>",
[14] "0025 opt_mult <ic:6>",
[15] "0027 leave
Анализируя это, вы узнаете, какие имена находятся внутри: _ATP
а также _GDP
, Но, как я уже сказал, из упрямства я нахожу _ATP
, _GDP
некрасиво. Я хочу сказать просто ATP
, GDP
или возможно [ATP]
, [GDP]
потому что химики используют скобки для концентраций. Я знаю, что это то, что Юсуке Эндо называет ограниченным кодированием. У меня вопрос, можно ли победить любой из этих двух желаемых синтаксисов? Например, имея закрытие lambda { ATP * GDP * NDPK_constant }
Разборка дает:
ap RubyVM::InstructionSequence.disassemble( lambda { ATP * GDP * NDPK_constant } ).split( "\n" )
#=> [ 0] "== disasm: <RubyVM::InstructionSequence:block in irb_binding@(irb)>=====",
[ 1] "== catch table",
[ 2] "| catch type: redo st: 0000 ed: 0027 sp: 0000 cont: 0000",
[ 3] "| catch type: next st: 0000 ed: 0027 sp: 0000 cont: 0027",
[ 4] "|------------------------------------------------------------------------",
[ 5] "0000 trace 1 ( 23)",
[ 6] "0002 getinlinecache 9, <ic:0>",
[ 7] "0005 getconstant :ATP",
[ 8] "0007 setinlinecache <ic:0>",
[ 9] "0009 getinlinecache 16, <ic:1>",
[10] "0012 getconstant :GDP",
[11] "0014 setinlinecache <ic:1>",
[12] "0016 opt_mult <ic:5>",
[13] "0018 getinlinecache 25, <ic:3>",
[14] "0021 getconstant :NDPK_constant",
[15] "0023 setinlinecache <ic:3>",
[16] "0025 opt_mult <ic:6>",
[17] "0027 leave
Видно, что getconstant
появились в строках 7, 10 для :ATP
, :GDP
, Вне блока, ATP
а также GDP
константы содержат ChemicalSpecies
случаи, но внутри блока, я хочу, чтобы они ссылались на концентрации АТФ и ВВП. Я не нашел способа оценить блок в среде, в которой сами константы имеют разные значения (то есть, если я не хочу временно перезаписывать константы во время выполнения, используя грязные приемы, чего я не хочу). Я жажду заменить этот код RubyVM getconstant :ATP
инструкция, например. send :_ATP, 0, nil, 24, <ic:0>
, а затем, например. instance_eval
это закрытие в среде, где _ATP
средства ATP.concentration
... Я знаю, что задаю сложные вопросы, извините еще раз...
Что касается второго варианта [ATP]
, [GDP]
что для этого потребуется активировать какой-то новый хук создания массива только внутри блока, так что, если есть только один элемент, это ChemicalSpecies
его концентрация будет возвращена вместо объекта массива. Я думаю, что это одинаково трудная, если не невозможная задача.
1 ответ
Спасибо всем, и особенно Касперу. Подводя итог, я указал на Sourcify / RubyParser и сказал, чтобы изнасиловать не Ruby-код. Sourcify / RubyParser - точный ответ, который я хотел, но Каспер упомянул их только в комментариях. Что угодно - небеса берут репутацию. С тех пор, как я написал, мне пришла в голову новая идея - поддельные скобки Unicode:
ChemicalSpecies = Struct.new :concentration
ATP, GDP = ChemicalSpecies[ 225.0 ], ChemicalSpecies[ 75.0 ]
class << ( ChemicalSystem = Object.new )
def ⁅ATP⁆; ATP.concentration end
def ⁅GDP⁆; GDP.concentration end
end
rate = lambda { ⁅ATP⁆ + ⁅GDP⁆ * 0.6 }
ChemicalSystem.instance_exec &rate
#=> 10125.0
Поддельные скобки ⁅ATP⁆
выглядеть лучше, чем ненавистное простое решение _ATP
, Есть несколько более привлекательных вариантов, таких как скобки на всю ширину [ATP]
Но проблема не только в том, как их набирать, но и в том, чтобы пользователь не путал их с ванилями. Я вычеркнул весь Unicode, и единственного другого варианта, который я ненавидел, не было 「ATP
, Конечно, это не имеет ничего общего с самоизменяющимся кодом; реальный ответ был в комментариях Каспера.