Clojure : 관용적 인 Clojure 방식으로 java.util.hashmap과 함께 일하는
-
13-09-2019 - |
문제
나는있다 java.util.HashMap
물체 m
(호출에서 Java 코드로의 반환 값) 추가 키 값 쌍이있는 새 맵을 가져오고 싶습니다.
만약에 m
Clojure지도 였는데 다음을 사용할 수있었습니다.
(assoc m "key" "value")
그러나 그것을 시도합니다 HashMap
제공 :
java.lang.classcastException : java.util.hashmap은 clojure.lang.associative에 캐스트 할 수 없습니다
운이 없습니다 seq
어느 하나:
(assoc (seq m) "key" "value")
java.lang.classcastException : clojure.lang.eratorseq는 clojure.lang.associative에 캐스트 할 수 없습니다
내가 할 수있는 유일한 방법은 사용하는 것이 었습니다. HashMap
자신의 put
, 그러나 그것은 돌아옵니다 void
그래서 나는 명시 적으로 돌아와야합니다 m
:
(do (. m put "key" "value") m)
이것은 관용적 인 clojure 코드가 아니며, 수정하고 있습니다. m
새 맵을 만드는 대신.
작업하는 방법 a HashMap
더 clojure-ish 방식으로?
해결책
Clojure는 Java 컬렉션을 SEQ- 가능하게 만들므로 Clojure 시퀀스 기능을 java.util.hashmap.
하지만 협회 예상 a clojure.lang.associative 따라서 먼저 변환해야합니다 java.util.hashmap 그것에 :
(assoc (zipmap (.keySet m) (.values m)) "key" "value")
편집 : 간단한 솔루션 :
(assoc (into {} m) "key" "value")
다른 팁
Java 코드와 인터페이스하는 경우 총알을 물고 Java Way를 사용해야 할 수도 있습니다. .put
. 이것이 필사자의 죄가 아닙니다. Clojure는 당신과 같은 것을 제공합니다 do
그리고 .
특히 Java 코드를 쉽게 작업 할 수 있습니다.
assoc
Clojure 데이터 구조에서만 작동합니다. 많은 작업이 약간의 변경으로 새로운 (불변) 사본을 만들기 위해 매우 저렴하게 만들었 기 때문입니다. Java Hashmaps는 같은 방식으로 작동하지 않습니다. 변경할 때마다 계속 복제해야합니다.
Java Mutation-Land에서 실제로 나가고 싶다면 (예 : 아마도이 해시 랩을 오랫동안 유지하고 있으며 Java가 모든 곳에서 전화를 걸지 않기를 원하거나, 당신은 그들을 통해 직렬화해야합니다. print
그리고 read
, Clojure STM을 사용하여 스레드 안전 방식으로 작업하려고합니다.) Clojure 데이터 구조가 올바른 Java 인터페이스를 구현하여 서로 대화 할 수 있기 때문에 Java Hashmaps와 Clojure 해시 맵 사이를 쉽게 변환 할 수 있습니다.
user> (java.util.HashMap. {:foo :bar})
#<HashMap {:foo=:bar}>
user> (into {} (java.util.HashMap. {:foo :bar}))
{:foo :bar}
당신이 원한다면 do
-작업을 마친 후에 작업중 인 객체를 반환하는 것과 같은 것은 사용할 수 있습니다. doto
. 실제로, Java Hashmap 은이 기능의 공식 문서의 예로 사용됩니다. 이는 Java 객체를 사용하는 경우 (신중하게) 세계의 끝이 아니라는 또 다른 표시입니다.
clojure.core/doto
([x & forms])
Macro
Evaluates x then calls all of the methods and functions with the
value of x supplied at the front of the given arguments. The forms
are evaluated in order. Returns x.
(doto (new java.util.HashMap) (.put "a" 1) (.put "b" 2))
몇 가지 가능한 전략 :
가능하면 돌연변이와 부작용을 단일 함수로 제한하십시오. 함수가 항상 동일한 입력이 주어진 동일한 값을 반환하는 경우 내부적으로 원하는대로 수행 할 수 있습니다. 때로는 배열이나 맵을 돌리는 것이 알고리즘을 구현하는 가장 효율적이거나 가장 쉬운 방법입니다. 당신은 세계 다른 지역에 "누출"부작용을하지 않는 한 여전히 기능 프로그래밍의 이점을 누릴 것입니다.
객체가 잠시 동안 주변에 있거나 다른 Clojure 코드와 잘 놀어야한다면, 가능한 빨리 Clojure 데이터 구조로 가져 와서 마지막 순간에 Java Hashmaps에 다시 캐스팅하십시오 (먹이를 먹을 때. 다시 Java로 돌아갑니다).
전통적인 방식으로 Java Hash지도를 사용해도 괜찮습니다.
(do (. m put "key" "value") m)
This is not idiomatic Clojure code, plus I'm modifying m instead of creating a new map.
실제로 수정 대상 데이터 구조를 수정하고 있습니다. Java의 해시 맵에는 구조 공유가 부족합니다 이를 통해 Clojures Map을 효율적으로 복사 할 수 있습니다. 이 작업을 수행하는 일반적으로 관용적 인 방법은 Java-Interop 기능을 사용하여 전형적인 Java 방식의 Java 구조와 함께 작동하거나 Clojure 구조로 깨끗하게 변환하여 기능적 클로 주행 방식으로 작업하는 것입니다. 물론 인생이 더 쉬워지고 더 나은 코드를 초래하지 않는 한; 그런 다음 모든 베팅이 꺼져 있습니다.
이것은 Clojure 버전과 Java의 메모리 특성을 비교하려고 할 때 해시 맵을 사용하여 작성한 일부 코드입니다 (그러나 Clojure에서 사용).
(import '(java.util Hashtable)) (defn frequencies2 [coll] (let [mydict (new Hashtable)] (reduce (fn [counts x] (let [y (.toLowerCase x)] (if (.get mydict y) (.put mydict y (+ (.get mydict y) 1)) (.put mydict y 1)))) coll) mydict))
이것은 약간의 컬렉션을 취하고 각각의 다른 것 (문자열의 단어)이 몇 번이나 재사용되는지를 반환하는 것입니다.