2012-01-11 55 views
12

我一直在学习的Clojure和不解以下内容:困惑:Clojure for循环:while - >意外行为?

user=> (for [a (range 1 4) b (range 1 4)] [a b]) 
([1 1] [1 2] [1 3] [2 1] [2 2] [2 3] [3 1] [3 2] [3 3]); _no surprise here_ 

让我们添加:while (not= a b),我希望看到一个空列表,就好像条件为假循环应停止。在这种情况下,它是a = b = 1的第一项。让我们来看看:

user=> (for [a (range 1 4) b (range 1 4) :while (not= a b) ] [a b]) 

([2 1] [3 1] [3 2]) ; _surprise!_ 

更改:while:when过滤掉(= a b)

user=> (for [a (range 1 4) b (range 1 4) :when (not= a b) ] [a b]) 
([1 2] [1 3] [2 1] [2 3] [3 1] [3 2]); _expected_ 

任何人都可以解释为什么(for [ ... :while ..] ...)的行为也是这样吗?

我使用Clojure的1.3 OS X.

谢谢你,为缺乏格式化的道歉。这是我在StackOverflow上的处女帖子。

回答

9

让我们看看每个迭代。

a = 1 
    b = 1 -> a == b, break because of while 

a = 2 
    b = 1 -> a != b, print [2 1] 
    b = 2 -> a == b, break because of while 

a = 3 
    b = 1 -> a != b, print [3 1] 
    b = 2 -> a != b, print [3 2] 
    b = 3 -> a == b, break because of while 
+0

谢谢尼基塔。所以如果:只适用于内部循环,我如何使它适用于外部循环?我遇到的问题是:[for [from [:a:b:c:d:e:f] to [:a:b:c:d:e:f]:let [path(find-path ab)]] :while path)path);当路径为零时,循环应该停止。 – jbear 2012-01-11 11:30:32

+0

对不起,我的意思是 (for [from [:a:b:c:d:e:f] to [:a:b:c:d:e:f]:let [path(find-path from to) ]:while path] path) – jbear 2012-01-11 11:40:33

+0

@jbear,我不知道:(可能是你最好创建懒惰seq的路径,并采取,而他们不是null。我不确定'for'是懒惰。 – 2012-01-11 14:27:54

3

:while的条件在for仅终止最内环路。我一直使用for,但:while很少,我从来没有意识到这一点;感谢您的好问题!

可悲的是,我认为你能做的最好是紧裹take-whilefor身边,因为你需要一个“全球”输出序列停止柜台上你迭代输入序列中的一个,而不是停止计数器过度。例如:

(->> (for [a (range 1 4) 
      b (range 1 4)] 
     [a b]) 
    (take-while (fn [[a b]] (not= a b)))) 

() 
+1

为了清楚起见,':while'测试适用于它之前的序列,不一定是最内部的循环 – 2012-01-11 21:21:26

+0

感谢澄清,Alex。给予语言的突出,我不禁感到有点失望,其中一个基本构建块 - 列表理解 - 应该是相当有限的。希望随着我的学习进展,我会发现更多/更好的方式来做这件事,有很大的帮助from everyone。 – jbear 2012-01-12 07:12:46

+0

@jbear它实际上比它更少限制这种方式这是另一种方式。这样,如果你想要“全局”行为,你可以在'take-while'中轻松地封装'for'表达式。但是,如果“全球”行为是'for /:while'做的,你将如何恢复当前的“中级”行为?你必须完全放弃'for'并构建一个鼠标嵌套,mapcat,filter,take-while。 – amalloy 2012-01-12 18:12:06