Clojure - 动态嵌套循环的惯用方法

时间:2017-07-10 21:39:23

标签: python clojure

此代码基本上是循环遍历提供的字符串中的每个字母并继续直到z,然后递增前一个字母并在a处重新启动(例如从" bde"开始,接下来将是&#34 ; bdf",结束于" zzz" - 与典型循环的工作方式相同。)

我当然可以做一个嵌套的理解,因为这只是三个级别,但如果级别是任意深度的,我传统上接近的方式是使用递归(如下所示),基本上等于深度优先搜索。

这种方法的问题在于任何非平凡大小的输入都会使堆栈爆炸(例如," abcd"),我无法想到一个没有递归的好方法。在clojure代码下面实现的Python中的类似方法(具有一些小的差异,如累积结果在可变列表中)并没有达到输入的堆栈限制" abcd"。

我尝试使用loop / recur但是这个构造似乎不能在for macro中工作,因为调用必须在下一个循环迭代中挂起,因此不在尾部位置(至少我认为是原因)。

解决此类问题的最常用方法是什么?

;;; example using for macro
(defn gen-pw [pw r]
  (cond (empty? pw) r
        :else (flatten (for [x (range (int(first pw)) 123)]
                            (gen-pw (rest pw) (str r (char x)))))))


;;; example using map instead of for macro
(defn gen-pw [pw r]
  (cond (empty? pw) r
        :else (flatten (map #(gen-pw (rest pw) (str r (char %)))
                            (range (int(first pw)) 123))))) 

(gen-pw "bde" "") 
def gen_pw(pw,r='',final=[]):
    if not pw:
        final.append(r)
        return 
    for letter in range(ord(pw[0]),123):
        gen_pw(pw[1:],r + chr(letter))
    return final

print(gen_pw('abcd'))

4 个答案:

答案 0 :(得分:3)

您正在通过评估类似的内容生成一个非常过度嵌套的列表:

flatten

然后尝试用map修复偶然的嵌套,当然这必须递归地走进你的巨大结构,然后爆炸。相反,生成一个平面列表开始。最简单的方法就是使用map版本,将mapcat替换为flatten,然后删除现在不必要的(defn gen-pw [pw r] (cond (empty? pw) [r] :else (mapcat #(gen-pw (rest pw) (str r (char %))) (range (int(first pw)) 123))))

r

您还需要将基本案例从[r]调整为var io = require('socket.io').listen(server ....),就像我在此处所做的那样:您正在生成有效密码的列表,而不仅仅是一个密码,因此返回类型应始终为列表。

答案 1 :(得分:1)

执行此操作的一种方法是使用iterate

(defn transition [[text n]]
  (let [c (nth text n)
        nxt (if (= c \z) \z (-> c int inc char))
        nxt-str (str (subs text 0 n) nxt (subs text (inc n) (dec (count text))))]
    (if (= \z nxt)
      [nxt-str (inc n)]
      [nxt-str n])))

(defn ongoing? [[text n]]
  (not (every? #(= \z %) text)))

(->> (iterate transition ["zza" 2])
     (take-while ongoing?)
     (map first))

请注意,对于["zza" 2]\a位于第三位(因此为2),而对于[" dzs" 0] \d位于第一位置(因此为0)。

答案 2 :(得分:1)

我将此问题声明视为关于计算笛卡尔积的问题,因此我倾向于仅推荐the lazy mutual-recursion implementation of that中的clojure.math.combinatorics。使用它就像:

(ns loops
  (:require [clojure.math.combinatorics :refer [cartesian-product]]
            [clojure.string :refer [join]]))

(defn chars-from [start]
  (map char (range (int start) 123)))

(defn gen-pw [pw]
  (map join (apply cartesian-product (map chars-from pw))))

(gen-pw "bde")
;;=> ("bde" "bdf" "bdg" ... "bee" "bef" "beg" ...

答案 3 :(得分:-1)

如果您达到了递归限制,请制作流程iterative rather than recursive

你是对的,当递归过程调用是更大表达式的一部分(即不在尾部位置)时,它将产生递归过程。因此,请确保递归调用表示过程的整个值。

(defn gen-pw [pw r]
  (let (s (successor pw))
    (if (nil? s)                ; (successor "zzz") is equal to nil
      r
      (gen-pw s (cons s r)))))  ; (successor "bfe") is equal to "bff"