仅当参数从上次调用更改时才执行函数体的函数/宏

Posted

技术标签:

【中文标题】仅当参数从上次调用更改时才执行函数体的函数/宏【英文标题】:Function/macro to execute function body only when arguments changed from last call 【发布时间】:2014-06-19 15:19:21 【问题描述】:

这应该类似于memoize,但非常不同。虽然 memoize 应该与纯函数一起使用,但它通常对加速 IO 相关函数很有用。

我正在寻找的函数/宏应该表现得像高阶函数。它产生的功能应该:

第一次调用时,它应该调用原始函数,并将参数传递给它 记住:参数,返回值 第二次调用时 使用相同的参数:它应该返回记忆的返回值 使用不同的参数:忘记最后一个参数和返回值,调用原始函数,记住当前参数和返回值 第 n 次调用时,始终将参数与上次调用进行比较,并按照上述方式进行操作。

【问题讨论】:

这和 memoize 完全一样,只是它只记住一个参数->值对。 【参考方案1】:

修改memoize 的源代码很容易做到这一点。

(defn memo-one [f]
  (let [mem (atom )]
    (fn [& args]
      (if-let [e (find @mem args)]
        (val e)
        (let [ret (apply f args)]
          (reset! mem args ret)
          ret)))))

(defn foo [x] (println "called foo") x)
(def memo-foo (memo-one foo))

但是,已经有一个灵活的记忆库可以做到这一点。

(require '[clojure.core.memoize :as memo])

(defn foo [x] (println "called foo") x)
(def memo-foo (memo/lru foo :lru/threshold 1))

(memo-foo 1)
; called foo
; 1
(memo-foo 1)
; 1
(memo-foo 2)
; called foo
; 2  
(memo-foo 1)
; called foo
; 1

【讨论】:

很好,特别是在值已被持有的情况下避免交换 我意识到我需要一些完全不同的东西(自定义缓存策略),但基本上我在实现之前就使用了它。额外的问题:比较类型化数组的快速、内存有效的方法 :)(是的,这些是我的论点,我将它们重用于性能)。 @skrat 我建议将您的奖励问题作为一个新问题提出,并详细说明您如何使用阵列。如果您要重用相同的数组,那么答案很简单。【参考方案2】:

这是一个仅使用 clojure.core 的版本。我从 A. Webb 的示例中借用了foo 函数进行演示。

(defn memoize-1
  [f]
  (let [previous (atom nil)]
    (fn [& args]
      (second
       (swap! previous
              (fn [[prev-args prev-val :as prev]]
                (if (and prev (= prev-args args))
                  prev
                  [args (apply f args)])))))))


user> (defn foo [x] (println "called foo") x)
#'user/foo
user> (def memo-foo (memoize-1 foo))
#'user/memo-foo
user> (memo-foo 1)
called foo
1
user> (memo-foo 1)
1
user> (memo-foo 2)
called foo
2
user> (memo-foo 1)
called foo
1

【讨论】:

以上是关于仅当参数从上次调用更改时才执行函数体的函数/宏的主要内容,如果未能解决你的问题,请参考以下文章

仅当一个表达式中的可选项不是 nil 时才调用函数?

内联函数和宏比较

内联函数和宏定义

仅当调用函数内部的回调并执行函数的其余部分时,如何调用另一个函数?

用内联取代宏代码

C语言中函数和函数体的区别是啥?