Ruby中的自修改代码重新加载

时间:2013-02-02 04:55:05

标签: ruby closures block self-modifying

关注my previous question,让我对我究竟想要什么更精确,更少模糊。我写了一个化学品包,我决定将所有化学品和反应的名称大写,例如'ATP','Adenosine','DeoxyCytidine'等。这让我可以写出:

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

现在,如果使用ATP磷酸化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_1reactant_2两次重复,同时考虑ATPGDP。简单的解决方案是:

ChemicalReaction.new name: ATP_GDP_phosphate_exchange,
                     rate: lambda { _ATP * _GDP * NDPK_constant }

并且instance_eval上下文中的速率查找块将_ATP_GDP定义为ATPGDP的浓度。这非常非常接近,但不是完全我想要的东西,并且让我从天而降。我甚至可以使用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丑陋。我只想说ATPGDP[ATP][GDP],因为化学家使用括号表示浓度。我知道这就是Yusuke Endoh所说的约束编码。我的问题是,这两种理想的语法中的任何一种都可以被打败吗?例如,有一个闭包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出现在:ATP:GDP的第7,10行。在块之外,ATPGDP常量包含ChemicalSpecies个实例,但在块内,我希望它们引用ATP和GDP 浓度。我没有找到任何方法来评估环境中的块,其中常量本身具有不同的含义(即,除非我想在运行时使用脏技巧临时重写常量,这是我不想要的)。我渴望的是能够通过例如替换这个RubyVM代码getconstant :ATP指令。 send :_ATP, 0, nil, 24, <ic:0>,然后,例如。 instance_eval_ATP表示ATP.concentration的环境中[ATP]封闭......我知道我在问难题,对不起再次......

至于第二个选项[GDP]ChemicalSpecies,那个只需要在块内部激活某种新的数组创建钩子,这样如果只有一个元素,那就是{ {1}},将返回其浓度而不是数组对象。我认为这同样是困难的,即使不是不可能的任务。

1 个答案:

答案 0 :(得分:1)

感谢大家,尤其是卡斯珀。总而言之,我被指向Sourcify / RubyParser,并告诉强奸不是Ruby代码。 Sourcify / RubyParser是我想要的确切答案,但Casper仅在评论中提及它们。无论如何 - 天堂取名誉。自从我发布以来,一个新想法让我访问了 - 假的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。当然,这与自修改代码无关;真正的回答是在Casper的评论中。