Clojure For Comprehension示例

时间:2012-07-17 20:42:44

标签: clojure sequence for-comprehension

我正在使用docjure,它的select-columns函数需要一个列映射。我想抓住我的所有列而不必手动指定它。 如何生成以下作为惰性无限向量序列[:A:B:C:D:E ...:AA:AB:AC ....:ZZ ......:XFD]?

7 个答案:

答案 0 :(得分:6)

您的问题归结为:" 如何将数字转换为字母A-Z的基数为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)

答案 1 :(得分:4)

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))

答案 2 :(得分:2)

这个答案是对的;希望以教育的方式。

数学上你要求的是字母无限序列的所有子集的懒惰序列

(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)

使用math.combanatorics:

(ns foo.core
  (:require [clojure.math.combinatorics :as combinatorics]))

答案 3 :(得分:2)

正如jneira所说,iterate感觉就像这样做的正确方法。

这是对他的功能的改进,应该更清楚地理解,因为它涉及较少的中间类型。与基于loop / recur的其他一些解决方案不同,它完全是懒惰的:

(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")

答案 4 :(得分:1)

我认为这可能是你正在寻找的东西(如果没有,好吧,至少它是我认为的“正确”答案应该是; 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")

Code in git。我怀疑我正在滥用def,但它确实有效。

这个想法非常简单:我从序列中获取输出并将其反馈回自身。对于输出中的每个值(也是输入),我通过附加种子序列中的每个字母来生成新输出。由于这是循环的,它只是继续(输入中有一个初始“”,但不是输出,这有助于避免从零开始创建一些东西)。

将输出馈送到输入中的过程在Haskell的一篇颇有名的论文中被称为“打结”。但是在Clojure中更难做到,因为它是一种急切的语言(甚至懒惰的序列也不够“懒惰”) - 我能找到的唯一解决方案是混淆def(我怀疑有人可能会做得更好{ {1}}和delay,但我没有运气。

也许它甚至可以写成地图?

[2012-07-19更新更紧凑的代码]

Tying the knot in Clojure: circular references without (explicit, ugly) mutation?的答案中有更好的代码的相关问题(这与jneira的答案相同)。

为了完整性,这是使用force的最终版本:

iterate

答案 5 :(得分:0)

可能有一种方法可以删除“for”复制,但这里有一些对我有用的东西:

(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)))

答案 6 :(得分:-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;
}

伙计们就是这么简单,或者我错过了什么...... 当然上面的程序反向打印所需的字符串 我们可以通过使用递归或将其存储在字符串中来反转它...