【问题标题】:memoization vs. state-free code记忆与无状态代码
【发布时间】:2016-02-24 15:31:44
【问题描述】:

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


我的库是这样工作的:每次调用一个函数时,它都需要传递一个状态哈希映射,该函数返回一个带有操作状态对象的替换。因此,这使所有内容都保持不变,并且每种状态都保留在库之外。

(require '[mylib.core :as l])
(def state1 (l/init-state))
(def state2 (l/proceed state1))
(def state3 (l/proceed state2))

如果继续不应该重复执行相同的操作,我有几个选项来解决这个问题:


选项 1:“手工操作”

将必要的状态存储在 state-hash-map 中,并仅在必要时更新。意味着:拥有一个复杂的机制,知道哪些部分必须重新计算,哪些不需要。这总是可能的,就我而言,这不是那么微不足道。如果我实现了这个,我会产生更多的代码,最终更容易出错。那么有必要吗?

选项 2:记忆

所以在库中的关键点使用memoize 函数很容易: 在这些点上,我预计可能会重复使用相同的参数调用函数。这是另一种编程哲学:对每个步骤进行建模,就好像它是第一次运行一样。并将多次调用的事实分离到另一个实现。 (这让我想起了react/om/reagent的render函数的想法)

选项 3:core.memoize

但是记忆是有状态的——当然。这 - 例如 - 当 lib 在网络服务器中运行时成为一个问题。服务器将继续用捕获的结果填充内存。然而,就我而言,只捕获每个用户会话的计算结果是有意义的。因此,将缓存附加到前面描述的 state-hash-map 是完美的,它将由 lib 传回。

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


我的问题是:我是否或多或少正确估计了可能的选项?或者还有其他我没有考虑过的选择吗?如果没有,看起来 core.memoize 是要走的路。然后,如果有人可以在手边给我一个简短的模式,我将不胜感激,应该在这里使用。

【问题讨论】:

    标签: clojure functional-programming clojurescript


    【解决方案1】:

    如果state1state2state3 在您的示例中不同,则记忆化将一无所获。 proceed 每次都会用不同的参数调用。

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


    另外,你没有提到init-state 是否没有副作用,以及它是否返回相同的state1。如果是这样,为什么不将所有(或某些)状态保留为静态文字。如果它们不占用太多空间,您可以编写一个宏来计算它们的编译时间。比如说,前 20 个状态是硬编码的,然后调用 proceed

    【讨论】:

    • 哦,抱歉,可能还不够清楚。我不会直接记忆进程,而是在进程内部调用记忆子函数,这些子函数在状态的部分(!)上运行。有我吗?
    • init-state 就像 lib 的其余部分一样没有副作用。所以它总是返回同样的东西。我可以称之为获取初始状态
    • 如果 stateX 总是相同的,那么我建议采用数据方式。此外,如果您发布代码,您可以获得更好的反馈。这是一个专有项目吗?
    • 在“状态”内部,一些部分通过继续进行更改,其他部分保持原样。 State2,state3,它们总是不同的,因为有一个随机的内部进行(arrh,这仍然是纯粹的功能吗?我猜不是,只是意识到..)。该代码只是要构建的,现在发布它没有意义 - 但它根本不是专有的,只是私人的乐趣;-)
    • 随机?不再起作用。您可以将种子传递给 init,并使用带有该种子的 RNG(可能作为状态上的惰性序列)(即使现在有些人会认为这不是 FP)。总是发布代码,甚至是伪代码,这样我们就不必去寻找细节了。
    猜你喜欢
    • 2018-05-14
    • 2020-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-29
    • 1970-01-01
    相关资源
    最近更新 更多