كيف أتجنب سلوك Clojure لتجميع seqs الكسول التي أريد أن أقصر دائرة الدائرة؟

StackOverflow https://stackoverflow.com/questions/3407876

سؤال

لدي تسلسل طويل كسول أريد تقليله واختباره بتكاسل. بمجرد أن لا يكون هناك عنصرين متسلسلين = (أو بعض المسند الآخر) لبعضها البعض ، أريد التوقف عن استهلاك القائمة ، وهو مكلف لإنتاجه. نعم ، هذا يبدو مثل take-while, ، لكن اقرأ المزيد.

أردت أن أكتب شيئًا بسيطًا وأنيقًا مثل هذا (التظاهر لمدة دقيقة every? يعمل مثل reduce):

(every? = (range 100000000))

لكن هذا لا يعمل بتكاسل ، وبالتالي فهو معلق على seqs لانهائي. اكتشفت أن هذا يعمل تقريبًا كما أردت:

(apply = (range 100000000))

ومع ذلك ، لاحظت أن التسلسل كان يؤدي إلى إنشاء عناصر إضافية غير ضرورية واختبارها. على الأقل ، هذا ما أعتقد أنه ما يحدث في الجزء التالي من الكود:

;; Displays chunking behavior in groups of four on my system and prints 1 2 3 4
(apply = (map #(do (println %) %) (iterate inc 1)))

;; This prints 0 to 31
(apply = (map #(do (println %) %) (range)))

لقد وجدت حل بديل take-while, ، و count للتحقق من عدد العناصر المأخوذة ، ولكن هذا أمر مرهق إلى حد ما.

هل يجب أن أقترح بأدب أن يكون لهيكي أنه يصنع مزيجًا من مزيج من reduce و every? دائرة قصيرة بشكل صحيح ، أم أنني أفتقد طريقة واضحة موجودة بالفعل؟

تعديل: نشر شخصان لطيفان حلولاً لتجنب التسلسل على التسلسلات البطيئة ، لكن كيف أتجنب التضخيم عند القيام apply, ، والتي يبدو أنها تستهلك في مجموعات مكثف من أربعة؟

تحرير #2: كما يلاحظ ستيوارت سييرا واكتشفت بشكل مستقل ، فإن هذا ليس في الواقع. إنه ينطبق فقط على التمثيل بشكل طبيعي ، لذلك سأسمي هذا المغلق وأعطيه الإجابة. قمت بتضمين وظيفة صغيرة في إجابة منفصلة للقيام بتقليل الجزء من المشكلة ، لأولئك المهتمين.

هل كانت مفيدة؟

المحلول

تم تصحيحه مرتين: طريقة أبسط لإلغاء توصيل تسلسل كسول:

(defn unchunk [s]
  (when (seq s)
    (lazy-seq
      (cons (first s)
            (unchunk (next s))))))

الإصدار الأول تم حذفه (when ... لذلك أعادت seq لا حصر لها من NIL's بعد انتهاء تسلسل الإدخال.

النسخة الثانية المستخدمة first بدلاً من seq لذلك توقفت على لا شيء.

إعادة: سؤالك الآخر ، "كيف أتجنب التقطيع عند إجراء التطبيق ، والذي يبدو أنه يستهلك مجموعات مكثفة من أربعة":

هذا بسبب تعريف =, ، والتي ، عند إعطاء سلسلة من الحجج ، يفرض أول 4:

(defn =
  ;; ... other arities ...
  ([x y & more]
   (if (= x y)
     (if (next more)
       (recur y (first more) (next more))
       (= y (first more)))
     false)))

نصائح أخرى

إن النظر إلى clojure.core في تعريف التطبيق يجعل من الواضح لماذا كان يتكدس في مجموعات من أربعة متى apply يستخدم مع تسلسل لانهائي. Reduce لا تقصر دائرة ، إما ... لذلك تركت أكتب الحل الخاص بي:

(defn reducep
  "Like reduce, but for use with a predicate. Short-circuits on first false."
  ([p coll]
     (if-let [s (seq coll)]
       (reducep p (first s) (next s))
       (p)))
  ([p val coll]
     (if-let [s (seq coll)]
       (if-let [v (p val (first s))]
         (recur p (first s) (next s))
         false)
       true)))

ثم باستخدام Stuart's Unchunk (مع إضافية and)

(defn unchunk [s]
  (lazy-seq
   (cons (first s)
         (and (next s)
              (unchunk (next s))))))

انا حصلت:

(reducep = (map #(do (print %) %) (unchunk (range)))) ;; Prints 01, returns false
(reducep = (map #(do (print %) %) (repeat 20 1))) ;; returns true
(reducep = (map #(do (print %) %) (unchunk [0 0 2 4 5]))) ;; Returns false
(reducep = (map #(do (print %) %) (unchunk [2 2 2 2 2]))) ;; returns true

إذا كان هذا يناسبك أيضًا ، فستضيف هذا.

تعديل: من المحتمل أن يكون إصدار Stuart المعدل من Unchunk بعد تحريره هو الأفضل من الإصدار في هذا المنشور السابق.

لقد وجدت هذا المنشور أثناء الوصول إلى حد زمني لمشكلة 4Clojure ووجدت طريقة أخرى لتجنب 32-Chunks:

;; add another dummy sequence parameter to the map:
(apply = (map #(do (prn %2) %) (range) (range)))

لا يبدو أن أشكال الخريطة العليا من الخريطة تستخدم تسلسلات مكثفات (Clojure 1.5)

عليك أن تفعل شيئًا بالمعلمة الثانية ، لذلك قد يكون الأمر واضحًا في ذلك: قد يكون ذلك أفضل:

(apply = (map (fn [i _] (prn i) i) (range) (range)))

هذا ليس أنيقًا مثل الحلول الأخرى ولكنه قد يكون جيدًا للاستخدامات السريعة والقذرة ، مثل الاختبار "هل هذا بطيء بسبب الضبط؟".

متعلق apply, ، يمكنك استخدام partition للحصول على أزواج من التسلسل و = لهم:

(every? #(apply = %) (partition 2 1
    (map (fn [i _] (prn i) i) (range) (range))))

برغم من reducep يبدو مفيدًا أيضًا.

ملاحظة. لا أريد أن أعطي انطباعًا بأن التسلسل المكثف أبطأ ، إنه ليس كذلك. مشكلتي هي أن حالة اختبار 4Clojure تستدعي "أولاً" على وظيفة توليد SEQ الخاصة بي على مجموعة من القيم ، لذا فإن التقطيع يعني أنني أفعل 32 مرة من العمل. (PPS. لا يزال رمزتي بطيئًا جدًا)

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top