在我之前的问题之后,让我对我到底想要什么更准确,更少混淆。我写了一个化学包,我决定所有化学物质和反应的名称都大写,例如“ATP”、“腺苷”、“脱氧胞苷”等。这让我可以写例如:
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
出现在第 7 行、第 10 行的:ATP
, :GDP
。在块外,ATP
常量GDP
包含ChemicalSpecies
实例,但在块内,我希望它们引用 ATP 和 GDP浓度。在常量本身具有不同含义的环境中,我没有找到任何方法来评估块(也就是说,除非我想在运行时使用肮脏的技巧临时重写常量,这是我不想要的)。我渴望的是能够getconstant :ATP
用例如替换这个 RubyVM 代码指令。send :_ATP, 0, nil, 24, <ic:0>
,然后例如。instance_eval
在一个环境中关闭_ATP
意味着ATP.concentration
......我知道我在问一些棘手的问题,再次抱歉......
至于第二个选项[ATP]
,[GDP]
那只需要在块内激活某种新的数组创建钩子,这样如果只有一个元素,即 a ChemicalSpecies
,则将返回其浓度而不是数组对象。我认为这同样是困难的,即使不是不可能的任务。