如何在Clojure中替换Java的嵌套for循环?

16

我创建了一个非常简单的嵌套循环示例,但无法编写相应的Clojure代码。我一直在尝试使用列表推导式,但无法得到相同的答案。任何帮助都将不胜感激。

public class Toy {

    public static void main(String[] args) {
        int maxMod = 0;
        for (int i=0;i<1000;i++) {
            for (int j=i;j<1000;j++) {
                if ((i * j) % 13 == 0 && i % 7 == 0) maxMod = i * j;
            }
        }
        System.out.println(maxMod);
    }
}

6
我猜这不是练习的重点,但你可以每次将i增加七个单位而不是一个单位,以节省大量的浪费工作。 - amalloy
3个回答

25

这里是一个列表推导式的解决方案:

(last 
  (for [i (range 1000) 
        j (range 1000)
        :let [n (* i j)] 
        :when (and (= (mod n 13) 0) 
                   (= (mod i 7) 0))] 
    n))

那和使用doseq有什么区别? - BillRobertson42
1
你真的只想要最后一个条目,我们假设它也将是最大的条目吗?在上面的表达式中,max函数将继续尝试找出它是否已经看到了由for序列产生的每个元素的新最大值。 - seh
3
@Bill的函数for返回一个懒惰序列,而doseq则会执行体内语句多次(可能是为了产生副作用),并返回nil。 - Retief
顺便说一句,很好的工作,将计算“n”的过程提取出来,这是原始Java函数未能做到的。 - seh

9

通常情况下,您需要使用某种序列操作(例如dnolen的答案)。但是,如果您需要执行某些无法用序列函数组合表达的操作,则可以使用loop宏。对于这个精确的问题,dnolen的答案比任何使用loop的方法都要好,但出于说明目的,以下是使用loop编写的内容。

(loop [i 0
       max-mod 0]
  (if (>= i 1000)
    (println max-mod)
    (recur (inc i)
           (loop [j 0
                  max-mod max-mod]
             (if (>= j 1000)
               max-mod
               (recur (inc j)
                      (if (and (= (mod (* i j) 13) 0)
                               (= (mod 1 7) 0))
                        (* i j)
                        max-mod)))))))

这几乎是您提供的代码的精确翻译。

也就是说,显然这很丑陋,因此尽可能使用for(或其他类似的函数)的解决方案更可取。


2
列表推导式可以从其他列表创建列表,但您只需要一个单一的值作为结果。您可以使用列表推导式创建输入值(ij),然后使用reduce从列表中获取单个值:
(reduce (fn [max-mod [i j]]
          (if (and (zero? (mod (* i j) 13))
                   (zero? (mod i 7)))
            (* i j)
            max-mod))
        0
        (for [i (range 1000) j (range 1000)]
             [i j]))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接