【问题标题】:Alternative to using an atom替代使用原子
【发布时间】:2016-02-12 11:10:20
【问题描述】:

我有一个带有原子的小 Clojurescript 程序。一个字符串被放入这个原子中,它是一个函数 (low-level-fn) 的配置,程序中几乎所有其他函数都使用该函数。接收所有配置的程序有一个入口点。该程序的运行结构是一棵倒置的树,其中许多叶子实际上是low-level-fn

我的意图总是摆脱这个原子作为完成程序的一部分 - 通过将配置字符串传递给外部函数使其成为高阶函数。通过添加最左边的参数(即字符串配置)并创建部分函数来做同样的事情。无论哪种情况,这个“尚未完成”的函数都在入口点函数中构建。

我的问题是程序可能会变得比使用 atom 时更糟,因为“尚未完成”的函数必须作为参数传递给程序中的几乎所有函数。

我还应该考虑其他想法吗?我很可能会摆脱原子以使整个程序成为一个纯函数。 edit - 实际上有或没有原子,程序仍然是一个纯函数。 编辑 - 是吗?

【问题讨论】:

  • “程序可能会变得更糟 [...] 因为“尚未完成”的功能必须作为参数传递给程序中的几乎所有功能" 为什么传递函数比传递原子更糟糕?
  • 在阅读程序时,几乎每个函数都需要考虑这个额外的参数,这是一个低级的关注点,与所讨论的函数无关。没有它,程序会干净得多。
  • 配置是否会在执行时发生变化?如果没有,您可以在一开始就使用 Var,例如 (def configuration your-reading-config-function)
  • 不在执行期间,但这是一个库,用户提供配置字符串。
  • 如果我理解正确的话,你想根据用户输入生成一个函数,然后重复调用这个函数。一个新的输入应该创建一个不同的函数并重新运行程序。在这种情况下,依赖原子是错误的安全性——假设您在其中一个线程完成之前从两个并发线程调用函数——在这种情况下,当原子被修改时,两个线程的函数都将被更改。在这种情况下,我不确定是否有其他方法可以传递任何信息(或者更好的是函数)。

标签: clojure clojurescript


【解决方案1】:

当我正确理解您的问题时,最简单的方法是使用闭包。

下面的呢:

(defn myrunner [param]
  ;; generate closures depending on the parameter
  (letfn [(my-fn [x]
            (* param x))]
    ;; now process everything inside the letfn
    (my-fn 2)))

(myrunner 2) ;; => 4
(myrunner 6) ;; => 12
;; parallel execution works, too
(pmap myrunner (range 100)) ;; 0, 2, 4,...

编辑:从示例中删除了副作用。

【讨论】:

  • 是的,我刚刚意识到在 REPL 中... 将编辑,一会儿。
  • 谢谢对不起我删除了我的评论。建议减少数学运算并且没有副作用,现在已经完成。
  • 对不起 - 我在这里真的很马虎。
  • 所以在我的问题中,我似乎应该说闭包或更高阶或部分应用的功能。它们实际上都等同于同一件事-将变量绑定到函数中。仍然最重要的一点是在库/函数中有原子是否危险。那会 100% 让我不再考虑使用 atom 进行配置。
  • 根据定义它并不危险。如果您可以确保您的代码永远不会同时运行,那应该没问题。您需要非常仔细地考虑您的设计。原子呈现可变状态,因此您需要始终牢记您的原子可能已更改。在你的特殊情况下,我会避免它。
【解决方案2】:

这个呢:

(defn original-lowlv-fn [cfg param]
  ;; your lowlevel fun that needs config
  (prn cfg param))


(defn init-library [cfg-from-user]
  ;; init-library must be called before library use
  ;; your new lowlevel-fn
  (def lowlv-fn
    (let [cfg {:my-cfg cfg-from-user}]
      (partial original-lowlv-fn cfg))))

例子:

(lowlv-fn :foo)

【讨论】:

  • @chris-murphy 因为我只能评论我自己的解决方案:在程序运行期间可以更改原子。它代表状态,如果一个人已经阅读了您的程序/库,他现在必须检查状态发生变化的地方。如果您使用部分/闭包,则不会发生这种歧义
  • 使用您的解决方案,用户必须调用 init-library,然后将返回的内容返回给入门级函数。如果用户首先使用 cfg-from-user 调用入门级函数,然后让入门级函数代表她调用 init-library,那就更容易了。
  • @ChrisMurphy 我并不是说我的解决方案很好。这实际上是一个 hack。我想提供一个解决方案,只需更改几行代码即可。我想在评论中指出的是,原子代表状态,这是您在函数式程序中要避免的事情。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-10-10
  • 1970-01-01
  • 1970-01-01
  • 2016-10-14
  • 1970-01-01
  • 2011-10-19
  • 1970-01-01
相关资源
最近更新 更多