在另一个帖子The best way to construct a function with memory中,描述了如何在文件中备份函数:
$runningLogFile = "/some/directory/runningLog.txt";
flog[x_, y_] := flog[x, y] = f[x, y] /.
v_ :> (PutAppend[Unevaluated[flog[x, y] = v;], $runningLogFile]; v)
我觉得我理解这里的大部分成分而不明白它是如何工作的。有人可以告诉我这是如何评估的吗?
答案 0 :(得分:2)
让我们一步一步地评估flog[1, 2]
......
flog [1,2]
评估此表达式时,Mathematica会在问题中给出的1
定义中用x
替换2
和y
替换flog
。这将产生我们巡演的下一步:
flog [1,2] = f [1,2] /。 v_:> (PutAppend [Unevaluated [flog [1,2] = v;],$ runningLogFile]; v)的强>
请注意,此处的作业flog[1, 2] = ...
是flog
本身定义的一部分。
/.
是一个中缀运算符,它是ReplaceAll函数的替代表示。 ReplaceAll
会将替换规则应用于第一个参数的值。坚持这个想法 - 我们会回到它。第一个参数是flog[1, 2] = f[1, 2]
。此表达式将评估f[1, 2]
,然后将结果分配给flog[1, 2]
。为了便于讨论,我们假设f[1, 2]
返回345
。因此,新的定义将添加到flog
,即flog[1, 2] = 345
。分配后,我们可以检查flog
:
观察flog
最初只有一个定义,但现在它有两个 - 新添加的flog[1, 2]
定义缓存该调用的结果。这通常被称为“memoization”。
flog[1, 2] = 345
可能会产生为flog
建立新定义的副作用,但是,与Mathematica中的每个表达式一样,它也会产生一个值。该值为345
,在很多情况下,它将成为ReplaceAll
的第一个参数。
ReplaceAll
的第二个参数是:>
运算符的调用,RuleDelayed函数的中缀表达式。为了使这篇文章保持在一个可管理的大小,我们只需要注意规则在这种情况下评估自己。
所以,现在我们有一个涉及/.
来评估......
345 /。 v_:> (PutAppend [Unevaluated [flog [1,2] = v;],$ runningLogFile]; v
替换表达式将其第一个参数(345
)与替换规则(v_
)的模式组件匹配。 v_
匹配345
(或其他任何内容)并为345
提供名称v
以便替换。然后,ReplaceAll
会将345
替换为规则右侧的v
的每次出现。结果是要评估的下一个表达式......
(PutAppend [Unevaluated [flog [1,2] = 345;],$ runningLogFile]; 345)
这里我们有两个用分号分隔的表达式。顺便提一下,;
是一个扩展为CompoundExpression的中缀运算符。第一个表达式涉及PutAppend,它将第一个参数的值写入名为第二个参数值的文件中。但请注意,第一个参数包含在Unevaluated中。这会抑制对第一个参数的评估,以便将其完全按原样写入文件:flog[1, 2] = 345;
。如果当前的Mathematica会话结束,可以将书面表达式读入未来的Mathematica会话,以重新建立flog[1, 2]
的记忆结果。
CompoundExpression
会丢弃除最后一个之外的所有参数的值。这里,最后一个参数是345
。由于我们已经结束了表达式,因此这将是原始调用的最终返回值。也就是说,flog[1, 2]
返回345
- 尽管我们看到有副作用将此结果保存到内存和磁盘以供将来参考。
以后调用flog[1, 2]
现在,如果再次调用flog[1, 2]
,Mathematica将找到新定义flog[1, 2] = 345
。 345
将直接返回,没有我们上面讨论过的任何复杂情况。特别是,它甚至不会再次呼叫f[1, 2]
。当然,这是这个例子的全部动机。假设f
计算成本非常高,证明所有这些体操都是合理的,以尽量减少计算的次数。