在Clojure中保留带有索引的有状态查找表的习惯性方法
我对Clojure和函数式编程相当陌生,并且一直在努力解决以下问题。 我想为一系列令牌(字符串)分配一个唯一且稳定的索引。 由于会有比插入更多的查找,哈希映射似乎是要走的路。
在Java中,我会写一些东西
int last = 0;
HashMap<String, Integer> lut = new HashMap<String, Integer>();
function Integer getIndex(String token) {
Integer index = lut.get(token);
if(index == null)
last++;
lut.put(token, last);
return last;
else {
return index;
}
}
Clojure中的音译版本就像
(def last-index (atom 0))
(def lookup-table (atom {}))
(defn get-index [token]
(if (nil? (get @lookup-table token))
(do
(swap! last-index inc)
(swap! lookup-table assoc token @last-index)
@last-index)
(get @lookup-table token)))
但是,这似乎并不是很具代表性,因为它基本上是副作用,甚至没有隐藏它。
那么,如果没有两个原子来保持状态,你会如何做到这一点?
Ankur给出的答案不是线程安全的,尽管我不认为他的描述为什么是非常有用的,而且他的选择更糟糕。 说“我现在不担心多线程”是合理的,在这种情况下,答案是好的。 但是,即使您在任何特定情况下都不需要保证,也能够安全地编写这些内容,这是非常有价值的,唯一安全的方法是在swap!
内部完成所有逻辑swap!
,如下所示:
(let [m (atom {})]
(defn get-index [token]
(get (swap! m
#(assoc % token (or (% token) (count %))))
token)))
你可以通过避免swap!
来加速这一点swap!
如果函数被调用时已经有一个条目,并且如果在进入swap!
已经有一个条目,则避免使用assoc swap!
,但是你必须“仔细检查”地图在赋值之前没有当前令牌的条目(count %)
,因为在开始swap!
之前,其他线程可能会隐藏起来swap!
(但在决定swap!
),并为当前令牌分配一个值,在这种情况下,您必须遵守该分配,而不是创建一个新的。
编辑:顺便说一下,Java版本当然有相同的线程安全问题,因为默认情况下,Java中的所有内容都是可变的,而不是线程安全的。 至少在Clojure中你必须放一个!
在那里,说:“是的,我知道这很危险,我知道我在做什么。”
所以从某种意义上说,Ankur的解决方案是Java代码的完美翻译,但更好的是改进它!
原子中的单个地图就足够了:
(def m (atom {}))
;adding new string to map
(swap! m #(assoc %1 "Hello" (count %)))
;get an index
(@m "Hello")
(defn get-index [token]
(or (@m token)
((swap! m #(assoc %1 token (count %))) token)))
你基本上试图将Java命令式代码映射到clojure,这就是为什么你在你的问题中得到了这个解决方案。 试着从构成表情的角度思考,而不是思考步骤式的强制性风格。
链接地址: http://www.djcxy.com/p/11469.html上一篇: Idiomatic way of keeping a stateful lookup table with indexes in Clojure
下一篇: Sectioning Fetched Results Controller breaks Search Display