2009-07-25 48 views
10

İki yönlü bir haritayı clojure uygulamanın en iyi yolu ne olurdu? (İki yönlü harita ile, A-> B ve B-> A erişimini sağlayabilen ilişkisel bir haritayı kastediyorum. Bu nedenle, değerler kendileri ters yöne gitmek için anahtarlar olacaktır.)clojure iki yönlü bir harita?

I her haritada bir tane olmak üzere iki tane harita kurabildiğimi varsayalım, ama bunu yapmanın daha aptalca bir yolu var mı?

Her iki anahtarın aynı değere ve bu koşulun uygulanmadığı durumlara eşlenemeyeceğini ima ederek, bir bijeksiyon istediğimiz durumlarda ilgileniyorum.

cevap

12

Apache commons'daki koleksiyonlardan biri gibi bunun için her zaman bir Java kitaplığı kullanabilirsiniz. TreeBidiMap, java.util.Map'u uygular, bu yüzden herhangi bir çaba harcamadan bile yapılabilir. onlar kalıcı koleksiyonları bekliyoruz beri

user> (def x (org.apache.commons.collections.bidimap.TreeBidiMap.)) 
#'user/x 
user> (.put x :foo :bar) 
nil 
user> (keys x) 
(:foo) 
user> (.getKey x :bar) 
:foo 
user> (:foo x) 
:bar 
user> (map (fn [[k v]] (str k ", " v)) x) 
(":foo, :bar") 

Bazı şeyler, assoc ve dissoc gibi olsa çalışmaz ve TreeBidiMap değişkendir.

Bunu yerel Clojure'da yapmak istiyorsanız, ters yönlü karmayı tutmak için meta verileri kullanabilirsiniz. Bu hala bellek gereksinimlerinizi iki katına çıkaracak ve her ekleme ve silme için zamanı iki katına çıkaracak, ancak aramalar yeterince hızlı olacak ve en azından her şey paketlenecek.

(defn make-bidi [] 
    (with-meta {} {})) 

(defn assoc-bidi [h k v] 
    (vary-meta (assoc h k v) 
      assoc v k)) 

(defn dissoc-bidi [h k] 
    (let [v (h k)] 
    (vary-meta (dissoc h k) 
       dissoc v))) 

(defn getkey [h v] 
    ((meta h) v)) 

Elbette, işlevsellikten tam olarak yararlanabilmek için bir dizi başka işlev uygulamak zorunda kalabilirsiniz. Bu yaklaşımın ne kadar uygun olduğundan emin değil.

user> (def x (assoc-bidi (make-bidi) :foo :bar)) 
#'user/x 
user> (:foo x) 
:bar 
user> (getkey x :bar) 
:foo 
+1

Teşekkürler, bu yararlı. Bir clojure yerel seçeneğim olmasını tercih ederim, bu yüzden ikinci fikrin deneyebileceğim bir şey. –

3

Çoğu durumda, ben gayet aşağıdaki eserlerini bulduk:

(defn- bimap 
    [a-map] 
    (merge a-map (clojure.set/map-invert a-map))) 

Bu sadece yeni bir harita haline ters harita ile orijinal harita birleşecek o zaman normal harita işlemlerini kullanabilirsiniz sonuçla beraber.

Hızlı ve kirli, ancak bu anahtarlardan herhangi biri değerlerden herhangi biri ile aynıysa veya a-map'daki değerler benzersiz değilse, bu uygulamadan kaçınmalısınız.

1

şu şekilde bu sorunu reframe edebilirsiniz:
hızla a ve b ikisi tarafından dizilerini arama yapabilmek istediğiniz tüp ([a1 b1] [a2 b2] ...) listesi göz önüne alındığında. Dolayısıyla, a -> [a b] ve b -> [a b] eşlemelerini istiyoruz. Yani en az tuples paylaşılabilir. Ve bunu uygulamayı düşünürsek, bu durumun, her iki sütun tarafından indekslenen A ve B sütunlarına sahip bir bellek içi veritabanı tablosu olduğu açıkça anlaşılmaktadır.

Sadece bellekte hafif bir veritabanı kullanabilirsiniz.

Üzgünüz, belirli bir şey önermek için Java Platform'a aşina değilim. Neden Datomic akla geliyor, ancak bu özel kullanım durumu için bir overkill olabilir. Öte yandan, Brian'ın cevabına yaptığınız görüşe dayanarak size arzu edilen görünüşte değişmezlik söz konusudur.