У меня есть следующая функция:
(defn next-transformation
[arr]
(let [
arr-len (count arr)
i-range (range 0 arr-len)
j-range (range 0 arr-len)
indexes (for [i i-range
j j-range]
(let [
xi (nth arr i)
xj (nth arr j)
]
(if (> xi xj)
[i j]
nil
)
)
)
non-nil-indexes (filter
(fn [elem]
(not (= elem nil))
)
indexes
)
]
(if (not (empty? non-nil-indexes))
(first non-nil-indexes)
nil
)
)
)
Возвращает первый элемент массива кортежей [i j]
, описывающих элементы массива arr
, для которых arr[i] > arr[j]
истинно.
Цикл for
в приведенном ниже фрагменте проходит через каждую пару i и j:
indexes (for [i i-range
j j-range]
(let [
xi (nth arr i)
xj (nth arr j)
]
(if (> xi xj)
[i j] ;; I want the loop to stop here
nil
)
)
)
Как изменить этот цикл for так, чтобы он останавливался, как только находит первый соответствующий кортеж (т. е. цикл должен останавливаться в месте, отмеченном комментарием ;; I want the loop to stop here
)?
Вот эквивалентный код на Java:
private Integer[] next-transformation(final Integer[] arr) {
for (int i=0; i < arr.length; i++) {
for (int j=0; j < arr.length; j++) {
if (arr[i] > arr[j]) {
return new Integer[] {i, j};
}
}
}
}
Обновление 1:
По рекомендации @CharlesDuffy я заменил for
на loop
/recur
:
(defn next-transformation
[arr]
(loop [i 0
j 0]
(let [
arr-len (count arr)
]
(if (and (< i arr-len)
(< j arr-len))
(let [
xi (nth arr i)
xj (nth arr j)
j-plus-1 (+ j 1)
i-plus-1 (+ i 1)
new-i (if (< j-plus-1 arr-len)
i
(+ i 1))
new-j (if (< j-plus-1 arr-len)
(+ j 1)
0)
]
(if (> xi xj)
;; We found it
[i j]
;; We haven't found it, recur
(recur new-i new-j)
)
)
nil ; We are at the end of the loop
) ; if
)
) ; loop
) ; defn
for
в Clojure не следует рассматривать как цикл. Это определение последовательности; то, что его можно использовать для управления потоком, является побочным эффектом, а не основной целью....при этом см.
:while
внутриfor
.Но если вы хотите остановиться после возврата первого элемента, почему бы не просто
take 1
из сгенерированной последовательности? (На самом деле есть правильный ответ на этот вопрос «почему бы и нет?» о том, как последовательности Clojure разбиваются на фрагменты и не обрабатываются на уровне лени поэлементно, но это то место, где оптимизация производительности делает поведение в реальном мире немного отличным от теоретического идеала; однако стоит знать и понимать, что такое теоретические идеалы, если вы хотите иметь возможность писать идиоматический код).... во всяком случае, для вашего фактического варианта использования я бы обычно использовал
loop
/recur
, а неfor
вообще. Выбор правильного инструмента для работы и все такое.(также, если вы используете
:when
в своемfor
, вам не нужно возлагать на вызывающего абонента ответственность за пропускnil
)for
не похож на циклическую конструкцию, которую вы используете в императивных языках, но это «понимание списка». Clojure — это не очередной «транспилятор LISP» для базового языка, а функциональный язык программирования с неизменяемыми структурами данных. Так что это некоторый сдвиг в мышлении, так что не копайте глубоко в «императивных» областях мозга для решения подобных коанов.@CharlesDuffy Спасибо за ваши комментарии. У меня вопрос: является ли
non-nil-indexes
ленивой последовательностью?@CharlesDuffy Re ... во всяком случае, для вашего фактического варианта использования я бы обычно использовал loop/recur: правильно ли мое решение в обновлении 1 (соответствует букве и духу Clojure )?