शाब्दिक अष्टक
एक बिंदु पर मैं एक मैट्रिक्स में पढ़ रहा था जो उचित पंक्तियों और स्तंभों को बनाए रखने के लिए अग्रणी शून्य का उपयोग करता था। गणितीय रूप से यह सही है, क्योंकि अग्रणी शून्य स्पष्ट रूप से अंतर्निहित मूल्य को नहीं बदलता है। हालांकि, इस मैट्रिक्स के साथ एक संस्करण को परिभाषित करने के प्रयास रहस्यमय तरीके से विफल होंगे:
java.lang.NumberFormatException: Invalid number: 08
जो मुझे पूरी तरह से चकित कर गया। कारण यह है कि क्लोजर ऑक्टल पूर्णांक मानों को अग्रणी शून्य के साथ अष्टक के रूप में मानता है, और ऑक्टल में संख्या 08 नहीं है।
मुझे यह भी उल्लेख करना चाहिए कि क्लोजर 0x उपसर्ग के माध्यम से पारंपरिक जावा हेक्साडेसिमल मूल्यों का समर्थन करता है । आप "बेस + आर + वैल्यू" नोटेशन का उपयोग करके 2 और 36 के बीच किसी भी आधार का उपयोग कर सकते हैं, जैसे कि 2r101010 या 36r16 जो 42 बेस दस हैं।
एक अनाम फ़ंक्शन शाब्दिक में शाब्दिक वापसी की कोशिश कर रहा है
यह काम:
user> (defn foo [key val]
{key val})
#'user/foo
user> (foo :a 1)
{:a 1}
इसलिए मुझे विश्वास था कि यह भी काम करेगा:
(#({%1 %2}) :a 1)
लेकिन इसके साथ असफल:
java.lang.IllegalArgumentException: Wrong number of args passed to: PersistentArrayMap
क्योंकि # () रीडर मैक्रो का विस्तार हो जाता है
(fn [%1 %2] ({%1 %2}))
कोष्ठक में लिपटे मानचित्र शाब्दिक के साथ। चूंकि यह पहला तत्व है, इसलिए इसे एक फ़ंक्शन के रूप में माना जाता है (जो वास्तव में एक शाब्दिक मानचित्र है), लेकिन कोई आवश्यक तर्क (जैसे कि एक कुंजी) प्रदान नहीं किए जाते हैं। सारांश में, अनाम फ़ंक्शन शाब्दिक का विस्तार नहीं करता है
(fn [%1 %2] {%1 %2}) ; notice the lack of parenthesis
और इसलिए आपके पास अनाम फ़ंक्शन के शरीर के रूप में कोई शाब्दिक मूल्य ([]: a, 4,%) नहीं हो सकता है।
टिप्पणियों में दो समाधान दिए गए हैं। ब्रायन कारपर अनुक्रम कार्यान्वयन कंस्ट्रक्टरों का उपयोग करने का सुझाव देते हैं (सरणी-नक्शा, हैश-सेट, वेक्टर) जैसे:
(#(array-map %1 %2) :a 1)
जबकि दान से पता चलता है कि आप बाहरी कोष्ठक को खोलने के लिए पहचान फ़ंक्शन का उपयोग कर सकते हैं :
(#(identity {%1 %2}) :a 1)
ब्रायन का सुझाव वास्तव में मुझे मेरी अगली गलती के लिए लाता है ...
यह सोचकर कि हैश-मैप या सरणी-मानचित्र अपरिवर्तनीय कंक्रीट मानचित्र कार्यान्वयन को निर्धारित करता है
निम्नलिखित को धयान मे रखते हुए:
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
जब आप आम तौर पर एक क्लोजर मानचित्र के ठोस कार्यान्वयन के बारे में चिंता करने की ज़रूरत नहीं होगी, तो आपको पता होना चाहिए कि फ़ंक्शंस जो एक नक्शा विकसित करते हैं - जैसे कि एशोक या संयोजन - एक PersistentArrayMap ले सकते हैं और एक PersistentHashMap लौटा सकते हैं , जो बड़े मानचित्रों के साथ तेज़ी से कार्य करता है।
प्रारंभिक बाइंडिंग प्रदान करने के लिए लूप के बजाय पुनरावृत्ति बिंदु के रूप में एक फ़ंक्शन का उपयोग करना
जब मैंने शुरुआत की, तो मैंने इस तरह के कई कार्य लिखे:
; Project Euler #3
(defn p3
([] (p3 775147 600851475143 3))
([i n times]
(if (and (divides? i n) (fast-prime? i times)) i
(recur (dec i) n times))))
जब वास्तव में लूप इस विशेष कार्य के लिए अधिक संक्षिप्त और मुहावरेदार होगा:
; Elapsed time: 387 msecs
(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))))
ध्यान दें कि मैंने खाली तर्क, "डिफ़ॉल्ट निर्माणकर्ता" फ़ंक्शन बॉडी (p3 775147 600851475143 3) को लूप + प्रारंभिक बाध्यकारी के साथ बदल दिया। पुनरावृत्ति होना अब पाश बाइंडिंग (एफ एन मानकों के बजाय) rebinds और (एफ एन के बजाय पाश,) वापस प्रत्यावर्तन बात करने के लिए कूदता है।
"फैंटम" संस्करण को संदर्भित करना
मैं आपके द्वारा खोजे जाने वाले प्रोग्रामिंग के दौरान REPL के उपयोग के प्रकार के बारे में बात कर रहा हूँ - जो कि अनजाने में आपके संदर्भ में है। जब तक आप नाम स्थान (शायद अपने संपादक को बंद करके) को पुनः लोड नहीं करते हैं, तब तक सब कुछ ठीक रहता है और बाद में आपके कोड में संदर्भित अनबाउंड प्रतीकों का एक गुच्छा खोजता है। यह भी अक्सर होता है जब आप रीफैक्टरिंग कर रहे होते हैं, एक var एक नामस्थान से दूसरे में जा रहे हैं।
लूप के लिए अनिवार्य की तरह सूची की समझ का इलाज करना
अनिवार्य रूप से आप एक नियंत्रित लूप का प्रदर्शन करने के बजाय मौजूदा सूचियों के आधार पर एक आलसी सूची बना रहे हैं। क्लोजर का डोज़क वास्तव में अनिवार्य फॉरेक्स लूपिंग कंस्ट्रक्शन के लिए अधिक अनुरूप है।
वे कैसे भिन्न होते हैं, इसका एक उदाहरण है कि वे उन तत्वों को फ़िल्टर करने की क्षमता रखते हैं, जिनका वे मनमाने ढंग से उपयोग करने के बारे में बताते हैं:
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)
एक और तरीका यह है कि वे अनंत आलसी दृश्यों पर काम कर सकते हैं:
user> (take 5 (for [x (iterate inc 0) :when (> (* x x) 3)] (* 2 x)))
(4 6 8 10 12)
वे एक से अधिक बाध्यकारी अभिव्यक्ति को संभाल सकते हैं, सबसे पहले सही अभिव्यक्ति पर पुनरावृत्ति करते हैं और अपने तरीके से काम करते हैं:
user> (for [x '(1 2 3) y '(\a \b \c)] (str x y))
("1a" "1b" "1c" "2a" "2b" "2c" "3a" "3b" "3c")
वहाँ भी नहीं है को तोड़ने या जारी रखने के लिए समय से पहले ही बाहर निकलने के लिए।
संरचनाओं का अति प्रयोग
मैं एक ओओपीश पृष्ठभूमि से आता हूं इसलिए जब मैंने क्लोजर शुरू किया तो मेरा दिमाग अभी भी वस्तुओं के संदर्भ में सोच रहा था। मैंने खुद को एक संरचना के रूप में सब कुछ मॉडलिंग किया क्योंकि इसके "सदस्यों" का समूहन, हालांकि ढीला था, जिससे मुझे सहज महसूस हुआ। वास्तव में, संरचनाओं को ज्यादातर एक अनुकूलन माना जाना चाहिए; क्लोजर मेमोरी को संरक्षित करने के लिए चाबियाँ और कुछ लुकअप जानकारी साझा करेगा। आप आगे उन्हें परिभाषित करने का अनुकूलन कर सकते accessors कुंजी देखने प्रक्रिया में तेजी लाने के लिए।
कुल मिलाकर आपको प्रदर्शन के अलावा किसी नक्शे पर एक संरचना का उपयोग करने से कुछ भी हासिल नहीं होता है , इसलिए अतिरिक्त जटिलता इसके लायक नहीं हो सकती है।
बिना सोचे समझे BigDecimal कंस्ट्रक्टर का उपयोग करना
मुझे बहुत से बिगडिमल्स की आवश्यकता थी और इस तरह से बदसूरत कोड लिख रहा था:
(let [foo (BigDecimal. "1") bar (BigDecimal. "42.42") baz (BigDecimal. "24.24")]
जब वास्तव में क्लोजर एम को संख्या में जोड़कर बिगडेसिमल शाब्दिक का समर्थन करता है :
(= (BigDecimal. "42.42") 42.42M) ; true
शक्करयुक्त संस्करण का उपयोग करने से ब्लोट का बहुत हिस्सा कट जाता है। टिप्पणी में, twils उल्लेख किया है कि आप भी उपयोग कर सकते हैं bigdec और bigint कार्यों अधिक स्पष्ट हो सकता है, अभी तक संक्षिप्त रहते हैं।
नाम पैकेज के लिए जावा पैकेज नामकरण रूपांतरणों का उपयोग करना
यह वास्तव में प्रति गलती नहीं है, बल्कि ऐसा कुछ है जो मुहावरेदार संरचना और एक विशिष्ट क्लोजर परियोजना के नामकरण के खिलाफ जाता है। मेरी पहली पर्याप्त क्लोजर परियोजना में नाम स्थान की घोषणाएँ थीं - और इसी तरह की फ़ोल्डर संरचनाएँ -
(ns com.14clouds.myapp.repository)
जो मेरे पूर्ण-योग्य फ़ंक्शन संदर्भों को फूला हुआ करता है:
(com.14clouds.myapp.repository/load-by-name "foo")
चीजों को और अधिक जटिल करने के लिए, मैंने एक मानक मावेन निर्देशिका संरचना का उपयोग किया :
|-- src/
| |-- main/
| | |-- java/
| | |-- clojure/
| | |-- resources/
| |-- test/
...
जो "मानक" क्लोजर संरचना की तुलना में अधिक जटिल है:
|-- src/
|-- test/
|-- resources/
जो कि खुद लेनिंगेन प्रोजेक्ट्स और क्लोजर का डिफॉल्ट है।
मैप्स कुंजी मिलान के लिए क्लोज़र के बजाय जावा के बराबर () का उपयोग करते हैं
मूल रूप से आईआरसी पर चॉसर द्वारा रिपोर्ट किया गया , यह जावा के बराबरी () के उपयोग से कुछ अनपेक्षित परिणाम हैं:
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
चूँकि इंटेगर और 1 के दोनों लंबे उदाहरणों को डिफ़ॉल्ट रूप से एक ही प्रिंट किया जाता है, इसलिए यह पता लगाना मुश्किल हो सकता है कि आपका मानचित्र किसी भी मान को क्यों नहीं लौटा रहा है। यह विशेष रूप से सच है जब आप एक फ़ंक्शन के माध्यम से अपनी कुंजी पास करते हैं, जो शायद आपके लिए अनजाने में एक लंबा रिटर्न देता है।
यह ध्यान दिया जाना चाहिए कि java.util.Map इंटरफेस के अनुरूप नक्शे के लिए जावा के बराबरी () के बजाय क्लोज़र के = का उपयोग करना आवश्यक है।
मैं स्टुअर्ट हल्लोवे, ल्यूक वेंडरहार्ट द्वारा प्रैक्टिकल क्लोजर द्वारा प्रोग्रामिंग क्लोजर का उपयोग कर रहा हूं , और आईआरसी पर अनगिनत क्लोजर हैकर्स और मेलिंग सूची की मदद से अपने जवाबों के साथ मदद करने के लिए।