2011-07-31 12 views
5

Soy nuevo en Clojure y estoy haciendo algunas cosas básicas de labrepl, ahora quiero escribir una función que reemplace ciertas letras con otras letras, por ejemplo: elosska → elößkä.Iterar a través de un mapa con doseq

me escribió esto:

(ns student.dialect (:require [clojure.string :as str])) 
(defn germanize 
    [sentence] 
    (def german-letters {"a" "ä" "u" "ü" "o" "ö" "ss" "ß"}) 
    (doseq [[original-letter new-letter] german-letters] 
    (str/replace sentence original-letter new-letter))) 

pero no funciona como espero. ¿Usted me podría ayudar por favor?

Respuesta

16

Aquí es mi opinión,

 

(def german-letters {"a" "ä" "u" "ü" "o" "ö" "ss" "ß"}) 

(defn germanize [s] 
    (reduce (fn[sentence [match replacement]] 
      (str/replace sentence match replacement)) s german-letters)) 

 
(germanize "elosska") 
+0

Sí, eso es más dulce que el mío. Muy bonito :-) – Scott

+0

Sí, 'reduce' es más funcional y corto, solo quería mostrar dónde surge el problema ... –

+0

Wow, me tomó un poco asimilar este, es un uso inteligente de reducir y argumentar la desestructuración ! Podría usar la técnica de inmediato en algunos de mi propio código. Realmente desearía que hubiera un libro con 'patrones' funcionales como este. – NielsK

6

Hay 2 problemas aquí:

  1. doseq no conserva la cabeza de la lista que creó por su evaluación, por lo que no obtendrá ningún resultado
  2. str/replace obras de copias separadas de texto, produciendo 4 resultados diferentes: puede verificar esto reemplazando doseq con for y obtendrá una lista con 4 entradas.

código podría reescribirse siguiente manera:

(def german-letters {"a" "ä" "u" "ü" "o" "ö" "ss" "ß"}) 
(defn germanize [sentence] 
    (loop [text sentence 
     letters german-letters] 
    (if (empty? letters) 
     text 
     (let [[original-letter new-letter] (first letters)] 
     (recur (str/replace text original-letter new-letter) 
       (rest letters)))))) 

En este caso, los resultados intermedios se recogen, por lo que todas las sustituciones se aplican a misma cadena, produciendo cadena correcta:

user> (germanize "elosska") 
"elößkä" 

PD tampoco se recomienda usar def en la función - es mejor usarlo para formularios de nivel superior

+1

Hamza Yerlikaya proporciona una solución de estilo funcional - 'reduce' es muy útil cuando se necesita para recoger los resultados intermedios ... –

6

Alex ya ha respondido correctamente la pregunta con respecto al problema original usando doseq ... pero encontré la pregunta interesante y quería ver cómo sería una solución más "funcional". Y con eso quiero decir sin usar un bucle.

me ocurrió esto:

(ns student.dialect (:require [clojure.string :as str])) 

(defn germanize [sentence] 
    (let [letters {"a" "ä" "u" "ü" "o" "ö" "ss" "ß"} 
     regex (re-pattern (apply str (interpose \| (keys letters))))] 
    (str/replace sentence regex letters))) 

que produce el mismo resultado:

student.dialect=> (germanize "elosska") 
"elößkä" 

La línea regex (re-pattern... simplemente evalúa a #"ss|a|o|u", lo que habría sido más limpio y más fácil de leer, si ingresó como una cadena explícita, pero pensé que era mejor tener solo una definición de las letras alemanas.

+0

yo diría que este es el camino a seguir, especialmente si el rendimiento es motivo de preocupación en absoluto. Solo realiza un único reemplazo y crea una sola cadena (usando 'StringBuffer'), mientras que' loop'-ing o 'reduce' -ing sobre el mapa de reemplazos construirá una nueva cadena por reemplazo y siempre repasará toda la cadena del paso anterior. Además, dado que todas las soluciones presentadas (con razón) usan 'clojure.string/replace', que es una función integrada que puede manejar toda la operación en una llamada, se le debe permitir manejarla. Además, esto básicamente se lee como la declaración del problema. +1. –

+0

Por cierto, supongo que los reemplazos nunca introducen patrones que necesiten procesamiento adicional.Este es el caso aquí, pero si no fuera así, entonces ese sería un problema completamente diferente que requeriría mayor aclaración (por ejemplo, ¿se supone que toda la operación es idempotente? Sino, ¿cómo deberían pedirse los pares de patrón/reemplazo? ¿Sigue siendo así? ok para usar un mapa para mantenerlos?). Solo un lado ... :-) –

+0

@ Michał Gracias por tu comentario. No estaba al tanto de las implicaciones de rendimiento de la reducción, por lo que aprendí algo valioso hoy (¡y todavía tengo mucho que aprender de Clojure!). – Scott

Cuestiones relacionadas