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
Java 在Clojure中保存带索引的有状态查找表的惯用方法_Java_Clojure_Hashmap_State - Fatal编程技术网

Java 在Clojure中保存带索引的有状态查找表的惯用方法

Java 在Clojure中保存带索引的有状态查找表的惯用方法,java,clojure,hashmap,state,Java,Clojure,Hashmap,State,一般来说,我对Clojure和函数式编程相当陌生,我一直在努力解决以下问题。我想为一系列标记(字符串)分配一个唯一且稳定的索引。由于将有比插入更多的查找,因此哈希映射似乎是一种可行的方法 在Java中,我会按照 int last = 0; HashMap<String, Integer> lut = new HashMap<String, Integer>(); function Integer getIndex(String token) { Integer

一般来说,我对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;
    }
}
但这似乎不是很像我,因为它基本上是副作用,甚至没有隐藏它


那么,在没有两个原子保持状态的情况下,您如何做到这一点呢?

原子中的单个贴图就足够了:

(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,这就是为什么您在问题中得到了该解决方案。试着从组合表达式的角度来思考,而不是从循序渐进的命令式风格来思考。

Ankur给出的答案不是线程安全的,尽管我不认为seh对原因的描述很有帮助,他的选择更糟糕。说“我现在不担心多线程”是有道理的,在这种情况下,答案很好。但是能够安全地编写这样的东西是很有价值的,即使在任何特定情况下都不需要这种保证,唯一安全的方法是在
交换中执行所有逻辑,如下所示:

(let [m (atom {})]
  (defn get-index [token]
    (get (swap! m
                #(assoc % token (or (% token) (count %))))
         token)))
您可以通过避免
交换来加快速度如果在调用函数时已经有一个条目,并且在您输入
交换后,如果已经有条目,则避免关联,但您必须“仔细检查”映射是否没有当前令牌的条目,然后才分配它
(count%)
,因为在您开始
交换之前,可能有其他线程潜入
ing(但在您决定
swap!
之后),并为当前令牌分配了一个值,在这种情况下,您必须尊重该分配,而不是进行新的分配

编辑:顺便说一下,Java版本当然也有同样的线程安全问题,因为默认情况下Java中的所有内容都是可变的,而不是线程安全的。至少在Clojure中,您必须输入一个


所以从某种意义上说,Ankur的解决方案是Java代码的完美翻译,但更好的是改进它

值得注意的是,您永远不希望有这样的代码来修改相互依赖的两个原子。原子是独立态。如果需要对相互依赖的多个变量进行变异,则应使用refs和dosync。考虑到Ankur的回答,在这里并不是特别相关,但要记住一点。你为什么建议使用
swap而不是
比较并设置
,甚至在
dosync
调用中重新绑定映射?如果您希望与其他线程竞争,请使用
swap这里允许在不知情的情况下用你的更改覆盖他们的更改。我很好奇你认为我的便条会让事情变得更糟。我很简短,但我的观点是,在这里无条件地重新绑定atom是不安全的,因为两个正在运行的线程这样做会放弃另一个线程试图进行的更改。如何协调这两条赛道是设计问题。重新阅读
交换的文档今天,我看到它在内部旋转,直到它一致地设置一个值,因此我建议使用
比较并设置将是过分的。我仍然认为对ref使用
dosync
是最清晰的方法。两个线程竞相使用
(swap!af)
永远不要“丢弃”更改。原子将始终处于一致状态,并且最终
a
将设置为
(f(f old-a))
。问题是,他正在根据
swap外部的信息构建
f1
f2
可能已过期<代码>比较和交换只会让犯这个错误变得更容易。
dosync
是可以的,只要你确保它是一个ref而不是atom,但是管理一个引用和知道如何编写一个好的
swap通常是过火了很重要。
(let [m (atom {})]
  (defn get-index [token]
    (get (swap! m
                #(assoc % token (or (% token) (count %))))
         token)))