else - funciones en clojure
Ejemplo de Clojure For Comprehension (7)
Estoy usando docjure y necesita un mapa de columnas para su función de columnas de selección. Me gustaría obtener todas mis columnas sin tener que especificarlas manualmente. ¿Cómo puedo generar lo siguiente como una secuencia de vector infinitamente perezosa [: A: B: C: D: E ...: AA: AB: AC ....: ZZ ...: XFD]?
Probablemente hay una manera de eliminar la duplicación "para", pero aquí hay algo que funciona para mí:
(def all-letters (map char (range 65 90)))
(defn kw [& args] (keyword (apply str args)))
(concat
(for [l all-letters] (kw l))
(for [l all-letters l2 all-letters] (kw l l2))
(for [l all-letters l2 all-letters l3 all-letters] (kw l l2 l3)))
Esta respuesta es incorrecta; con suerte de una manera educativa.
matemáticamente lo que estás pidiendo es una secuencia perezosa de todos los subconjuntos de la secuencia infinita del alfabeto.
(take 40 (map #(keyword (apply str %))
(rest (combinatorics/subsets "ABCDEFGHIJKLMNOPQRSTUVWXYZ"))))
(:A :B :C :D :E :F :G :H :I :J :K :L :M :N
:O :P :Q :R :S :T :U :V :W :X :Y :Z :AB :AC
:AD :AE :AF :AG :AH :AI :AJ :AK :AL :AM :AN :AO)
foo.core> (nth (map #(keyword (apply str %))
(rest (combinatorics/subsets "ABCDEFGHIJKLMNOPQRSTUVWXYZ")))
40000)
:BLOUZ
project.clj:
(defproject foo "1.0.0-SNAPSHOT"
:description "FIXME: write description"
:dependencies [[org.clojure/clojure "1.3.0"]
[ org.clojure/math.combinatorics "0.0.3"]]
:dev-dependencies [[swank-clojure/swank-clojure "1.4.0"]]) ; swank)
usando math.combanatorics:
(ns foo.core
(:require [clojure.math.combinatorics :as combinatorics]))
Su pregunta se reduce a: " ¿Cómo convierto un número en una cadena de base 26 con el alfabeto AZ? ".
Aquí hay una forma de hacerlo: probablemente no de la manera más concisa, pero hacerlo más elegante se deja como un ejercicio para el lector :).
Suponga que los números 0-25 se asignan a ''A'' - ''Z'', 26 a ''AA'', etcétera. Primero definimos una función to-col
que convierte un entero en una palabra clave de columna. Puedes usar esa función para generar una secuencia infinita.
(defn to-col [num]
(loop [n num s ()]
(if (> n 25)
(let [r (mod n 26)]
(recur (dec (/ (- n r) 26)) (cons (char (+ 65 r)) s)))
(keyword (apply str (cons (char (+ 65 n)) s))))))
Eso le da una forma de generar una secuencia infinita de palabras clave de columna:
(take 100 (map to-col (range)))
;; => (:A :B :C :D :E :F :G :H :I :J :K :L :M :N :O :P :Q :R :S :T :U :V :W
;; :X :Y :Z :AA :AB :AC :AD :AE :AF :AG :AH :AI :AJ :AK :AL :AM :AN :AO :AP
;; :AQ :AR :AS :AT :AU :AV :AW :AX :AY :AZ :BA :BB :BC :BD :BE :BF :BG :BH
;; :BI :BJ :BK :BL :BM :BN :BO :BP :BQ :BR :BS :BT :BU :BV :BW :BX :BY :BZ
;; :CA :CB :CC :CD :CE :CF :CG :CH :CI :CJ :CK :CL :CM :CN :CO :CP :CQ :CR
;; :CS :CT :CU :CV)
La función clojure esencial para corecursion (y "atar el nudo" se trata de eso, ¿no?) Es iterar:
(def abc (map (comp str char) (range 65 91)))
(defn cols [seed]
(let [next #(for [x %] (for [y seed] (str x y)))]
(->> (iterate #(apply concat (next %)) seed)
(mapcat identity))))
(time (first (drop 475254 (cols abc))))
"Elapsed time: 356.879148 msecs"
"AAAAA"
(doc iterate)
-------------------------
clojure.core/iterate
([f x])
Returns a lazy sequence of x, (f x), (f (f x)) etc. f must be free of side-effects
EDITAR: Generalización de la función para devolver los subconjuntos "ordenados" de un conjunto
(defn ordered-combinations [seed]
(->> (map list seed)
(iterate #(for [x % y seed] (concat x [y])))
(mapcat identity)))
(def cols
(let [abc (map char (range 65 91))]
(map #(apply str %) (ordered-combinations abc))))
user> (take 30 (map #(apply str %) cols))
("A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z" "AA" "AB" "AC" "AD")
user> (take 28 (ordered-combinations [0 1]))
((0) (1) (0 0) (0 1) (1 0) (1 1) (0 0 0) (0 0 1) (0 1 0) (0 1 1) (1 0 0) (1 0 1) (1 1 0) (1 1 1) (0 0 0 0) (0 0 0 1) (0 0 1 0) (0 0 1 1) (0 1 0 0) (0 1 0 1) (0 1 1 0) (0 1 1 1) (1 0 0 0) (1 0 0 1) (1 0 1 0) (1 0 1 1) (1 1 0 0) (1 1 0 1))
#include<stdio.h>
int main()
{
int n=703;
char arr[26]={''a'',''b'',''c'',''d'',''e'',''f'',''g'',''h'',''i'',''j'',''k'',''l'',''m'',''n'',''o'',''p'',''q'',''r'',''s'',''t'',''u'',''v'',''w'',''x'',''y'',''z''};
while(n){
printf("%c ",arr[(n)%26]);
n=(n)/26;
}
return 0;
}
chicos es esto tan simple como esto o me falta algo ... por supuesto, el programa anterior imprime el atring requerido en reversa, podemos evitar eso mediante el uso de recursion o almacenarlo en una cadena para revertirlo ...
Como lo menciona jneira, iterar se siente como la manera correcta de hacer esto.
Aquí hay una mejora en su función que debería ser más clara de entender, ya que implica menos tipos intermedios. Es completamente vago a diferencia de algunas de las otras soluciones basadas en loop / recurre:
(defn column-names-seq [alphabet]
(->> (map str alphabet)
(iterate (fn [chars]
(for [x chars
y alphabet]
(str x y))))
(apply concat)))
Para usarlo, simplemente proporcione una cadena de alfabeto, por ejemplo:
(take 30 (column-names-seq "ABCDEFGHIJKLMNOPQRSTUVWXYZ")) ;; => ("A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z" "AA" "AB" "AC" "AD")
Creo que este es el tipo de cosa que estabas buscando (si no, bueno, al menos es lo que pensé que debería ser la respuesta "correcta"; o).
(defn stream [seed]
(defn helper [slow]
(concat (map #(str (first slow) %) seed) (lazy-seq (helper (rest slow)))))
(declare delayed)
(let [slow (cons "" (lazy-seq delayed))]
(def delayed (helper slow))
delayed))
(take 25 (stream ["a" "b" "c"]))
("a" "b" "c" "aa" "ab" "ac" "ba" "bb" "bc" "ca" "cb" "cc" "aaa" "aab" "aac" "aba" "abb" "abc" "aca" "acb" "acc" "baa" "bab" "bac" "bba")
Código en git . Sospecho que estoy abusando horriblemente, pero funciona.
La idea es bastante simple: tomo el resultado de la secuencia y lo vuelvo a alimentar. Para cada valor en la salida (que también es la entrada), genero una nueva salida agregando cada una de las letras en la secuencia de inicialización. Como esto es circular, simplemente continúa (hay un "" inicial que está en la entrada, pero no en la salida, que ayuda a evitar crear algo de la nada).
El proceso de alimentar la salida a la entrada se llama "atar el nudo" en un papel bastante famoso para Haskell. Pero es más difícil de hacer en Clojure porque es un lenguaje entusiasta (e incluso las secuencias perezosas no son "lo suficientemente flojas") - la única solución que pude encontrar fue desordenar con def
(sospecho que alguien podría hacerlo mejor con delay
y force
, pero No tuve suerte).
Y tal vez incluso podría escribirse como un mapa?
[actualizado 2012-07-19 con un código más compacto]
Pregunta relacionada con un código mucho mejor en una respuesta en Tying the knot in Clojure: referencias circulares sin (explícita, fea) mutación? (es la misma idea que la respuesta de jneira).
Para completar, aquí está la versión final usando iterate
:
(defn stream [seed]
(defn helper [slow] (mapcat (fn [c] (map #(str c %) seed)) slow))
(apply concat (iterate helper seed)))