Clojure 记忆化与无状态代码

Clojure 记忆化与无状态代码,clojure,functional-programming,clojurescript,Clojure,Functional Programming,Clojurescript,在开发无状态Clojure库的过程中,我遇到了一个问题:必须使用相同的参数重复调用许多函数。由于到目前为止,一切都没有副作用,这将始终导致相同的结果。我正在考虑如何让这更具表演性 我的库是这样工作的:每次调用一个函数时,它都需要传递一个状态哈希映射,该函数返回一个被操纵状态对象的替换。所以这使得一切都是不变的,每种状态都被保存在库之外 (require '[mylib.core :as l]) (def state1 (l/init-state)) (def state2 (l/proceed

在开发无状态Clojure库的过程中,我遇到了一个问题:必须使用相同的参数重复调用许多函数。由于到目前为止,一切都没有副作用,这将始终导致相同的结果。我正在考虑如何让这更具表演性


我的库是这样工作的:每次调用一个函数时,它都需要传递一个状态哈希映射,该函数返回一个被操纵状态对象的替换。所以这使得一切都是不变的,每种状态都被保存在库之外

(require '[mylib.core :as l])
(def state1 (l/init-state))
(def state2 (l/proceed state1))
(def state3 (l/proceed state2))
如果继续不应重复执行相同的操作,我有几个选项来解决此问题:


选项1:“手工操作”

将必要的状态存储在状态哈希映射中,并仅在必要时更新。意思是:有一个复杂的机制,知道哪些部分需要重新计算,哪些不需要。这永远是可能的,在我的情况下,它不会那么微不足道。如果我实现了这一点,我将产生更多的代码,最终更容易出错。那么有必要吗

选项2:备忘录化

因此,在lib中的关键点使用
memoize
函数是有诱惑力的:在这些点上,我预计可能会使用相同的参数重复调用函数。这是编程的另一种哲学:将每一步建模为第一次运行。并将多次调用的事实分离到另一个实现。(这让我想起了react/om/algent的渲染功能)

选项3:core.memoize

但回忆录当然是有状态的。例如,当lib在web服务器中运行时,这就成了一个问题。服务器将继续用捕获的结果填充内存。然而,在我的例子中,只捕获每个用户会话的计算结果是有意义的。因此,最好将缓存附加到前面描述的状态哈希映射,该映射将由lib传回

看起来core.memoize为这项工作提供了一些工具。不幸的是,它没有很好的文档记录——我没有找到与所描述的情况相关的有用信息



我的问题是:我是否或多或少正确估计了可能的选择?还是有其他我没有考虑的选择?如果不是的话,它看起来像是core.memoize。那么,如果有人能在手边给我一个简短的模式,我将不胜感激。如果
state1
state2
state3
在您的示例中有所不同,那么回忆录将毫无用处<代码>继续每次将使用不同的参数调用

作为一般设计原则,不要将缓存策略强加给使用者。设计使您的图书馆的消费者能够使用任何记忆技术,或者根本不使用任何记忆技术



另外,您没有提到
initstate
是否没有副作用,以及它是否返回相同的
state1
。如果是这样,为什么不将所有(或部分)状态保持为静态文本呢。如果它们不占用太多空间,您可以编写一个宏来计算它们的编译时间。比如说,前20个州是硬编码的,然后调用
继续

哦,对不起,可能还不够清楚。我不会直接记忆procedure,而是在procedure内部会有对记忆子函数的调用,这些子函数在状态的部分(!)上运行。明白了吗?init state和lib的其他部分一样没有副作用。所以它总是返回相同的东西。我可以称之为get initial stateX如果stateX总是相同的,那么我建议使用数据方式。此外,如果你发布代码,你可以得到更好的反馈。这是一个专有项目吗?在“状态”中,某些部分由“继续”更改,其他部分保留原样。状态2,状态3,它们总是不同的,因为有一个随机的内部进程(啊,这仍然是纯功能的吗?我想不是,只是意识到…)。代码只是要构建的,现在发布它是没有意义的-但它根本不是合适的,只是私人的乐趣;-)随机的不再有用了。您可以将一个seed传递给init,并将一个RNG与该seed一起使用(可能是一个延迟seq on状态)(即使现在有人会说它不是FP)。总是发布代码,甚至是伪代码,所以我们不必去寻找细节。