Clojure 当另一个字段更新时更新该字段模式的标准实施规程
假设您有两个值Clojure 当另一个字段更新时更新该字段模式的标准实施规程,clojure,Clojure,假设您有两个值x和y,这样每次更新x时都应该计算y。在Java中,一切都是一个对象,所以我几乎没有选择。这样做的方法是通过一个类,例如 公共数据{ 私有类型x,y; 数据(x型){ 这个.x=x; 这个.y=null; } 无效更新(类型2 val){ //执行更新X的操作 //执行(昂贵)操作以更新Y } void getX(){…} void getY(){…} } 在clojure中,直接翻译可能会使用deftype和defprotocol/definterface。但仅仅定义两个变量之
x
和y
,这样每次更新x
时都应该计算y
。在Java中,一切都是一个对象,所以我几乎没有选择。这样做的方法是通过一个类,例如
公共数据{
私有类型x,y;
数据(x型){
这个.x=x;
这个.y=null;
}
无效更新(类型2 val){
//执行更新X的操作
//执行(昂贵)操作以更新Y
}
void getX(){…}
void getY(){…}
}
在clojure中,直接翻译可能会使用deftype
和defprotocol
/definterface
。但仅仅定义两个变量之间的依赖关系就太过分了。特别是,我反对赋予它一个名称,并根据实际的类型和协议来处理它。
我知道这就是defrecord
s的用途——当您需要创建一个不代表业务域中实体的类时,但是defrecord
s是不可变的
我还有其他选择吗?我看了一下
RxClojure
,因为这里的情况似乎是“反应性的”,但这似乎是面向“发射”事件的,而不是仅仅将最新的事件存储在原子中。我忍不住认为你想得太多了
(def some-var (atom some-value))
(def derived (atom (some-expensive-fn some-var)))
(defn update-derived
[old-state new-state]
(new-state)
(defn update-var
[x]
(swap! derived
update-derived
(swap! some-var (fn [old-state] (x)))))
我们定义了一些状态、一些派生状态和一个接受新值并同时更新这两个值的函数
根据评论进行编辑
如果希望能够多次执行此操作,可以使用返回闭包的工厂:
(defn make-stateful-thing-with-derived-value
[init-x-value compute-y]
(let [x (atom init-x-value)
y (atom (compute-y init-x-value))]
(list x
y
(fn [new-x-value]
(swap! y
(fn [old-s] (compute-y new-x-value))
(swap! x (fn [old-s] (new-x-value)))))))
该函数获取一个初始x值和一个派生y的函数,并返回一个包含原子x和y的列表,以及一个接受值以更新它们的函数。您也可以使用facility进行此操作。例如:
(defn entangle [x-val computation]
(let [x (atom x-val)
y (atom (computation x-val))]
(add-watch x nil (fn [_ _ old-x new-x]
(reset! y (computation new-x))))
{:x x
:y y}))
在上述示例中,y
值取决于通过计算
函数得到的x
值
(let [{:keys [x y]} (entangle 10 #(* % %))]
(println "x:" @x "y:" @y)
(swap! x inc)
(println "x:" @x "y:" @y)
(reset! x 200)
(println "x:" @x "y:" @y))
;; x: 10 y: 100
;; x: 11 y: 121
;; x: 200 y: 40000
nil
在某些情况下,这可能很方便,尽管通常这种混乱的代码会消除引用透明性,我可能会避免使用它。Ah。但与可以多次实例化的类或记录不同,这将限于程序中的两个特定变量。想象一下同样的功能,但是参数化了
一些var
和派生的@PeeyushKushwaha检查我的更新答案。把它打进去,这样就不用编辑了,这样帕伦斯在结尾可能会不平衡,但你会得到要点。谢谢你更新答案。使用闭包使独立实体(更新函数,x,y)能够共享公共状态是有用的,我没有想到这一点。@PeeyushKushwaha我从您的个人资料中看到您是一名web开发人员。您可以在Javascript中做同样的事情:(init,f)=>{let x=init;let y=f(x);return[x,y,(update)=>{x=update;y=f(x);}
是的。但我不知道我能在clojure中完成闭包!这正是我想要的。我同意这可能会让人困惑,但我认为可以通过将两个ref都放在一个对象中(就像您在返回Engure
时所做的那样),并让代码用户意识到这是一个特殊的“反应性”对象来管理(当然不能消除)这种困惑,也许你甚至没有像在let
中那样尝试将ref分成不同的变量。