2010-10-22 29 views
8

Maven kullanarak JAR dosyası olarak oluşturduğum bir Clojure programım var. JAR Manifest'ine gömülü, yapı zaman damgası da dahil olmak üzere bir yapı-versiyon numarasıdır.Çalışma zamanında Clojure "sabitlerini" ayarlama

kolayca aşağıdaki kodu kullanarak JAR Manifest dan zamanında bunu okuyabilir:

(defn set-version 
    "Set the version variable to the build number." 
    [] 
    (def version 
    (-> (str "jar:" (-> my.ns.name (.getProtectionDomain) 
            (.getCodeSource) 
            (.getLocation)) 
        "!/META-INF/MANIFEST.MF") 
     (URL.) 
     (.openStream) 
     (Manifest.) 
     (.. getMainAttributes) 
     (.getValue "Build-number")))) 

ama defn içeride def kullanmak kötü karma olduğu söylendi.

Çalışma zamanında sabit ayarlamak için Clojure-deyimsel yolu nedir? Açıkçası kodumda def olarak gömmek için sürüm bilgisi bilgisi yok, ancak program başladığında main işlevinden bir kez (ve hepsine) ayarlanmasını istiyorum. Daha sonra çalışan kodun geri kalanına def olarak ulaşılabilir olmalıdır.

UPDATE: BTW, Clojure, uzun zamandır karşılaştığım en havalı dillerden biri olmalı. Rich Hickey için Kudos!

cevap

7

Uygulamanızın en temiz yolu, main yönteminde alter-var-root yöntemini kullanmayı düşünüyorum.

(declare version) 

(defn -main 
    [& args] 
    (alter-var-root #'version (constantly (-> ...))) 
    (do-stuff)) 

Bu derleme zamanında Var beyan kez zamanında kök değerini ayarlar, DEREF gerektirmez ve ana iş parçacığı bağlı değildir. Önceki sorunuzda bu öneriye cevap vermediniz. Bu yaklaşımı denedin mi?

+0

Denemedim, ama yapacağım. Enteresan görünüyor. Değer belirlendiğinde performans cezası yok. Komut satırı seçeneklerinden değerleri ayarlamak için bu tekniği de kullanabilirim - sadece bir kez ayarlanması gerekir. – Ralph

1

Umarım bu sefer bir şey özlemem.

Eğer sürüm sabit ise, bir defa tanımlanacak ve değiştirilmeyecek, basitçe defn'i kaldırarak (def versiyonu ...) tek başına saklayabilirsiniz. Sanırım bunu bir sebepten istemiyorsun.

(def *version* (atom "")) 

(defn set-version! [] (swap! *version* ...)) 
+0

@jneira: "Umarım bu sefer bir şey özlemem." :-) Bunu yapamam çünkü derleme zamanında değerlendirir (inanıyorum) ve henüz okuyacağınız bir JAR dosyası yok. – Ralph

+0

hehe: -P tamam ve ikinci seçenek? (üçüncüsü, işlev yerine dizeleri geri döndürür, ancak çeşitli çağrılar için verimsiz olabilirdi) – jneira

+0

Atomlarla bir şeyler yapmam gerektiğini düşünürdüm, sadece fazlalık olacağını düşündüm, senkronizasyon yükü, vb. Belki de "def" 'i "ana" işlevinden çağırmak, "çatık" olsa bile, tamam olur. – Ralph

4
: Bir fn global değişkenler değiştirmek isterseniz

i daha deyimsel yolu veri ve erişim depolamak için eşzamanlılık inşaatlar bazılarını kullanmak ve Örneğin güvenli bir şekilde bunu değiştirmek olduğunu düşünüyorum

Dinamik bağlamayı kullanabilirsiniz.

(declare *version*) 

(defn start-my-program [] 
    (binding [*version* (read-version-from-file)] 
    (main)) 

Şimdi main ve *version* değerini göreceksiniz çağıran her fonksiyonu.

+0

+1 bana "ve çağırdığı her işlevi" hatırlatmak dinamik bir bağlanma, belki de sorun uyuyor – jneira

+0

Ben bunu seviyorum! Atom kullanacaktım, ama bunu daha fazla araştırmam gerekecek. Ayarlamam gereken değişkenler (sabitler), programın geri kalanı zaten çalışmadan önce ayarlanmalıdır. Versiyon numarası yanında, Apache commons-cli ile elde edilen komut satırı seçeneklerini kaydetmek için de çözüm kullanacağım. – Ralph

+3

"Bağlantının" iş parçacığı sınırlarını unutmayın. Ayrıca bkz. "Bound-fn". – kotarak

2

Kotarak'ın çözümü gayet iyi çalışıyor olsa da, burada alternatif bir yaklaşım var: kodunuzu, sürümü döndüren belleğe alınmış bir işleve dönüştürün. Bunun gibi:

(def get-version 
(memoize 
    (fn [] 
    (-> (str "jar:" (-> my.ns.name (.getProtectionDomain) 
      (.getCodeSource) 
      (.getLocation)) 
     "!/META-INF/MANIFEST.MF") 
    (URL.) 
    (.openStream) 
    (Manifest.) 
    (.. getMainAttributes) 
    (.getValue "Build-number"))))) 
İlgili konular