2011-09-19 15 views
17

C'è un modo migliore per farlo in Clojure?Invertire una stringa (domanda semplice)

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

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

Avete a che fare la punta apply $ str $ vec ogni volta che si vuole invertire una stringa torna alla sua forma originale?

risposta

24

È meglio utilizzare clojure.string/reverse:

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

UPDATE: per i curiosi, qui seguire le frammenti di codice sorgente per clojure.string/reverse sia in Clojure (v1.4) e 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

Ah ha senso, grazie. Sono ancora un po 'troppo radicato nell'universo di Haskell dove le stringhe sono solo liste collegate – djhworld

10

Applicare, come invertire, funziona su qualsiasi tipo di seqable, non solo sui vettori, quindi

(applicare str (reverse "Hello"))

è un po 'più breve. clojure.string/reverse dovrebbe essere più efficiente, però.

18

OK, quindi sarebbe facile eseguire la propria funzione con l'applicazione all'interno, oppure utilizzare una versione di reverse dedicata che funziona meglio (ma solo) nelle stringhe. Tuttavia, le cose principali da considerare qui sono l'arità (quantità e tipo di parametri) della funzione str e il fatto che il reverse funzioni su una raccolta.

(doc reverse) 

clojure.core/reverse 
([coll]) 
Returns a seq of the items in coll in reverse order. Not lazy. 

Ciò significa che il contrario non funziona solo sulle stringhe, ma anche su tutte le altre raccolte. Tuttavia, a causa inverso si aspetta una raccolta come parametro, si tratta di una stringa come un insieme di caratteri

(reverse "Hello") 

e restituisce uno pure

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

Ora, se abbiamo appena sostituiamo le funzioni per la raccolta, si può individuare la differenza:

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

mentre

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

La grande differenza tra i due è la quantità di parametri. Nel primo esempio, str accetta un parametro, una raccolta di 5 caratteri. Nel secondo, str accetta 5 parametri: 5 caratteri.

Cosa si aspetta la funzione 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. 

Così, quando si dà a un parametro (una raccolta), tutti i ritorni str è un toString della collezione. Ma per ottenere il risultato desiderato, è necessario alimentare i 5 caratteri come parametri separati per str, invece della raccolta stessa. Applica è la funzione che viene utilizzata per "entrare" nella raccolta e farlo accadere.

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

Funzioni che gestiscono più parametri separati spesso visto in Clojure, quindi è bene rendersi conto quando e perché è necessario utilizzare applicarsi. L'altro lato da capire è, perché l'autore della funzione str ha fatto accettare più parametri invece di una collezione?Di solito, c'è una buona ragione. Qual è il caso d'uso prevalente per la funzione str? Non concatenare sicuramente una raccolta di caratteri separati, ma concatenando valori, stringhe e risultati delle funzioni.

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

E se avessimo uno str che ha accettato una singola raccolta come parametro?

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

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

Cool, funziona! Ma ora:

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

Hmmm, ora come risolvere quello? :)

In questo caso, fare in modo che str accetta più parametri che vengono valutati prima dell'esecuzione della funzione str, fornisce la sintassi più semplice. Ogni volta che è necessario utilizzare str su una raccolta, applicare è un modo semplice per convertire una raccolta in parametri separati.

Realizzare uno str che accetta una raccolta e fare valutare ogni parte all'interno richiederebbe uno sforzo maggiore, darebbe una mano solo in casi di utilizzo meno comuni, comporterebbe un codice o una sintassi più complicati o limiterebbe la sua applicabilità. Quindi potrebbe esserci un modo migliore per invertire le stringhe, ma invertire, applicare e str sono i migliori in quello che fanno.

+0

Wow grazie per la risposta dettagliata, questo è sicuramente molto molto utile per conoscere e spiegare (molto succintamente) le decisioni di design dietro 'str' e' reverse' e ​​la funzione solo reverse 'stringa '. – djhworld

+0

È qualcosa che ho capito quando mi sono chiesto perché alcune funzioni prendano collezioni e altri (una serie di) parametri separati. Comprendere la differenza non solo mi ha aiutato ad usare le funzioni degli altri, ma anche a decidere come parametrizzare il mio. Ecco perché ho pensato che un po 'più di elaborazione per una "domanda semplice" potrebbe essere utile anche agli altri. – NielsK

+0

Un'indagine buona e dettagliata, ma l'esempio '(str2 '(un" + "b" = "(+ a b)))' è un uomo di paglia. Sarebbe banale scriverlo come '(str2 [a" + "b" = "(+ a b)])', o usare '(list ...)' invece di '[...]'. Come si nota, la versione esistente è più conveniente per il caso d'uso comune, quindi la usiamo; ma non c'è ragione per cui * non potremmo * farlo nell'altro modo, che tu sembri implicare. – amalloy

Problemi correlati