Sabit Sekizli
Bir noktada, doğru satırları ve sütunları korumak için baştaki sıfırları kullanan bir matriste okuyordum. Matematiksel olarak bu doğrudur, çünkü sıfırın başındaki değer açıkça altta yatan değeri değiştirmez. Bununla birlikte, bu matrisle bir değişken tanımlama girişimleri şunlarla gizemli bir şekilde başarısız olur:
java.lang.NumberFormatException: Invalid number: 08
bu beni tamamen şaşırttı. Bunun nedeni, Clojure'un baştaki sıfırlar ile tam sayı değerlerini sekizli olarak ele alması ve sekizlik tabanda 08 sayısının olmamasıdır.
Clojure'un 0x öneki aracılığıyla geleneksel Java onaltılık değerlerini desteklediğini de belirtmeliyim . Ayrıca "taban + r + değer" gösterimini kullanarak 2 ile 36 arasındaki herhangi bir tabanı da kullanabilirsiniz, örneğin 2r101010 veya 36r16 42 taban on olan.
Anonim bir işlev değişmez değerinde değişmez değerleri döndürmeye çalışma
Bu çalışıyor:
user> (defn foo [key val]
{key val})
#'user/foo
user> (foo :a 1)
{:a 1}
bu yüzden bunun da işe yarayacağına inandım:
(#({%1 %2}) :a 1)
ancak şununla başarısız olur:
java.lang.IllegalArgumentException: Wrong number of args passed to: PersistentArrayMap
çünkü # () okuyucu makrosu,
(fn [%1 %2] ({%1 %2}))
harita değişmez değeri parantez içine alınır. İlk öğe olduğu için, bir işlev olarak ele alınır (gerçek bir harita aslında budur), ancak gerekli bağımsız değişkenler (anahtar gibi) sağlanmaz. Özetle, anonim işlev değişmezi gelmez değil genişletmek
(fn [%1 %2] {%1 %2})
ve bu nedenle anonim işlevin gövdesi olarak herhangi bir gerçek değere ([],: a, 4,%) sahip olamazsınız.
Yorumlarda iki çözüm verildi. Brian Carper , aşağıdaki gibi dizi uygulama oluşturucularının (dizi haritası, karma küme, vektör) kullanılmasını önerir:
(#(array-map %1 %2) :a 1)
ederken Dan gösterileri kendinizin kullanabileceği kimlik dış parantez unwrap işlevi:
(#(identity {%1 %2}) :a 1)
Brian'ın önerisi aslında beni bir sonraki hatama getiriyor ...
Karma haritanın veya dizi haritasının değişmeyen somut harita uygulamasını belirlediğini düşünmek
Aşağıdakileri göz önünde bulundur:
user> (class (hash-map))
clojure.lang.PersistentArrayMap
user> (class (hash-map :a 1))
clojure.lang.PersistentHashMap
user> (class (assoc (apply array-map (range 2000)) :a :1))
clojure.lang.PersistentHashMap
Genelde bir Clojure haritasının somut uygulaması hakkında endişelenmenize gerek kalmazken, assoc veya conj gibi bir harita oluşturan işlevlerin bir PersistentArrayMap alıp daha büyük haritalar için daha hızlı performans gösteren bir PersistentHashMap döndürebileceğini bilmelisiniz .
İlk bağlamaları sağlamak için bir döngü yerine özyineleme noktası olarak bir işlevi kullanma
Başladığımda, bunun gibi birçok işlev yazdım:
(defn p3
([] (p3 775147 600851475143 3))
([i n times]
(if (and (divides? i n) (fast-prime? i times)) i
(recur (dec i) n times))))
Aslında döngü , bu belirli işlev için daha kısa ve deyimsel olduğunda:
(defn p3 [] {:post [(= % 6857)]}
(loop [i 775147 n 600851475143 times 3]
(if (and (divides? i n) (fast-prime? i times)) i
(recur (dec i) n times))))
Boş bağımsız değişken olan "varsayılan yapıcı" işlev gövdesini (p3 775147 600851475143 3) bir döngü + ilk bağlamayla değiştirdiğime dikkat edin. Tekrarlanmasını şimdi (yerine fn parametrelerin) döngü bağlantıları rebinds ve (yerine fn arasında döngü) geri tekrarlama noktasına atlar.
"Hayali" değişkenlere başvurma
Keşif programlamanız sırasında REPL'i kullanarak tanımlayabileceğiniz var tipinden bahsediyorum ve sonra bilmeden kaynağınızda referans alın. Siz ad alanını yeniden yükleyene kadar (belki düzenleyicinizi kapatarak) ve daha sonra kodunuz boyunca referans verilen bir grup bağlantısız sembol keşfedene kadar her şey yolunda gider. Bu aynı zamanda, bir değişkeni bir ad alanından diğerine taşırken, yeniden düzenleme yaparken sık sık olur.
For list anlayışına for döngüsü zorunluluğu gibi davranmak
Esasen, sadece kontrollü bir döngü gerçekleştirmek yerine mevcut listelere dayalı bir tembel liste oluşturuyorsunuz. Clojure en doseq aslında zorunlu foreach döngü yapılara daha benzer.
Nasıl farklı olduklarına bir örnek, keyfi yüklemler kullanarak hangi öğeleri yinelediklerini filtreleme yeteneğidir:
user> (for [n '(1 2 3 4) :when (even? n)] n)
(2 4)
user> (for [n '(4 3 2 1) :while (even? n)] n)
(4)
Farklı olmalarının bir başka yolu da sonsuz tembel diziler üzerinde çalışabilmeleridir:
user> (take 5 (for [x (iterate inc 0) :when (> (* x x) 3)] (* 2 x)))
(4 6 8 10 12)
Ayrıca, önce en sağdaki ifadeyi yineleyerek ve sola doğru ilerleyerek birden fazla ciltleme ifadesini de işleyebilirler:
user> (for [x '(1 2 3) y '(\a \b \c)] (str x y))
("1a" "1b" "1c" "2a" "2b" "2c" "3a" "3b" "3c")
Ayrıca ara yok veya erken çıkmaya devam et.
Yapıların aşırı kullanımı
OOPish bir geçmişe sahip olduğum için Clojure'e başladığımda beynim hala nesneler açısından düşünüyordu. Kendimi her şeyi bir yapı olarak modellerken buldum çünkü "üyelerin" gruplanması, ne kadar gevşek olursa olsun, beni rahat hissettiriyordu. Gerçekte, yapılar çoğunlukla bir optimizasyon olarak düşünülmelidir; Clojure, belleği korumak için anahtarları ve bazı arama bilgilerini paylaşır. Anahtar arama sürecini hızlandırmak için erişimciler tanımlayarak bunları daha da optimize edebilirsiniz .
Genel olarak , performans dışında bir harita üzerinde yapı kullanmaktan hiçbir şey kazanmazsınız , bu nedenle eklenen karmaşıklık buna değmeyebilir.
Şekersiz BigDecimal oluşturucuları kullanma
Çok fazla BigDecimals'a ihtiyacım vardı ve şöyle çirkin kod yazıyordum:
(let [foo (BigDecimal. "1") bar (BigDecimal. "42.42") baz (BigDecimal. "24.24")]
Clojure , sayıya M ekleyerek BigDecimal değişmez değerlerini desteklediğinde :
(= (BigDecimal. "42.42") 42.42M)
Şekerli versiyonu kullanmak şişkinliğin çoğunu ortadan kaldırır. Yorumlarda twils , bigdec ve bigint işlevlerini daha açık ve özlü kalabilmek için de kullanabileceğinizi belirtti .
Ad alanları için Java paketi adlandırma dönüşümlerini kullanma
Bu aslında kendi başına bir hata değil, daha ziyade tipik bir Clojure projesinin deyimsel yapısına ve isimlendirmesine aykırı bir şey. İlk önemli Clojure projemde aşağıdaki gibi ad alanı bildirimleri ve bunlara karşılık gelen klasör yapıları vardı:
(ns com.14clouds.myapp.repository)
tam nitelikli işlev referanslarımı şişiren:
(com.14clouds.myapp.repository/load-by-name "foo")
İşleri daha da karmaşık hale getirmek için standart bir Maven dizin yapısı kullandım:
|-- src/
| |-- main/
| | |-- java/
| | |-- clojure/
| | |-- resources/
| |-- test/
...
Bu, "standart" Clojure yapısından daha karmaşıktır:
|-- src/
|-- test/
|-- resources/
bu, Leiningen projelerinin ve Clojure'un kendisinin varsayılanıdır .
Haritalar anahtar eşleştirme için Clojure's = yerine Java's equals () kullanır
İlk olarak IRC'de chouser tarafından bildirilen Java's equals () ' ın bu kullanımı bazı sezgisel olmayan sonuçlara yol açar:
user> (= (int 1) (long 1))
true
user> ({(int 1) :found} (int 1) :not-found)
:found
user> ({(int 1) :found} (long 1) :not-found)
:not-found
Her iki yana Tamsayı ve Uzun 1 örneklerini varsayılan olarak aynı basılır, harita herhangi bir değer döndürüyor değil neden algılamak için zor olabilir. Bu, özellikle anahtarınızı, belki de size haber vermeden uzun bir süre döndüren bir işlevden geçirdiğinizde doğrudur.
Haritaların java.util.Map arayüzüne uyması için Clojure's = yerine Java's equals () kullanmanın gerekli olduğu unutulmamalıdır .
Ben kullanıyorum Clojure Programlama Stuart Halloway tarafından Pratik Clojure Luke VanderHart tarafından ve sayısız Clojure hacker yardımıyla IRC benim cevaplar boyunca yardım ve posta listesine.