Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Clojure 当另一个字段更新时更新该字段模式的标准实施规程_Clojure - Fatal编程技术网

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分成不同的变量。