你能举例说明一个人如何代表一个"玩家"具有可变属性的类,如HP和位置(3d矢量),init,setter和getter等函数?
答案 0 :(得分:12)
在真正惯用的Clojure中,你的“玩家”将是不可变的,你可能会将其表示为地图,例如:
{:type :player
:team :red
:hit-points 10
:location [17 9 6]}
播放器可能包含在更大的“世界”数据结构中,并且会有一个纯函数update-world
可以创建一个具有任何必要修改的新世界(例如将玩家移动到新位置)
对于getter / setter - 只需使用法线贴图操作函数。在您操作标准Clojure数据的情况下,通常不需要getter / setter。
答案 1 :(得分:2)
您还可以定义记录,这些记录具有与地图类似的界面和语义,但提供了多种好处。例如,访问记录中的成员比在地图中更快。此外,您可以在记录上扩展协议,并将其用于记录上的快速多态分派。例如你可以在各种形状对象上扩展绘制协议。根据Rich Hickey(http://www.infoq.com/interviews/hickey-clojure-reader#),使用协议帮助他们使用Clojurescript编译器。
e.g。
(defrecord Action [time key args state]
(comment protocol extension can go here))
答案 2 :(得分:2)
我同意mikera的说法,你应该尝试不可变地做到这一点,但你要求专门针对可变属性,所以我的建议方式与mikera的答案非常相似,但要在地图中使用atoms你将拥有可能想要改变的可变属性。
(def player1
{:type :player
:team :red
:hit-points (atom 10)
:location (atom [17 9 6])})
请注意,只有您可能想要更改的内容才会包含在原子中。为了访问可变数据,您必须取消引用它,如下所示:
@(player1 :hit-points)
10
(swap! (player1 :hit-points) dec)
9
@(player1 :hit-points)
9
(reset! (player1 :hit-points) 2)
2
@(player1 :hit-points)
2
这可以作为制作一个玩家的一个例子,尽管你要求的东西有init,getter和setter。我应该说在这一点上我几乎没有Clojure以外的编程经验,所以我可能没有完全掌握那些将会是什么,但这就是我将如何设置它。
(defn new-player
[hit-points location]
{:type :player
:team :red
:hit-points (atom hit-points)
:location (atom location)})
然后,当我想制作新玩家时,我会这样做:
(def my-player
(new-player 20 [0 0 0]))
{:type :player
:team :red
:hit-points #<Atom@1959415: 20>
:location #<Atom@12d0e49: [0 0 0]>}
我认为没有必要制作明确的“getter”和/或“setter”,因为您可以通过解除引用来获取任何可变数据,并使用swap!
或reset!
设置任何可变数据与我上面展示的方式完全相同。话虽如此,如果你愿意,你可以这样做:
(defn get-hp
[player]
@(player :hit-points))
(defn get-loc
[player]
@(player :location))
(defn set-hp
[player new-hp]
(reset! (player :hit-points) new-hp))
(defn set-loc
[player new-loc]
(reset! (player :location) new-loc))
现在你可以这样做:
(get-hp my-player)
20
(get-loc my-player)
[0 0 0]
(set-hp my-player 17)
17
(get-hp my-player)
17
(set-loc my-player [0 1 1])
[0 1 1]
(get-loc my-player)
[0 1 1]
由于这个答案还不够长,我认为在制作新玩家时包含默认值可能会很好。我可以想到一个简单但不一定优雅的方法:
(defn new-player
([]
(new-player 20 [0 0 0]))
([hp-or-loc]
(cond
(integer? hp-or-loc) (new-player hp-or-loc [0 0 0])
(vector? hp-or-loc) (new-player 20 hp-or-loc)
:else (throw (Exception. "Value must be either integer for hp or vector for location."))))
([hp loc]
{:type :player
:team :red
:hit-points (atom hp)
:location (atom loc)}))
现在默认情况下,新玩家将拥有20马力并位于[0 0 0]位置。如果传递整数或向量,它将假定应该是hp或location(分别)的值,否则它将抛出异常。
同样,我认为在大多数情况下可变数据可能是不必要的,最简单的解决方案可能是将问题设想为“如何制作这种可变数据结构”,而不是“如何创建新的,更新版本的不可变数据,然后将这些数据传递回循环的开头,之后我可能会更新并再次重复“。
希望其中一些有用。