2013-08-15 3 views
10

में कार्टेशियन उत्पाद मैं एक विधि को लागू करने की कोशिश कर रहा हूं जो सूचियों की एक सूची लेगा और इन सूचियों के कार्टेशियन उत्पाद को वापस लाएगा।क्लोजर

यहाँ मैं अब तक है:

(defn cart 


([] '()) 
([l1] (map list l1)) 
([l1 l2] 
    (map 
    (fn f[x] (map 
    (fn g [y] (list x y)) 
    l2)) 
     l1) 
    ) 

) 

(defn cartesian-product [& lists] 
     (reduce cart lists) 

) 





;test cases 
(println (cartesian-product '(a b) '(c d))) ; ((a c) (a d) (b c) (b d)) 
(println (cartesian-product())) ;() 
(println (cartesian-product '(0 1))) ; ((0) (1)) 
(println (cartesian-product '(0 1) '(0 1))) ; ((0 0) (0 1) (1 0) (1 1)) 
(println (apply cartesian-product (take 4 (repeat (range 2))))) ;((0 0 0 0) (0 0 0 1) (0 0 1 0) (0 0 1 1) (0 1 0 0) (0 1 0 1) (0 1 1 0) (0 1 1 1) (1 0 0 0) (1 0 0 1) (1 0 1 0) (1 0 1 1) (1 1 0 0) (1 1 0 1) (1 1 1 0) (1 1 1 1)) 

समस्या मेरी समाधान वास्तव में 'brackety' है।

(((a c) (a d)) ((b c) (b d))) 
() 
(0 1) 
(((0 0) (0 1)) ((1 0) (1 1))) 
(((((((0 0) (0 1)) 0) (((0 0) (0 1)) 1)) 0) (((((0 0) (0 1)) 0) (((0 0) (0 1)) 1)) 1)) ((((((1 0) (1 1)) 0) (((1 0) (1 1)) 1)) 0) (((((1 0) (1 1)) 0) (((1 0) (1 1)) 1)) 1))) 

मैं

 (apply concat(reduce cart lists)) 

जोड़ने की कोशिश की लेकिन फिर मैं तो जैसे एक दुर्घटना मिलती है:

((a c) (a d) (b c) (b d)) 
() 
IllegalArgumentException Don't know how to create ISeq from: java.lang.Long clojure.lang.RT.seqFrom (RT.java:494) 

तो, मुझे लगता है कि मैं पास लेकिन लापता कुछ कर रहा हूँ। हालांकि, क्योंकि मैं क्लोजर और कार्यात्मक प्रोग्रामिंग के लिए इतना नया हूं, मैं पूरी तरह से गलत ट्रैक पर हो सकता हूं। कृपया सहायता कीजिए! :)

+2

clojure.math.combinatorics (https://github.com/clojure/math.combinatorics/) में एक तेज़ कार्टेशियन उत्पाद है, जो उपयोगी है यदि आप केवल परिणाम चाहते हैं ... –

+0

चीयर्स, यह व्यायाम का अधिक है हालांकि :) –

उत्तर

18

यह बहुत आसान है प्रत्यावर्तन मैन्युअल बाहर काम करने की कोशिश कर रहा द्वारा की तुलना में एक के लिए-समझ के रूप में करने के लिए:

(defn cart [colls] 
    (if (empty? colls) 
    '(()) 
    (for [x (first colls) 
      more (cart (rest colls))] 
     (cons x more)))) 

user> (cart '((a b c) (1 2 3) (black white))) 
((a 1 black) (a 1 white) (a 2 black) (a 2 white) (a 3 black) (a 3 white) 
(b 1 black) (b 1 white) (b 2 black) (b 2 white) (b 3 black) (b 3 white) 
(c 1 black) (c 1 white) (c 2 black) (c 2 white) (c 3 black) (c 3 white)) 

आधार मामला स्पष्ट है (यह खाली युक्त एक सूची होने की जरूरत है सूची, खाली सूची नहीं, क्योंकि कोई सूचियों का कार्टेशियन उत्पाद लेने का एक तरीका नहीं है)। रिकर्सिव केस में, आप पहले संग्रह के प्रत्येक तत्व x पर और फिर शेष सूचियों के प्रत्येक कार्टेशियन उत्पाद पर फिर से प्रयास करते हैं, जिसे आपने चुना है x

+1

यह मेरी समझ है कि एलआईएसपी/क्लोजर की "भावना में" नहीं है? क्या यह महत्वपूर्ण है? –

+2

आईएमओ 'के लिए' का उपयोग बिल्कुल मूर्खतापूर्ण है। – JohnJ

+1

@ सैमजर्मन आपत्ति क्या है? ध्यान दें कि '([x coll1, y coll2] (f x y) के लिए) '~' (मैपकैट (एफएन [एक्स] (मानचित्र (एफएन [वाई] (एफ एक्स वाई)) coll2)) coll1) '। आप _could_ का अनुवाद 'मानचित्र' और 'concat' का उपयोग करने के लिए करते हैं, लेकिन मुझे लगता है कि यह कम स्पष्ट होगा। –

6

तुलना के लिए, मूल

(defn cart 
    ([xs] 
    xs) 
    ([xs ys] 
    (mapcat (fn [x] (map (fn [y] (list x y)) ys)) xs)) 
    ([xs ys & more] 
    (mapcat (fn [x] (map (fn [z] (cons x z)) (apply cart (cons ys more)))) xs))) 

(cart '(a b c) '(d e f) '(g h i)) 
;=> ((a d g) (a d h) (a d i) (a e g) (a e h) (a e i) (a f g) (a f h) (a f i) 
; (b d g) (b d h) (b d i) (b e g) (b e h) (b e i) (b f g) (b f h) (b f i) 
; (c d g) (c d h) (c d i) (c e g) (c e h) (c e i) (c f g) (c f h) (c f i)) 
4

व्यक्तिगत रूप से की भावना में, मैं amalloy के for समाधान का प्रयोग करेंगे। अंगूठे का मेरा सामान्य नियम यह है कि यदि मेरे लूप को एक map/filter/आदि के रूप में व्यक्त किया जा सकता है, तो एक साधारण फ़ंक्शन तर्क (इसलिए फ़ंक्शन नाम या लघु fn/#() फ़ॉर्म) के साथ कॉल करें, यह फ़ंक्शन का उपयोग करने के लिए बेहतर है। जैसे ही यह उससे अधिक जटिल हो जाता है, for अभिव्यक्ति पढ़ने के लिए कहीं अधिक आसान है। विशेष रूप से, for नेस्टेड मानचित्रों से कहीं बेहतर है। जैसा कि कहा गया है, अगर मैं for यहाँ उपयोग नहीं किया, यह कैसे मैं समारोह लिखना होता है:

(defn cart 
    ([] '(())) 
    ([xs & more] 
    (mapcat #(map (partial cons %) 
        (apply cart more)) 
      xs))) 

बातें ध्यान रखें: सबसे पहले, को कम करने के लिए कोई ज़रूरत नहीं है। रिकर्सन इसे ठीक से संभाल सकता है।

दूसरा, केवल दो मामले। हम फ़ंक्शन को रिक्त सूची पर ठीक से कॉल कर सकते हैं, इसलिए हम जिसकी परवाह करते हैं वह रिक्त बनाम खाली नहीं है।

तीसरा, जैसा कि अमलाय ने समझाया, (cart) का सही मान '(()) है। यह वास्तव में बल्कि सूक्ष्म है, और जब मैं इस तरह के एक समारोह लिखता हूं तो मैं इसे विश्वसनीय रूप से गड़बड़ करता हूं। यदि आप एक साधारण मामले से बहुत सावधानी से चलते हैं, तो आप यह देखने में सक्षम होना चाहिए कि वह मूल्य रिकर्सन क्यों काम करता है।

चौथा, मैं आम तौर पर fn का उपयोग करना पसंद नहीं करता। यह एक व्यक्तिगत वरीयता है, लेकिन मैं हमेशा #(), partial, या comp का उपयोग करता हूं यदि मैं इससे दूर हो जाऊं। #() छोटे कार्यों के लिए निश्चित रूप से बेवकूफ है, हालांकि अन्य दो थोड़ा कम आम हैं।

पांचवां, कुछ स्टाइल नोट्स। सबसे बड़ा मुद्दा इंडेंटेशन है। यहां सबसे अच्छा सुझाव एक संपादक को ढूंढना है जो ऑटो इंडेंट्स लिस्प कोड है। ऑटो-इंडेंटेशन आपके संपादक के लिए सबसे महत्वपूर्ण चीजों में से एक है, क्योंकि यह आपके माता-पिता से मेल नहीं खाता है, क्योंकि इससे यह स्पष्ट रूप से स्पष्ट हो जाता है। इसके अलावा, बंद होने वाले माता-पिता कभी भी अपनी लाइन पर नहीं जाते, fn को आंतरिक नामों की आवश्यकता नहीं होती है जब तक आप रिकर्सिंग की योजना बना रहे हों, और मेरे पास आमतौर पर आपके कुछ और न्यूलाइन हैं। मुझे लगता है कि करने के लिए है कि ऊपर मेरी कोड यथोचित शालीनता से स्टाइल है पसंद है, और एक और उदाहरण के रूप में, यहाँ कैसे मैं अपने कोड प्रारूप होता है:

(defn cart 
    ([] '()) 
    ([l1] (map list l1)) 
    ([l1 l2] 
    (map (fn [x] 
      (map (fn [y] 
        (list x y)) 
       l2)) 
     l1))) 

(defn cartesian-product [& lists] 
    (reduce cart lists)) 
+0

दो से अधिक सूचियों के साथ काम नहीं करता है । – ClojureMostly

4

मुझे पता है कि मैं पार्टी के लिए देर हो चुकी हूं - मैं बस चाहता था पूर्णता के लिए, एक अलग दृष्टिकोण जोड़ने के लिए।

अमैलॉय के दृष्टिकोण की तुलना में, यह आलसी भी है (हालांकि पैरामीटर सूचियों का बेसब्री से मूल्यांकन किया जाता है) और थोड़ा तेज़ होता है जब सभी परिणामों की आवश्यकता होती है (मैंने उन्हें दोनों डेमो कोड के साथ परीक्षण किया), हालांकि यह स्टैक ओवरफ़्लो (अंतर्निहित for समझ की तरह यह उत्पन्न करता है और मूल्यांकन करता है) क्योंकि सूचियों की संख्या बढ़ जाती है। साथ ही, ध्यान रखें कि eval में कोड के आकार की सीमा है जिसे इसे पास किया जा सकता है।

समस्या के पहले एक उदाहरण पर विचार करें: आप [:a :b :c] और '(1 2 3) के कार्टेशियन उत्पाद को ढूंढना चाहते हैं।

(for [e1 [:a :b :c] 
     e2 '(1 2 3)] 
    (list e1 e2)) 

; ((:a 1) (:a 2) (:a 3) (:b 1) (:b 2) (:b 3) (:c 1) (:c 2) (:c 3)) 

अब, सवाल है:: यह एक तरीका है कि सूचियों का एक मनमाना संख्या के साथ काम करता में सामान्यीकरण करने के लिए यह संभव है स्पष्ट समाधान एक for समझ, इस तरह उपयोग करने के लिए है? यहां जवाब सकारात्मक है।

(defmacro cart [& lists] 
    (let [syms (for [_ lists] (gensym))] 
    `(for [[email protected](mapcat list syms lists)] 
     (list [email protected])))) 

(macroexpand-1 '(cart [:a :b :c] '(1 2 3))) 

; (clojure.core/for [G__4356 [:a :b :c] 
;     G__4357 (quote (1 2 3))] 
; (clojure.core/list G__4356 G__4357)) 

(cart [:a :b :c] '(1 2 3)) 

; ((:a 1) (:a 2) (:a 3) (:b 1) (:b 2) (:b 3) (:c 1) (:c 2) (:c 3)) 

अनिवार्य रूप से, आप संकलक आपके लिए उपयुक्त for समझ उत्पन्न होते हैं: यह वही है निम्न मैक्रो करता है। एक समारोह को यह परिवर्तित बिल्कुल स्पष्ट है, लेकिन वहाँ एक छोटी सी कमी है:

(defn cart [& lists] 
    (let [syms (for [_ lists] (gensym))] 
    (eval `(for [[email protected](mapcat #(list %1 `'~%2) syms lists)] 
      (list [email protected]))))) 

(cart [:a :b :c] '(1 2 3)) 

; ((:a 1) (:a 2) (:a 3) (:b 1) (:b 2) (:b 3) (:c 1) (:c 2) (:c 3)) 

सूचियाँ कि गैर उद्धृत छोड़ दिया जाता है फ़ंक्शन कॉल, जिसके कारण हवाले से %2 यहाँ आवश्यक है माना जाता है।

Online Demo:

; https://projecteuler.net/problem=205 

(defn cart [& lists] 
    (let [syms (for [_ lists] (gensym))] 
    (eval `(for [[email protected](mapcat #(list %1 `'~%2) syms lists)] 
      (list [email protected]))))) 

(defn project-euler-205 [] 

    (let [rolls (fn [n d] 
       (->> (range 1 (inc d)) 
        (repeat n) 
        (apply cart) 
        (map #(apply + %)) 
        frequencies)) 

     peter-rolls (rolls 9 4) 
     colin-rolls (rolls 6 6) 

     all-results (* (apply + (vals peter-rolls)) 
         (apply + (vals colin-rolls))) 

     peter-wins (apply + (for [[pk pv] peter-rolls 
            [ck cv] colin-rolls 
            :when (> pk ck)] 
           (* pv cv)))] 

    (/ peter-wins all-results))) 

(println (project-euler-205)) ; 48679795/84934656 
0

सबसे प्रयोजनों के लिए अनिल का जवाब के रूप में आप (एक आलसी समझ पाने के लिए, और एक आलसी seq एक ढेर अतिप्रवाह कारण नहीं के रूप में आप अपने सदस्यों का एहसास, भले ही आप का उपयोग नहीं करते महान है पुनरावृत्ति होना)।

मैं स्पष्ट पुनरावृत्ति होना साथ पूंछ पुनरावर्ती संस्करण शिल्प की कोशिश कर में रुचि थी, कम से कम नहीं है जो की वजह से आलस्य अपने आवेदन में किसी भी मदद की हो, लेकिन यह भी मनोरंजन के लिए नहीं जा रहा था और गिगल्स:

(defn cartesian-product 
    ([cols] (cartesian-product '([]) cols)) 
    ([samples cols] 
    (if (empty? cols) 
     samples 
     (recur (mapcat #(for [item (first cols)] 
         (conj % item)) samples) 
      (rest cols)))))