Clojure vektorida takroriylarni almashtirish

Bo'sh iplar bilan bir vektorda ikki nusxani almashtirishga harakat qilaman. Ammo, men topadigan yagona vazifalar - ularning o'rnini bosa olmaydi, ularni echishdir. Qanday qilib olaman?

["Oktyabr 2016" "Oktyabr 2016" "Noyabr 2016" "Noyabr 2016" "Noyabr 2016" "Noyabr 2016"]

va chiqish:

["Oktyabr 2016" "" "Noyabr 2016" "" "" "" "]

Men topa oladigan har qanday narsa ["Oktyabr 2016" "Noyabr 2016"] qaytadan chiqadi, lekin ichki kodni doseq amalga oshirsangiz, lekin samarasiz ko'rinadi. Bunga erishishning yana bir yo'li bormi? Rahmat!

3

5 javoblar

Mana bir yechim uchun strategiya.

  1. loop over the items of the vector.
  2. Maintain a set of visited items. It can be used to check for uniqueness.
  3. For each item: if the set contains the current item then insert "" into the result vector.
  4. If the current item is unique then insert it into the result vector and also the set.
  5. Return the result vector when all items are visited.
  6. Optionally: Use a transient result vector for better performance.

Kod:

(defn duplicate->empty [xs]
  (loop [xs     (seq xs)
         result []
         found #{}]
        (if-let [[x & xs] (seq xs)]
          (if (contains? found x)
            (recur xs (conj result "") found)
            (recur xs (conj result x) (conj found x)))
          result)))

Uni qidirish:

(duplicate->empty ["Oct 2016" "Oct 2016" "Nov 2016" "Nov 2016" "Nov 2016" "Nov 2016"])
=> ["Oct 2016" "" "Nov 2016" "" "" ""]
5
qo'shib qo'ydi
Vektorni dastlabki yaratish paytida muammoni hal qilish tugallandi, biroq bu ishni hal qilib ko'rsatdi.
qo'shib qo'ydi muallif Zaden, manba

Transdüser versiyasi faqat to'liqlik uchun.

(defn empty-duplicates
  ([]
   (fn [rf]
     (let [seen (volatile! #{})]
       (fn
         ([] (rf))
         ([res] (rf res))
         ([res x]
          (if (contains? @seen x)
            (rf res "")
            (do (vswap! seen conj x)
                (rf res x))))))))
  ([coll]
   (sequence (empty-duplicates) coll)))

(comment

  (def months ["Oct 2016" "Oct 2016" "Nov 2016" "Nov 2016" "Nov 2016" "Nov 2016"])

  (into [] (empty-duplicates) months) ;=> ["Oct 2016" "" "Nov 2016" "" "" ""]

  )
2
qo'shib qo'ydi
(defn eliminate-duplicates [v]
        (let [result (transient (vec (repeat (count v) "")))
              index-of-first-occurences (apply merge-with #(first %&) (map-indexed (fn [x y] {y x}) v))]
            (doall (for [[s pos] index-of-first-occurences]
                       (assoc! result pos s)))
            (persistent! result)))
1
qo'shib qo'ydi

asosan yuqoridagi kabi, lekin dangasa ketma-ketlikni ishlatib:

(defn rdups
  ([items] (rdups #{} items))
  ([found [x & xs :as items]]
   (when (seq items)
     (if (contains? found x)
       (lazy-seq (cons "" (rdups found xs)))
       (lazy-seq (cons x (rdups (conj found x) xs)))))))

user> (rdups ["Oct 2016" "Oct 2016" "Nov 2016" "Nov 2016" "Nov 2016" "Nov 2016"])
;;=> ("Oct 2016" "" "Nov 2016" "" "" "")
1
qo'shib qo'ydi
lazy-seqni ishlatadi (xuddi qabul qilingan ans) PLUS loopni tushiradi. Menga eng toza yo'l ko'rinadi.
qo'shib qo'ydi muallif Phil Cooper, manba

iterate dan foydalanishingiz mumkin:

(def months ["Oct 2016" "Oct 2016" "Nov 2016" "Nov 2016" "Nov 2016" "Nov 2016"])

(defn step [[[head & tail] dups res]]
  [tail
   (conj dups head)
   (conj res (if (dups head)
               ""
               head))])

(defn empty-dups [xs]
  (->> (iterate step [xs #{} []])
       (drop-while (fn [[[head] _ _]] head))
       (map #(nth % 2))
       first))

(empty-dups months)
;; => ["Oct 2016" "" "Nov 2016" "" "" ""]
0
qo'shib qo'ydi