对不起,这个问题的描述是如此抽象:它对我的工作而言,出于商业机密的原因,我不能给出现实世界的问题,只是一个抽象 .
我有一个应用程序,它接收包含键值对的消息 . 密钥来自一组定义的关键字,每个关键字都有固定的数据类型 . 因此,如果“Foo”是一个整数而“Bar”是一个日期,您可能会收到如下消息:
Foo: 234
Bar: 24 September 2011
消息中可以包含任何密钥子集 . 键的数量相当大(几十个) . 但是现在让我们坚持使用Foo和Bar .
显然有一条这样的记录对应于这些消息:
data MyRecord {
foo :: Maybe Integer
bar :: Maybe UTCTime
-- ... and so on for several dozen fields.
}
该记录使用“可能”类型,因为该字段可能尚未收到 .
我还需要从当前值(如果存在)计算出许多派生值 . 比如我想拥有
baz :: MyRecord -> Maybe String
baz r = do -- Maybe monad
f <- foo r
b <- bar r
return $ show f ++ " " ++ show b
其中一些功能很慢,所以我不想不必要地重复它们 . 我可以为每个新消息重新计算baz,并在原始结构中备忘,但是如果一条消息使foo和bar字段保持不变,则会浪费CPU时间 . 相反,我可以在每次需要时重新计算baz,但如果基础参数自上次以来没有改变,那么这将浪费CPU时间 .
我想要的是某种智能记忆或基于推送的重新计算,只有在参数改变时才重新计算baz . 我可以通过注意baz仅依赖于foo和bar来手动检测到这一点,因此只能在更改这些值的消息上重新计算它,但是对于容易出错的复杂函数 .
另外一个问题是这些功能中的一些可能有多种策略 . 例如,您可能有一个可以使用'mplus'从Foo或Bar计算的值 .
有谁知道现有的解决方案?如果没有,我该怎么办呢?
3 回答
我假设你有一个"state"记录,这些消息都涉及更新它以及设置它 . 因此,如果
Foo
是12
,它可能稍后是23
,因此baz
的输出会改变 . 如果不是这种情况,那么答案就变得微不足道了 .让我们从baz的“核心”开始 - 一个不在记录上的函数,而是你想要的值 .
现在让我们改变它:
现在,无论何时使用普通函数,都可以使用缓存转换函数,将生成的缓存转换函数替换回记录中 . 这本质上是一个尺寸为1的手册 .
对于您描述的基本情况,这应该没问题 .
一个包含动态依赖关系图的更加通用且更通用的解决方案名为“增量计算”,但我看到它的研究论文不仅仅是严肃的 生产环境 实现 . 你可以看看这些对于初学者,并遵循前面的参考线索:
http://www.carlssonia.org/ogi/Adaptive/
http://www.andres-loeh.de/Incrementalization/paper_final.pdf
增量计算实际上也与函数式反应式编程非常相关,因此您可以查看一下conal 's papers on that, or play with Heinrich Apfelmus' reactive-banana库:http://www.haskell.org/haskellwiki/Reactive-banana
在命令式语言中,看看python中的trellis:http://pypi.python.org/pypi/Trellis或lisp中的单元格:http://common-lisp.net/project/cells/
您可以构建与您需要执行的计算相对应的有状态图 . 当出现新值时,将这些值推入图形并重新计算,更新图形直到达到输出 . (或者您可以将值存储在输入中并根据需要重新计算 . )这是一个非常有状态的解决方案,但它可以工作 .
您是否可以根据费率的实时输入等创建市场数据,如收益率曲线?
听起来像你想要一个不可变的变量,但允许从“尚未计算”到“计算”的一次性变异 . 嗯,你很幸运:这正是懒惰的评价给你的!因此,我提出的解决方案非常简单:只需为您要计算的每个项目的字段扩展记录 . 这是一个这样的例子,我们正在做的CPU密集型任务正在破坏一些加密方案:
这里的要点是对
foo
的调用有这样的费用:如果你从不要求结果的
plaintext
,那就便宜了 .在第一次调用
plaintext
时,CPU会长时间搅拌 .在对
plaintext
的后续调用中,立即返回先前计算的答案 .