Clojure: Vektorli elementlarni xaritada oqlangan holda qanday harakat qilish kerak

Kelajakda men quyidagi mantiqni bajarishga harakat qilaman:

Input:
{:a [11 22 33] :b [10 20 30]}, 2
Output:
{:a [11] :b [10 20 30 22 33]}

ya'ni oxirgi 2 ta elementni: a dan: b ga olib keling

Bu operatsiya uchun jozibali usul bormi?

0
Kelajak uchun faqat bir maslahat: Sizning savolingizda muammoni hal qilish uchun eng yaxshi tashabbusingizni yuborishingiz va keyin sizning tashabbusingizdan mamnun emasligingizni nazarda tutgan qo'shimcha yordam so'rashingiz kerak.
qo'shib qo'ydi muallif Sam Estep, manba
"2" nimani bildiradi? Misol tariqasida berilgan transformation bir nechta sharhlar bo'lishi mumkin, shuning uchun qo'shimcha misol bilan ta'riflash yoki bir nechta so'zlardan foydalanish yaxshiroq bo'lishi mumkin. Misol uchun, : ga o'tishni xohlagan : a dan olingan so'nggi 2 element bormi yoki barchasi birinchilardanmi? Va faqatgina : a va : b yoki faqat ikkitasi bormi bo'ladi?
qo'shib qo'ydi muallif Josh, manba
@Josh Afsuslanish uchun uzr so'rayman. "2" nima degani.
qo'shib qo'ydi muallif mye, manba

6 javoblar

Xaritada ikkita xaritani samarali ravishda o'zgartirganingiz uchun xaritani aniq ravishda dekonstruksiya qilish va yangi xaritani to'g'ridan to'g'ri "https://clojuredocs.org/clojure.core/subvec" dan foydalanib, rel = "nofollow noreferrer"> subvec va vektor manipulyatsiyasi uchun:

(defn move [m n]
  (let [{:keys [a b]} m
        i (- (count a) n)
        left (subvec a 0 i)
        right (subvec a i)]
    {:a left :b (into b right)}))

(move {:a [11 22 33] :b [10 20 30]} 2)
;;=> {:a [11], :b [10 20 30 22 33]}

Bonus sifatida, ushbu dastur juda idrakli va juda tez.

Shu bilan bir qatorda, bu yerda split-at ' funktsiyasidan foydalangan holda quyidagicha yozishingiz mumkin:

(defn split-at' [n v]
  [(subvec v 0 n) (subvec v n)])

