I am using docjure and it needs a column map for its select-columns function. I would like to grab all my columns without having to specify it manually. How do I generate the following as a lazy infinite vector sequence [:A :B :C :D :E ... :AA :AB :AC .... :ZZ ... :XFD]?
7 回答
您的问题归结为:“如何将数字转换为带有字母 AZ 的 base-26 字符串? ”。
这是一种方法 - 可能不是最简洁的方法,但让它更优雅留给读者作为练习:)。
假设数字 0-25 映射到“A”-“Z”,26 映射到“AA”等等。首先,我们定义一个to-col
将整数转换为列关键字的函数。您可以使用该函数生成无限序列。
(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))))))
这为您提供了一种生成无限列关键字序列的方法:
(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)
corecursion 的基本 clojure 函数(和“打结”是关于它的,不是吗?)是迭代的:
(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
编辑:泛化函数以返回集合的“有序”子集
(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))
正如 jneira iterate 所提到的那样,它是正确的方法。
这是对他的功能的改进,应该更清楚地理解,因为它涉及较少的中间类型。与其他一些基于循环/递归的解决方案不同,它是完全惰性的:
(defn column-names-seq [alphabet]
(->> (map str alphabet)
(iterate (fn [chars]
(for [x chars
y alphabet]
(str x y))))
(apply concat)))
要使用它,只需为其提供一个字母字符串,例如:
(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")
这个答案是错误的;希望以教育的方式。
从数学上讲,您要的是字母表无限序列的所有子集的惰性序列。
(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
项目.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)
使用 math.combanatorics:
(ns foo.core
(:require [clojure.math.combinatorics :as combinatorics]))
我认为这可能是您正在寻找的那种东西(如果不是,那么至少我认为“正确”的答案应该是 ;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")
git 中的代码。我怀疑我在滥用职权def
,但它确实有效。
这个想法很简单:我从序列中获取输出并将其反馈给自身。对于输出中的每个值(也是输入),我通过在种子序列中附加每个字母来生成一个新的输出。由于这是循环的,因此它会继续进行(输入中有一个初始的“”,但输出中没有,这有助于避免从无到有创建某些东西)。
在 Haskell 的一篇相当著名的论文中,将输出馈入输入的过程称为“打结”。但是在 Clojure 中更难做到,因为它是一种急切的语言(甚至懒惰的序列也不够“懒惰”)——我能找到的唯一解决方案就是搞砸了(我怀疑有人可能会用anddef
做得更好,但我没有运气)。delay
force
也许它甚至可以写成地图?
[2012-07-19 更新,代码更紧凑]
相关问题与更好的代码在Clojure 中打结的答案中:没有(显式,丑陋)突变的循环引用?(这与 jneira 的答案相同)。
为了完整起见,这是使用的最终版本iterate
:
(defn stream [seed]
(defn helper [slow] (mapcat (fn [c] (map #(str c %) seed)) slow))
(apply concat (iterate helper seed)))
Probably there is a way to remove the "for" duplication, but here is something that works for me:
(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)))
#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;
}
伙计们是这样简单还是我错过了什么......当然,上面的程序会反向打印所需的atring,我们可以通过使用递归或将其存储在字符串中来反转它来避免这种情况......