2017-09-21 19 views
7

Jako początkujący Clojure często mam trudności z wyrażeniem najprostszych rzeczy. Na przykład, dla zastąpienia ostatni element w wektorze, który byłbyJak zamienić ostatni element w wektor w Clojure

v[-1]=new_value 

w Pythonie, I skończyć z następujących wariantów w Clojure:

(assoc v (dec (count v)) new_value) 

który jest dość długi i bez wyrazu powiedzieć najmniej, lub jeszcze gorzej, ponieważ ma on czas działania w zakresie

To sprawia, że ​​czuję się głupio, jak jaskiniowiec, który próbuje naprawić szwajcarski zegarek z pałką.

Jaki jest właściwy sposób Clojure do zastąpienia ostatniego elementu w wektorze?


Aby wspierać moje O(n) -claim dla butlast -version (Clojure 1.8):

(def v (vec (range 1e6))) 
#'user/v 
user=> (time (first (conj (vec (butlast v)) 55))) 
"Elapsed time: 232.686159 msecs" 
0 
(def v (vec (range 1e7))) 
#'user/v 
user=> (time (first (conj (vec (butlast v)) 55))) 
"Elapsed time: 2423.828127 msecs" 
0 

Więc w zasadzie do 10 czasu liczba elementów jest 10 razy wolniejsze.

+1

Twoim pierwszym sposobem będzie to, jak to się robi. Oczywiście, możesz napisać funkcję "zastąpić ostatnią", aby ją wyczyścić. Myślę, że zbyt zwięzły sposób wyrażania Pythona zepsuł twoje oczekiwania. Nie sądzę, że indeksowanie z tyłu jest często potrzebne, aby wypowiadać własną składnię w Clojure. +1, ponieważ chciałbym zostać udowodniony źle. – Carcigenicate

+0

Uwaga: rozwiązanie nr 3 nie jest O (n), jeśli 'v' jest już wektorem (zalecam, aby zawsze używać wektora Clojure na liście Clojure jako domyślnego wyboru, o ile pomiar nie wskaże inaczej). –

+1

To jest dobrze rozgraniczone, konkretne pytanie i odpowiedź, ale myślę, że warto podkreślić, że wiele algorytmów wyrażonych za pomocą wyrażeń indeksu w Pythonie ma idiomatyczny ekwiwalent Clojure, który nie wymaga używania indeksów. – glts

Odpowiedz

9

użyję

(defn set-top [coll x] 
    (conj (pop coll) x)) 

Na przykład

(set-top [1 2 3] :a) 
=> [1 2 :a] 

Ale to działa również na przodu list:

(set-top '(1 2 3) :a) 
=> (:a 2 3) 

funkcji stos Clojure - peek , pop i conj - praca nad naturalnym otwartym końcem kolekcji sekwencyjnej.

Ale nie ma jednej właściwej drogi.


Jak różne rozwiązania reagują na pusty wektor?

  • Twój Python v[-1]=new_value zgłasza wyjątek, podobnie jak swój (assoc v (dec (count v)) new_value) i moją (defn set-top [coll x] (conj (pop coll) x)).
  • Twoja (conj (vec (butlast v)) new_value) zwraca.... butlast nie ma żadnego efektu.
1

Jeśli nalegasz na bycie "czystym", twoje 2. lub 3. rozwiązanie będzie działać. Wolę być prostsze & bardziej wyraźne używając funkcji pomocniczych from the Tupelo library:

(s/defn replace-at :- ts/List 
    "Replaces an element in a collection at the specified index." 
    [coll  :- ts/List 
    index :- s/Int 
    elem  :- s/Any] 
    ...) 

(is (= [9 1 2] (replace-at (range 3) 0 9))) 
(is (= [0 9 2] (replace-at (range 3) 1 9))) 
(is (= [0 1 9] (replace-at (range 3) 2 9))) 
As with drop-at, replace-at will throw an exception for invalid values of index. 

Podobne funkcje pomocnicze istnieją dla

  • insert-u
  • drop-at
  • prepend
  • dołączyć

Należy zauważyć, że wszystkie powyższe zasady działają równie dobrze na liście Clojure (chętnych lub leniwych), jak i na wektorze Clojure. The conj solution will fail, chyba że jesteś ostrożny, aby zawsze przymuszać wejście do wektora, tak jak w twoim przykładzie.

+3

i jaki jest zysk w porównaniu z 'assoc'? – ead