2011-09-19 17 views
17

¿Hay alguna otra forma de hacerlo en Clojure?Invierta una cadena (pregunta simple)

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

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

¿Tiene que ver la apply $ str $ vec poco cada vez que desee invertir una cadena de nuevo a su forma original?

Respuesta

24

Es mejor que utilice clojure.string/reverse:

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

ACTUALIZACIÓN: Para los curiosos, aquí siguen los fragmentos de código fuente para clojure.string/reverse tanto en Clojure (v1.4) y 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 tiene sentido, gracias. Todavía estoy un poco demasiado arraigado en el universo Haskell donde las cadenas son solo listas vinculadas – djhworld

10

Aplicar, como inversa, funciona en cualquier tipo seqable, no sólo los vectores, por lo

(aplicar str (inverso "Hola"))

es un poco más corto. Sin embargo, clojure.string/reverse debería ser más eficiente.

18

OK, por lo que sería fácil de rodar su propia función con aplicar el interior, o utilizar una versión dedicada de marcha atrás que funciona mejor (pero sólo) en cadenas. Sin embargo, las principales cosas en las que pensar aquí son la aridad (cantidad y tipo de parámetros) de la función str y el hecho de que el reverso funciona en una colección.

(doc reverse) 

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

Esto significa que el reverso no solo funciona en cadenas, sino también en todas las demás colecciones. Sin embargo, debido inversa espera una colección como parámetro, se trata de una cadena como un conjunto de caracteres

(reverse "Hello") 

y devuelve uno, así

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

Ahora si sólo sustituimos las funciones de la colección, puede detectar la diferencia:

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

mientras

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

La gran diferencia entre los dos es la cantidad de parámetros. En el primer ejemplo, str toma un parámetro, una colección de 5 caracteres. En el segundo, str toma 5 parámetros: 5 caracteres.

¿Qué espera la función 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. 

Así que cuando das en un parámetro (una colección), todos los retornos str es un toString de la colección. Pero para obtener el resultado que desea, debe alimentar los 5 caracteres como parámetros separados a str, en lugar de a la colección en sí. Aplicar es la función que se usa para 'entrar' en la colección y hacer que eso suceda.

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

funciones que manejan múltiples parámetros separados se ven a menudo en Clojure, por lo que es bueno darse cuenta de cuándo y por qué es necesario utilizar aplicar. El otro lado para darse cuenta es, ¿por qué el escritor de la función str hizo que aceptara múltiples parámetros en lugar de una colección?Por lo general, hay una buena razón. ¿Cuál es el caso de uso predominante para la función str? No concatenar seguramente una colección de caracteres separados, sino concatenar valores, cadenas y resultados de funciones.

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

¿Qué pasa si tenemos un str que acepta una sola colección como parámetro?

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

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

Cool, it works! Pero ahora:

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

Hmmm, ¿ahora cómo resolver eso? :)

En este caso, hacer que str acepte múltiples parámetros que se evalúan antes de que se ejecute la función str le da a str la sintaxis más fácil. Siempre que necesite usar str en una colección, aplicar es una forma simple de convertir una colección en parámetros separados.

Hacer un str que acepte una colección y hacer que evalúe cada parte dentro requeriría más esfuerzo, ayuda solo en casos de uso menos comunes, da como resultado un código o sintaxis más complicada o limita su aplicabilidad. Entonces, podría haber una forma mejor de invertir cadenas, pero invertir, aplicar y str son las mejores en lo que hacen.

+0

Wow gracias por la respuesta detallada, esto definitivamente es muy útil para conocer y explicar (muy sucintamente) las decisiones de diseño detrás de 'str' y' reverse' y la función 'reverse' solo de cadena. – djhworld

+0

Es algo de lo que me di cuenta cuando me preguntaba por qué algunas funciones toman colecciones y otras (una serie de) parámetros separados. Comprender la diferencia no solo me ayudó a usar las funciones de los demás, sino también a decidir cómo parametrizar las mías. Es por eso que pensé que un poco más de elaboración de una 'pregunta simple' podría ser útil para otros también. – NielsK

+0

Una investigación buena y detallada, pero el ejemplo '(str2 '(a" + "b" = "(+ a b)))' es un hombre de paja. Sería trivial escribirlo como '(str2 [a" + "b" = "(+ a b)])', o para usar '(list ...)' en vez de '[...]'. Como nota, la versión existente es más conveniente para el caso de uso común, así que la usamos; pero no hay ninguna razón por la que * no podamos * hacerlo de otra manera, lo cual parece implicar. – amalloy