Hackerrank: रिकॉर्ड तोड़ना
मैं क्लोजर सीख रहा हूं और इस पर एक रैंक n00b हूं, किताबों और ऑनलाइन ट्यूटोरियल से सीखने की कोशिश कर रहा हूं (लेकिन मुझे कभी-कभी चिंता होती है कि मैं बुरी आदतों को उठा रहा हूं या कम से कम सभी अच्छी आदतों को नहीं)। एक्सरसाइज के लिए मैंने हैकर्रानक पर ब्रेकिंग द रिकॉर्ड की समस्या की।
TL; DR समस्या का वर्णन:
अंकों की सूची (ऐतिहासिक क्रम में) के लिए, पिछले सर्वश्रेष्ठ स्कोर की संख्या को पार करने के साथ-साथ पिछले सबसे खराब स्कोर को भी रेखांकित किया गया था।
एक पुनरावृत्त भाषा में सूची के माध्यम से इसे पुनरावृत्त करना काफी आसान है ; लेकिन क्लोजर पुनरावृत्ति नहीं करता है और मैंने इस समस्या से निपटने के लिए (टेल एंड) पुनरावर्ती समाधान का निर्माण करने का निर्णय लिया है। अपने लिए आसान बनाने के लिए, मैंने सबसे पहले जावा में एक पुनरावर्ती समाधान किया, जिसका मैंने फिर अनुवाद किया। सभी में, यह रॉकेट सर्जरी के बिना काफी सरल पुनरावर्ती कार्य शामिल है।
जाहिर है मेरा कोड काम करता है, जैसा कि शामिल इकाई परीक्षणों द्वारा दिखाया गया है। मेरी चिंताएँ इस प्रकार हैं:
- जब जावा कोड के बगल में रखा जाता है, तो दोनों काफी समान दिखते हैं। क्या मैंने "मुहावरेदार" क्लोजर प्रोग्रामिंग का पालन किया है, या यह सिर्फ एक अनाड़ी "शब्द-के लिए शब्द लिप्यंतरण" है?
- क्या ऐसे कोई क्षेत्र हैं जो अलग-अलग क्लोजर निर्माण का उपयोग करके अधिक कॉम्पैक्ट और / या समझने में आसान हो सकते हैं?
आपके महत्वपूर्ण इनपुट की बहुत सराहना की जाएगी - जिसमें यूनिट परीक्षण के बारे में भी शामिल है , क्योंकि यह यकीनन प्रोग्रामिंग का एक महत्वपूर्ण हिस्सा है, जिसे मैं समानांतर में सीखना और अभ्यास करना चाहता हूं।
कोड:
(ns hackerrank.breaking-records
(:require [clojure.test :refer :all]))
(defrecord Record [min max countworse countbetter])
(defn recalc-record [rec newscore]
(Record.
(min newscore (:min rec))
(max newscore (:max rec))
(+ (:countworse rec) (if (> (:min rec) newscore) 1 0))
(+ (:countbetter rec) (if (< (:max rec) newscore) 1 0))))
(defn accumulate [curr-record remaining-scores]
(if (nil? (second remaining-scores))
curr-record
(recur (recalc-record curr-record (second remaining-scores)) (rest remaining-scores)))
)
(defn breaking-records [scores]
(let [result (accumulate (Record. (first scores) (first scores) 0 0) scores)]
(list (:countbetter result) (:countworse result))))
(deftest test-records
(testing "edge cases"
(is (= '(0 0) (breaking-records '())) "no games played yet")
(is (= '(0 0) (breaking-records '(5))) "single game"))
(testing "hackerrank examples"
(is (= '(2 4) (breaking-records '(10 5 20 20 4 5 2 25 1))))
(is (= '(4 0) (breaking-records '(3 4 21 36 10 28 35 5 24 42)))))
)
जवाब
मैंने और अधिक विशिष्ट क्लोजर सुविधाओं का उपयोग करने के लिए आपके समाधान को फिर से लिखा। जब आप डेटा पर लूप कर रहे हैं और संचित स्थिति का ट्रैक रखने की आवश्यकता है, तो इसे हरा पाना मुश्किल है loop/recur। पहला उदाहरण:
(ns tst.demo.core
(:use clojure.test))
(defn breaking-records
[scores]
; this loop has 5 variables. Init all of them
(loop [low (first scores)
high (first scores)
nworse 0
nbetter 0
score-pairs (partition 2 1 scores)]
(if (empty? score-pairs)
{:nbetter nbetter :nworse nworse}
(let [curr-score-pair (first score-pairs)
new-score (second curr-score-pair)]
; start the next iteration with modified versions of the 5 loop vars
(recur
(min new-score low)
(max new-score high)
(if (< new-score low)
(inc nworse)
nworse)
(if (< high new-score)
(inc nbetter)
nbetter)
(rest score-pairs))))))
और यूनिट परीक्षण:
(deftest test-records
(testing "edge cases"
(is (= (breaking-records []) {:nbetter 0 :nworse 0}) "no games played yet")
(is (= (breaking-records [5]) {:nbetter 0 :nworse 0}) "single game"))
(testing "hackerrank examples"
(is (= (breaking-records [10 5 20 20 4 5 2 25 1]) {:nbetter 2 :nworse 4}))
(is (= (breaking-records [3 4 21 36 10 28 35 5 24 42]) {:nbetter 4 :nworse 0}))))
; ***** NOTE: it's much easier to use vectors like [1 2 3] instead of a quoted list `(1 2 3)
कृपया इस दस्तावेज की सूची देखें , esp। क्लोजर चीटशीट। इसके अलावा, एक पूरे के रूप में टेम्पलेट परियोजना से पता चलता है कि मुझे चीजों को कैसे करना पसंद है। :)
फ़ंक्शन जो सबसे अधिक मदद करता है partition। डॉक्स देखें ।
थोड़ा सा परावर्तन
आप इसे एक छोटी राशि को सरल बना सकते हैं और अधिक विशिष्ट कार्यों जैसे reduceऔर का उपयोग करके इसे थोड़ा अधिक कॉम्पैक्ट बना सकते हैं cond->। यह संस्करण स्टेट को होल्ड reduceकरने और लूपिंग करने के लिए मैप का उपयोग करता है :
(defn breaking-records
[scores]
(let [state-init {:low (first scores)
:high (first scores)
:nworse 0
:nbetter 0}
accum-stats-fn (fn [state score-pair]
; Use map destructuring to pull out the 4 state variables
(let [{:keys [low high nworse nbetter]} state
new-score (second score-pair)
state-new {:low (min new-score low)
:high (max new-score high)
:nworse (cond-> nworse
(< new-score low) (inc))
:nbetter (cond-> nbetter
(< high new-score) (inc))}]
state-new))
state-final (reduce accum-stats-fn
state-init
(partition 2 1 scores))
result (select-keys state-final [:nworse :nbetter])]
result))