Функция Clojure seq
может создавать последовательности из многих типов объектов, таких как коллекции и массивы.seq
также работает с любым объектом, который реализует интерфейс java.util.Iterable
из среды Java Collections.К сожалению, семантика последовательностей Clojure и java.util.Iterator
(который используется с Iterable
) не на 100% совместимы, как указано в ответе @cfrick.
Это считается или в какой-то момент считалось нормальным для каждого вызова next
метода Iterator
для возврата одного и того же (изменяемого) объекта.Это работает только до тех пор, пока возвращаемое значение next
используется и отбрасывается до последующего вызова next
.Однако, если возвращаемые значения next
сохраняются и используются позже, это может привести к неопределенному поведению.Это именно то, что происходит в некоторых реализациях последовательностей Clojure.
Позвольте мне проиллюстрировать.Ниже приведена игрушечная реализация целого ряда целых чисел в Java.Обратите внимание, что реализация метода next
всегда возвращает один и тот же объект.
package foo.bar;
import java.util.*;
public class MyRange implements Iterable<MyRange.Num> {
public static class Num {
private int n;
public int get() { return n; }
public String toString() { return String.valueOf(n); }
}
private int max;
public MyRange(int max) { this.max = max; }
// Implementation of Iterable
public Iterator<Num> iterator() {
return new Iterator<Num> () {
private int at = 0;
private Num num = new Num();
public boolean hasNext() {
return at < max;
}
public Num next() {
num.n = at++;
return num;
}
};
}
}
Этот код прекрасно работает, когда используется способом, предназначенным разработчиками платформы Java Collections.Например:
(loop [i (.iterator (MyRange. 3))]
(when (.hasNext i)
(print (str (.next i) " "))
(recur i)))
;;=> 0 1 2
Но как только мы добавим последовательность Clojure в микс, все пойдет не так:
(map #(.get %) (MyRange. 3))
;;=> (2 2 2)
Мы получили (2 2 2)
вместо (0 1 2)
.Это как раз та проблема, о которой идет речь в seq
.
Если память используется, реализация Iterator
для EnumhMap
в Java 6 использовала реализацию изменяемого объекта вНаименование эффективности.Такая реализация не распределяет память на каждой итерации, поэтому она быстрее и не создает мусора.Но эта «техника» была проблематичной не только для Clojure, но и для некоторых пользователей Java.Таким образом, поведение было изменено в Java 7.