Подтвердить что ты не робот

Puzzled: Clojure для цикла с: while → неожиданным поведением?

Я изучал Clojure и озадачен следующим:

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_

Добавьте add :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.

4b9b3361

Ответ 1

Посмотрим на каждую итерацию.

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

Ответ 2

Условие :while в for завершает цикл только самого внутреннего. Я использую for все время, но :while так редко, что я никогда не осознавал этого; спасибо за большой вопрос!

К сожалению, самое лучшее, что вы можете сделать, это обернуть take-while вокруг for, так как вам нужен "глобальный" стоп-счетчик в выходной последовательности, а не стоп-счетчик на одной из входных последовательностей, которые вы "повторять". Например:

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

()