关注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_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]
,因为化学家使用括号表示浓度。我知道这就是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行。在块之外,ATP
和GDP
常量包含ChemicalSpecies
个实例,但在块内,我希望它们引用ATP和GDP 浓度。我没有找到任何方法来评估环境中的块,其中常量本身具有不同的含义(即,除非我想在运行时使用脏技巧临时重写常量,这是我不想要的)。我渴望的是能够通过例如替换这个RubyVM代码getconstant :ATP
指令。 send :_ATP, 0, nil, 24, <ic:0>
,然后,例如。 instance_eval
在_ATP
表示ATP.concentration
的环境中[ATP]
封闭......我知道我在问难题,对不起再次......
至于第二个选项[GDP]
,ChemicalSpecies
,那个只需要在块内部激活某种新的数组创建钩子,这样如果只有一个元素,那就是{ {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的评论中。