#clojure
Вопрос:
У меня есть следующая функция:
(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
)
)
)
Как я могу изменить этот цикл для, чтобы он остановился, как только найдет первый соответствующий кортеж (то есть цикл должен остановиться в месте, отмеченном ;; 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
Комментарии:
1.
for
в Clojure не следует рассматривать как петлю. Это определение последовательности ; то, что его можно использовать для управления потоком, является побочным эффектом, а не основной целью.2. …тем не менее, загляни
:while
внутрьfor
.3. …в любом случае, для вашего реального случая использования я бы, как правило, пошел с
loop
/recur
иfor
вообще не пошел. Выбор-правильного-инструмента-для-работы и все такое.4. (кроме того, если вы используете
:when
в своемfor
, вам не нужно возлагать на вызывающего абонента ответственность за пропускnil
s)5.
for
это не похоже на циклическую конструкцию, которую вы используете в императивных языках, но это «понимание списка». Clojure-это не еще один «транспилятор LISP» для базового языка, а функциональный язык программирования с неизменяемыми структурами данных. Так что это довольно серьезный сдвиг в мышлении, так что не копайтесь слишком глубоко в своих «императивных» областях мозга для решения подобных коанов.
Ответ №1:
В for
понимании списка используйте :when
фильтр для интересующих кортежей и first
возвращайте только первый:
(defn next-transformation [arr]
(first (for [i (range (count arr))
j (range (count arr))
:when (> (nth arr i) (nth arr j))]
[i j])))