将python代码转换为clojure,递归和for循环

时间:2015-06-09 05:02:56

标签: recursion clojure

我从长度为n的列表中计算出n个组合中的k。我发现python代码非常简洁,所以我做了直接翻译。

def comb(sofar, rest, n):
     if n == 0:
         print sofar
     else:
         for i in range(len(rest)):
             comb(sofar + rest[i], rest[i+1:], n-1)

comb("", "abcde", 3)

收率: abc,abd,abe等...

将被翻译成clojure代码:

(defn comb [sofar v n]
  (if (= n 0)
    (print sofar)
    (for [i (range 0 (count v))] 
      (comb  (str sofar (nth v i))   ;don't it need to be recur ?
             (subvec v (inc i))
             (dec i)))))

嵌套循环/递归非常混乱。

问题是如何更改代码以执行与python代码相同的功能?

我的clojure代码似乎没有做同样的工作。

1 个答案:

答案 0 :(得分:6)

首先,如果你想要一个clojure解决方案,请考虑使用clojure术语,将函数写入生成惰性序列结果的序列,并让最终使用者在需要时将这些函数减少为字符串。在下面,我根本不使用打印,只返回延迟序列,让repl成为打印机。

此外,您正在尝试将python for直接转换为clojure for来进行循环,这不是它的用途(没有双关语意)。 clojure's for是一个序列生成器,所以你可能会在那里感到困惑。

要获得问题的简单解决方案,您可以使用math.combinatorics

user=> (require '[clojure.math.combinatorics :as m])
user=> (m/combinations "abcde" 3)
((\a \b \c) (\a \b \d) (\a \b \e) (\a \c \d) (\a \c \e) (\a \d \e) (\b \c \d) (\b \c \e) (\b \d \e) (\c \d \e))

然后,如果需要,您可以编写一个映射以将其转换为与python相同输出的字符串。

user=> (map #(apply str %1) (m/combinations "abcde" 3))
("abc" "abd" "abe" "acd" "ace" "ade" "bcd" "bce" "bde" "cde")

但是,我怀疑你正在寻找更多关于循环的教程。

another solution to this problem here用于创建一个函数来将序列作为字符返回,它产生与上例相同的序列,并且也可以包含在字符串输出的映射中。它使用带有cond块的递归来控制迭代的结束。

Here is another good article通过将问题解构为本质上递归的事物来做关于它如何与递归一起工作的解释做同样的组合。

如果您想了解有关复发与强制循环的更多信息,请查看this SO question

here is a gist有一些循环/重复的例子和阶乘的递归,所以你可以直接看到两者之间的语法风格。

在习惯于编写生成此类序列的函数方面,我发现The Little Schemer是解释思维过程的绝佳资源。它是用方案编写的,但很容易理解并适用于clojure。在此之后,您可以查看更高阶函数(map / reduce)而不是使用循环。

总而言之,当你这样做时,如果你在任何地方都使用惰性函数,你的结果通常是懒惰的,并且你想尝试使用尾递归,如果你做的是递归,这样你在使用时就不会打击堆栈大型组合,以及您不感兴趣的倾销值。