(defn move [m n]
  (let [{:keys [a b]} m
        [left right] (split-at' (- (count a) n) a)]
    {:a left :b (into b right)}))
2
qo'shib qo'ydi
@ChrisMurphy Bu subvec dan sekinroq bo'lishi kerak. Vektor uchun bu erda split-at ' funktsiyasini aniqlashga to'g'ri kelishi mumkin.
qo'shib qo'ydi muallif Sam Estep, manba
@ChrisMurphy Men bu split-at ' funktsiyasidan foydalangan holda muqobil dasturni qo'shdim.
qo'shib qo'ydi muallif Sam Estep, manba
Men uni ko'ra olaman, ha. map va mapv bilan ishlaydigan yagona narsa, chunki map faqat bir so'z (tire) shuning uchun u split-atv (birovning "atv" ning biror narsani anglatishi kerak deb o'ylashi mumkin) bilan bir oz qayg'uruvchi ko'rinadi.
qo'shib qo'ydi muallif Sam Estep, manba
Bo'lish uchun yaxshi javob - keyin ikkinchi qismni birinchi oxirigacha ko'chirish. Buning uchun Clojure funksiyasi mavjud deb hisoblaydi: clojuredocs.org/clojure.core/split-at
qo'shib qo'ydi muallif Chris Murphy, manba
mapv => mapv ga o'xshash split-atv funktsiyasini chaqirishingizni tavsiya qilamiz. .
qo'shib qo'ydi muallif Thumbnail, manba
@SamEstep rost.
qo'shib qo'ydi muallif Thumbnail, manba

Birinchidan, boshqa javoblardagi pastki satrlarni ishlatib, indekslarOutOfBoundsExceptionni ko'chirish kerak bo'lgan elementlarning soni yig'ish hajmidan kattaroq bo'ladi.

Ikkinchidan, buzib tashlash, bu erda eng ko'p bajarilgan usul, funktsiyani ma'lum bir ma'lumot strukturasiga aylantiradi. Bu, tugmachasi bilan xarita: a va: b va vektorlar bo'lgan ushbu tugmalar uchun qadriyatlar. Endi kiritishda kalitlardan birini o'zgartirsangiz, uni ko'chirish funktsiyasida o'zgartirishingiz kerak.

Mening qarorim quyidagicha:

(defn move [colla collb n]
  (let [newb (into (into [] collb) (take-last n colla))
        newa (into [] (drop-last n colla))]
       [newa newb]))

Bu har qanday to'plam uchun ishlashi kerak va 2 vektorning vektorini qaytaradi. Mening yechimim yana foydalanish mumkin. Sinash:

(harakat (100000 oralig'i) (masofa 200000) 10000)

Tahrirlash:

Endi sizga kerakli vektorga kirish uchun birinchi va ikkinchi foydalanishingiz mumkin.

2
qo'shib qo'ydi

Buni Joshdan farqli o'laroq qilardim:

(defn tx-vals [ {:keys [a b]} num-to-move ]
  {:a (drop-last num-to-move a)
   :b (concat b (take-last num-to-move a)) } )

(tx-vals {:a [11 22 33], :b [10 20 30]} 2) 
      => {:a (11),       :b (10 20 30 22 33)}

Yangilash

Ba'zan clojure.core/split-at funksiyasini quyidagi kabi ishlatish qulayroq bo'lishi mumkin:

(defn tx-vals-2 [ {:keys [a b]} num-to-move ]
  (let [ num-to-keep        (- (count a) num-to-move)
        [a-head, a-tail]   (split-at num-to-keep a) ]
    { :a a-head
     :b (concat b a-tail) } ))

Vektorlarni chiqishda afzal ko'rsangiz (mening sevimli!), Faqat quyidagilarni bajaring:

(defn tx-vals-3 [ {:keys [a b]} num-to-move ]
  (let [ num-to-keep        (- (count a) num-to-move)
         [a-head, a-tail]   (split-at num-to-keep a) ]
    {:a (vec a-head)
     :b (vec (concat b a-tail))} ))

natijalarni olish uchun:

(tx-vals-2 data 2) => {:a (11), :b (10 20 30 22 33)}
(tx-vals-3 data 2) => {:a [11], :b [10 20 30 22 33]}
1
qo'shib qo'ydi
Ushbu yondashuv bilan bog'liq muammo shundaki, uning asimptotik murakkabligi O ( (a a) )) mavjud bo'lsa, u holda menda ( num-to-move ), asimptotik emas), faqat doimiy omil emas, farq. Va bu erda muntazam vaqtni ta'minlash kabi dangallikka ishora qilolmaysiz, chunki OP ayniqsa vektorlar uchun so'radi va natija xaritasida vektorlarni qaytarib olish uchun ketma-ketlikni majbur qilish kerak.
qo'shib qo'ydi muallif Sam Estep, manba
Yangilashga javoban: avval aytganimdek, ( vec ga qo'ng'iroq qilib) ketma-ketliklarni majburlash sizga ikkala dunyodagi eng yomonni keltiradi: bu erda qo'llanilgan yondashuvning dangalligidan foydalanmaydi va siz javobimdagi yondashuvning asimptotik ishiga ega bo'lmang.
qo'shib qo'ydi muallif Sam Estep, manba
(defn f [{:keys [a b]} n]
  (let [last-n (take-last n a)]
    {:a (into [] (take (- (count a) n) a))
     :b (into b last-n)}))

(f {:a [11 22 33] :b [10 20 30]} 2)
=> {:a [11], :b [10 20 30 22 33]}
0
qo'shib qo'ydi

Agar ushbu narsalarning tartibi ahamiyatga ega bo'lmasa, bu erda mening harakatlarim:

(def m {:a [11 22 33] :b [10 20 30]})

(defn so-42476918 [{:keys [a b]} n]
  (zipmap [:a :b] (map vec (split-at (- (count a) n) (concat a b)))))

(so-42476918 m 2)

beradi:

{:a [11], :b [22 33 10 20 30]}
0
qo'shib qo'ydi

oldingi javoblardan bir oz boshqacha (ehtimol, texnik jihatdan bir xil bo'lsa-da, bu dastur miqyosida farq qiladi) yondashuv bilan boraman.

Avvalo, ikkita to'plam orasidagi ma'lumotlarni uzatish juda tez-tez bajariladigan vazifadir, shuning uchun kutubxonangizda buning uchun maxsus yordam funktsiyasiga ega bo'lish kerak:

(defn transfer [from to n & {:keys [get-from put-to]
                             :or {:get-from :start :put-to :end}}]
  (let [f (if (= get-from :end)
            (partial split-at (- (count from) n))
            (comp reverse (partial split-at n)))
        [from swap] (f from)]
    [from (if (= put-to :start)
            (concat swap to)
            (concat to swap))]))

OK, bu aniq ko'rinadi, lekin bu ma'lumotlar to'plamning boshidan/oxiridan boshqasini boshlash/oxiriga o'tkazish imkonini beradi:

user> (transfer [1 2 3] [4 5 6] 2)
[(3) (4 5 6 1 2)]

user> (transfer [1 2 3] [4 5 6] 2 :get-from :end)
[(1) (4 5 6 2 3)]

user> (transfer [1 2 3] [4 5 6] 2 :put-to :start)
[(3) (1 2 4 5 6)]

user> (transfer [1 2 3] [4 5 6] 2 :get-from :end :put-to :start)
[(1) (2 3 4 5 6)]

Shunday qilib, qolgan narsalar sizning domeningizning o'ziga xos funktsiyasini yaratishdir:

(defn move [data n]
  (let [[from to] (transfer (:a data) (:b data) n
                            :get-from :end
                            :put-to :end)]
    (assoc data
           :a (vec from)
           :b (vec to))))

user> (move {:a [1 2 3 4 5] :b [10 20 30 40] :c [:x :y]} 3)
{:a [1 2], :b [10 20 30 40 3 4 5], :c [:x :y]}
0
qo'shib qo'ydi