首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >Clojure:在不定义新协议的情况下向defrecord添加函数

Clojure:在不定义新协议的情况下向defrecord添加函数
EN

Stack Overflow用户
提问于 2011-02-17 09:36:21
回答 3查看 5.9K关注 0票数 21

我习惯了python/java中的面向对象。现在正在做Clojure。我遇到了defrecord,但似乎我必须为我希望记录实现的每个功能或一组功能定义一个协议。创建新的协议会产生摩擦。我不仅要命名我想要的函数,还要命名协议。我正在寻找的是一种将函数与记录“很好地”关联起来的方法,这样函数就可以通过this参数访问记录的参数,而不必定义新的协议或向现有协议添加函数。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2011-02-17 18:32:17

问得好。

像往常一样,在Clojure中有一种很好的方法--下面是如何用10行Clojure代码实现您自己的简单动态OO系统(包括继承、多态性和封装)。

这个想法是:如果你愿意,你可以把函数放在normals映射或记录中,创建一个类似OO的结构。然后,您可以在“原型”样式中使用它。

代码语言:javascript
复制
; define a prototype instance to serve as your "class"
; use this to define your methods, plus any default values
(def person-class
  {:get-full-name 
    (fn [this] (str (:first-name this) " " (:last-name this)))})

; define an instance by merging member variables into the class
(def john 
  (merge person-class 
    {:first-name "John" :last-name "Smith"}))

; macro for calling a method - don't really need it but makes code cleaner
(defmacro call [this method & xs]
  `(let [this# ~this] ((~method this#) this# ~@xs)))

; call the "method"
(call john :get-full-name)
=> "John Smith"

; added bonus - inheritance for free!
(def mary (merge john {:first-name "Mary"}))
(call mary :get-full-name)
=> "Mary Smith"
票数 14
EN

Stack Overflow用户

发布于 2011-02-17 20:43:31

如果你还没有尝试过multimethods,它们可能更接近你正在寻找的东西。

定义:

代码语言:javascript
复制
(defrecord Person [first middle last])
(defmulti get-name class)
(defmethod get-name Person [person] (:first person))

使用:

代码语言:javascript
复制
(def captain (Person. "James" "T" "Kirk"))
(get-name captain)

所选择的multimethod实现基于defmulti中的分派函数(接受传递给该函数的args并返回分派值的函数)。通常,"class“是调度函数,就像这里一样,对类型进行调度。Multimethods支持多个独立的ad-hoc或基于Java的类型层次结构、默认实现等。

不过,总的来说,我认为您可能希望后退一步,并考虑您是否真的想要协议或多方法。您似乎正在尝试在Clojure中“做面向对象”。虽然OO的各个方面(比如多态性)都很棒,但也许你应该尝试用不同的方式来思考你的问题。例如,在我刚刚给出的例子中,还没有令人信服的理由来实现get-name的多态。为什么不直接说:

代码语言:javascript
复制
(defn get-name [x] (:first x))

你甚至需要一个人的记录吗?一张简单的地图就足够了吗?有时答案是肯定的,有时是否定的。

一般来说,Clojure不提供类继承。如果您真的需要的话,您当然可以构建一个等价的(即使是使用协议),但通常我发现在Clojure中还有其他更好的方法来解决这个问题。

票数 24
EN

Stack Overflow用户

发布于 2018-03-28 06:10:23

使用mikera的想法,我开发了一种方法来实现这一点(类似OO的类)

代码语言:javascript
复制
;; -----------------------------------------
;; main()
;; -----------------------------------------
(def p1 (newPoint 3 4))
(def p2 (newPoint 0 0))

(call p1 :getY) ;; == 4

(call p1 :distance p2) ;; == 5

完整的示例,使用一种“得体且有组织的”方式来声明一个类似于OO的类

代码语言:javascript
复制
;; -----------------------------------------
;; begin Point class
;; -----------------------------------------
(defrecord Point [x y methods] )

(def someMethods

  {
   :getX (fn [this] (:x this)  )    
   :getY (fn [this] (:y this)  )   
   :distance (fn [this other] 
                (def dx (- (:x this) (:x other)))
                (def dy (- (:y this) (:y other)))
                (Math/sqrt (+ (* dx dx) (* dy dy) ))
                 )
  }  

  )

;;
;; Point constructor
;; 
(defn newPoint [x y]
  (Point. x y someMethods)
  )

;; -----------------------------------------
;; end Point class
;; -----------------------------------------

;; -----------------------------------------
;; helper to call methods
;; -----------------------------------------
(defn call 
  ([obj meth] ((meth (:methods obj)) obj))
  ([obj meth param1] ((meth (:methods obj)) obj param1))
  ([obj meth param1 param2] ((meth (:methods obj)) obj param1 param2))
  )

;; -----------------------------------------
;; main()
;; -----------------------------------------
(def p1 (newPoint 3 4))
(def p2 (newPoint 0 0))

(call p1 :getY) ;; == ((:getX (:methods p1)) p1)

(call p1 :distance p2) ;; == ((:distance (:methods p1)) p1 p2)
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/5024211

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档