2

这是我之前的问题的后续。

我从阅读Let Over Lambda中想出了一个奇怪的对象方案,并且认为没有协议的优势,但想获得意见。我只是在探索使用高阶函数和封装。

(defn new-person
  "Construct a new Person object and return a Map of accessor methods."
  [init-first-name init-last-name init-age]
  (let [first-name (ref init-first-name)
        last-name (ref init-last-name)
        age (ref init-age)]
    {:get-first-name #(@first-name)
     :set-first-name #(dosync (ref-set first-name %1))
     :get-last-name #(@last-name)
     :set-last-name #(dosync (ref-set last-name %1))
     :get-age #(@age)
     :set-age #(dosync (ref-set age %1))}))

我可以像这样使用对象:

(def fred (new-person "fred" "flintstone" 42))

并以这种方式检索访问器方法:

(fred :get-age)

但我不知道如何调用访问器。

创建的对象是线程安全的,因为“实例”变量的所有突变都发生在 STM 中。

更新:新的和改进的版本:

(defn new-person
  "Construct a new Person object and return a Map of accessor methods."
  [init-first-name init-last-name init-age]
  (let [first-name (ref init-first-name)
        last-name (ref init-last-name)
        age (ref init-age)]
    {:first-name 
        (fn
          ([] @first-name)
          ([val] (dosync (ref-set first-name val))))
     :last-name
        (fn
          ([] @last-name)
          ([val] (dosync (ref-set last-name val))))
     :age
        (fn
          ([] @age)
          ([val] (dosync (ref-set age val))))}))
4

3 回答 3

5

也许不是 100% 回答你的问题,但你会尝试做对于 Clojure 来说不是很惯用的。“标准”解决方案类似于:

(defrecord Person [first-name last-name age])
(def fred (Person. "fred" "flintstone" 42))
(fred :age)

看起来您正在将 OO 可变状态强制转换为 Clojure 的“对象”

于 2011-05-15T14:19:09.993 回答
3

它与您的后续问题相同,将表格包装在另一组括号中。当内部形式返回一个函数时,它与返回函数的符号相同。规则是括号中的第一个形式将始终被查找为特殊形式、宏或函数。您需要像引用这样的东西来防止这种行为。

user=> (fred :get-age)
#<user$new_person$fn__531 user$new_person$fn__531@c4afc4>
user=> ((fred :get-age))
42

它与

user=> (let [age-getter (fred :get-age)] (age-getter))
42
于 2011-05-15T18:40:45.050 回答
1

Clojure 哲学不是封装对记录字段本身的访问。封装应该发生在更高的级别上,例如在处理该记录的一组函数中。请参阅Clojure - 数据类型

信息的封装是愚蠢的,字段是公共的,使用协议/接口来避免依赖

于 2011-05-15T16:12:08.953 回答