Самомодифицирующийся код в Ruby перезагружен

Следуя моему предыдущему вопросу, позвольте мне быть более точным и менее запутанным в отношении того, что именно я хочу. Я написал химический пакет, в котором решил, что названия всех химических веществ и реакций будут писаться с большой буквы, например, «АТФ», «Аденозин», «Деоксицитидин» и т. д. Это позволило мне написать, например:

ATP = ChemicalSpecies.new initial_concentration: 225.0     # in micromolars
GDP = ChemicalSpecies.new initial_concentration: 75.0      # in micromolars

Теперь, если АТФ используется для фосфорилирования GDP с использованием фермента NDPK с каталитической константой

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 code getconstant :ATP, например. send :_ATP, 0, nil, 24, <ic:0>, а затем, например. instance_eval это замыкание в среде, где _ATP означает ATP.concentration... Я знаю, что задаю трудные вопросы, еще раз извините...

Что касается второго варианта [ATP], [GDP], то для этого потребуется активировать какой-то новый хук создания массива только внутри блока, чтобы, если есть только один элемент, то есть ChemicalSpecies, возвращалась его концентрация вместо объекта массива . Я думаю, что это столь же сложная, если не невыполнимая задача.


person Boris Stitnicky    schedule 02.02.2013    source источник
comment
Пожалуйста, удалите все разговоры о химии и обобщите. Мы все можем звучать так, как будто мы говорим по-марсиански для внешнего мира, но для меня это звучит по-гречески. Кроме того, разборка не имеет большого значения.   -  person Linuxios    schedule 02.02.2013
comment
Это только подтверждает для меня, что вы пытаетесь согнуть молоток в вилку (или вставьте другую умную поговорку). Если ваши пользователи являются главной заботой, то к черту все, что говорит какой-нибудь философ программирования :) Код для пользователей как основная директива номер 1. Вы не можете заставить [ATP] вести себя хорошо без кучи навоза уродливого спагетти-кода, который никому не нужен. сможет расшифровать и сохранить. И не только это, но это будет хрупкий код, зависящий от внутренних компонентов Ruby. Если вы напишете свой собственный доменный язык, вы получите полный контроль и в конце концов станете гораздо более счастливым программистом (и пользователи тоже будут счастливы).   -  person Casper    schedule 02.02.2013
comment
Также, если вы видите мой предыдущий ответ. Возможно, вы даже сможете обойти все это без создания синтаксического анализатора. Просто простая замена строки, и eval может даже помочь. т.е. замените [ATP] на ATP.concetration и eval целиком.   -  person Casper    schedule 02.02.2013
comment
Кроме того, помните, что лямбда-выражения также захватывают константы. Лямбда, определенная в контексте, где доступны ATP и GDP, является лямбдой, в которой они доступны.   -  person Linuxios    schedule 02.02.2013
comment
@Linuxios: Да, и они фиксируют константы очень постоянным способом, с которым, как я узнал, не может справиться ни один instance_eval.   -  person Boris Stitnicky    schedule 03.02.2013


Ответы (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], но проблема заключается не только в том, как их набирать, но и в том, как не спутать их с ванилью. Я перерыл весь Юникод, и единственный другой вариант, который мне не не понравился, был 「ATP. Конечно, это не имеет ничего общего с самомодифицирующимся кодом; реальный ответ был в комментариях Каспера.

person Boris Stitnicky    schedule 03.02.2013