2011-09-19 11 views
17

Czy jest lepszy sposób na zrobienie tego w Clojure?Odwróć ciąg znaków (proste pytanie)

daniel=> (reverse "Hello") 
(\o \l \l \e \H) 

daniel=> (apply str (vec (reverse "Hello"))) 
"olleH" 

Czy masz zrobić to apply $ str $ vec nieco za każdym razem chcesz odwrócić ciąg z powrotem do swojej pierwotnej postaci?

Odpowiedz

24

Lepiej używać clojure.string/reverse:

user=> (require '[clojure.string :as s]) 
nil 
user=> (s/reverse "Hello") 
"olleH" 

UPDATE: Dla ciekawskich tutaj wykonać fragmenty kodu źródłowego clojure.string/reverse zarówno w Clojure (v1.4) oraz ClojureScript

; clojure: 
(defn ^String reverse 
    "Returns s with its characters reversed." 
    {:added "1.2"} 
    [^CharSequence s] 
    (.toString (.reverse (StringBuilder. s)))) 

; clojurescript 
(defn reverse 
    "Returns s with its characters reversed." 
    [s] 
    (.. s (split "") (reverse) (join ""))) 
+0

Ach ma sens, dzięki. Nadal jestem zbyt mocno zakorzeniony w uniwersum Haskella, w którym łańcuchy są po prostu połączonymi listami. – djhworld

10

Zastosuj, podobnie jak w odwrotnej kolejności, działa na każdym seqable type, nie tylko wektorach, więc

(zastosuj str (reverse "Hello"))

jest nieco krótszy. clojure.string/reverse powinny być bardziej wydajne.

18

OK, więc łatwo byłoby przetasować własną funkcję z zastosowaniem wewnątrz lub użyć dedykowanej wersji odwrotnej, która działa lepiej (ale tylko) na łańcuchach. Głównymi rzeczami, o których warto tutaj pomyśleć, jest arność (ilość i typ parametrów) funkcji str, oraz fakt, że odwrotność działa na kolekcję.

Oznacza to, że reverse działa nie tylko na ciągi, ale także na wszystkie inne kolekcje. Jednak z powodu odwrotnego oczekuje zbiór jako parametr, traktuje ciąg jako zbiór znaków

(reverse "Hello") 

i zwraca jedną jak również

(\o \l \l \e \H) 

Teraz, jeśli po prostu zastąpić funkcje gromadzenia, ty można dostrzec różnicę:

(str '(\o \l \l \e \H)) 
"(\\o \\l \\l \\e \\H)" 

podczas

(str \o \l \l \e \H) 
"olleH" 

Duża różnica między nimi to ilość parametrów. W pierwszym przykładzie str przyjmuje jeden parametr, zbiór 5 znaków. W drugim kroku str przyjmuje 5 parametrów: 5 znaków.

Czego oczekuje funkcja str?

(doc str) 
------------------------- 
clojure.core/str 
([] [x] [x & ys]) 
    With no args, returns the empty string. With one arg x, returns 
    x.toString(). (str nil) returns the empty string. With more than 
    one arg, returns the concatenation of the str values of the args. 

Tak więc, gdy podasz jeden parametr (zbiór), wszystkie powroty str jest toString kolekcji. Ale aby uzyskać pożądany wynik, musisz podać 5 znaków jako oddzielne parametry do str, zamiast samej kolekcji. Zastosuj to funkcja, która służy do "dostania się" do kolekcji i sprawi, że to się stanie.

(apply str '(\o \l \l \e \H)) 
"olleH" 

Funkcje obsługi wielu oddzielnych parametrów są często postrzegane w Clojure, więc dobrze jest uświadomić sobie, kiedy i dlaczego trzeba użyć zastosowania. Inną stroną do zdawania sobie sprawy jest to, dlaczego pisarz funkcji str sprawił, że akceptuje wiele parametrów zamiast kolekcji?Zwykle istnieje całkiem niezły powód. Jaki jest powszechny przypadek użycia funkcji str? Niepodzielenie na pewno zbioru oddzielnych znaków, ale łączenie wartości, łańcuchów i wyników funkcji.

(let [a 1 b 2] 
    (str a "+" b "=" (+ a b))) 
"1+2=3" 

Co gdybyśmy mieli str, który zaakceptowałby pojedynczą kolekcję jako parametr?

(defn str2 
    [seq] 
    (apply str seq) 
) 

(str2 (reverse "Hello")) 
"olleH" 

Fajnie, działa! Ale teraz:

(let [a 1 b 2] 
    (str2 '(a "+" b "=" (+ a b))) 
) 
"a+b=(+ a b)" 

Hmmm, jak to rozwiązać? :)

W tym przypadku, aby str akceptował wiele parametrów, które są oceniane przed uruchomieniem funkcji str, daje najłatwiejszą składnię. Ilekroć musisz użyć str na kolekcji, aplikacja to prosty sposób na konwersję kolekcji na osobne parametry.

Stworzenie łańcucha, który akceptuje kolekcję i sprawdzenie, czy każda jego część wymaga więcej wysiłku, pomaga tylko w rzadziej używanych przypadkach, powoduje bardziej skomplikowany kod lub składnię lub ogranicza możliwość zastosowania. Więc może istnieć lepszy sposób odwracania łańcuchów, ale odwrotne, zastosowanie i str są najlepsze w tym, co robią.

+0

Wow, dziękuję za szczegółową odpowiedź, jest to bardzo bardzo przydatne poznanie i wyjaśnienie (bardzo zwięźle) decyzji projektowych stojących za 'str' i' reverse' and the string-only 'reverse' function. – djhworld

+0

Jest to coś, co zdałem sobie sprawę, zastanawiając się, dlaczego niektóre funkcje pobierają kolekcje, a inne (serię) oddzielnych parametrów. Zrozumienie różnicy nie tylko pomogło mi w korzystaniu z innych funkcji, ale także w decydowaniu o parametryzacji własnej. Dlatego też myślałem, że trochę więcej opracowania "prostego pytania" może być przydatne także dla innych. – NielsK

+0

Dobre, szczegółowe śledztwo, ale przykład '(str2 '(a" + "b" = "(+ a b))) jest człowiekiem słomianym. Zapisanie go jako '(str2 [a" + "b" = "(+ a b)])' lub użycie '(list ...)' zamiast '[...]' byłoby trywialne. Jak już zauważyłeś, obecna wersja jest wygodniejsza dla zwykłego przypadku użycia, więc używamy go; ale nie ma powodu, abyśmy * nie mogli * zrobić tego w inny sposób, co wydaje się sugerować. – amalloy