Ответ 1
Много вопросов, пусть сначала начнется с нескольких анверов:
- Да,
xf
==xform
является "преобразователем". - Ваша функция
my-identity
не компилируется. У вас есть параметр, а затем несколько других явлений функции. Я считаю, что вы забыли(fn ...)
. -
Ваш аргумент для вашего идентификационного преобразователя называется
xf
. Однако это обычно называемыйrf
, что означает "сокращение функции". Теперь запутанная часть чтоxf
также уменьшают функции (следовательно,comp
просто работает). Однако, это сбивает с толку, что вы назовете егоxf
, и вы должны называть егоrf
. -
Преобразователи обычно "сконструированы", поскольку они могут быть с сохранением состояния и/или переданные параметры. В вашем случае вам не нужно его строить, так как это простой и не имеет состояния или даже параметра. Однако имейте в виду, что вы обычно завершают вашу функцию в другой функции возврата
fn
. Это означает, что вы необходимо вызвать(my-identity)
вместо того, чтобы просто передать его какmy-identity
. Опять же, это прекрасно здесь, просто немного несовместимо и, возможно, запутывает. -
Позвольте сначала продолжить и притвориться, что ваш преобразователь
my-identity
правильно (это не так, и я объясню позже, что происходит). -
eduction
относительно редко используется. Он создает "процесс". То есть вы можете запускать его снова и снова и видеть результат. В принципе, просто например, у вас есть списки или векторы, в которых хранятся ваши элементы, результат применения преобразователя. Обратите внимание: чтобы на самом деле все равно нужнаrf
(функция уменьшения). -
В начале я думаю, что полезно подумать о сокращении функций как
conj
(или фактическиconj!
), или в вашем случае+
. -
Ваш
eduction
печатает элементы, которые он производит, поскольку он реализуетIterable
который вызываетсяprintln
или вашим REPL. Он просто распечатывает каждый элемент, который вы добавляете в свой преобразователь с вызовом arity 2. -
Ваш вызов
(reduce + (eduction my-identity (range 5)))
не работает посколькуeduction
(объект, построенный вeduction
), реализует толькоIReduceInit
.IReduceInit
, поскольку его название предполагает, что требует начального стоимость. Итак, это сработает:(reduce + 0 (eduction my-identity (range 5)))
-
Теперь, если вы запустите выше
reduce
, как я предлагаю, вы увидите что-то очень интересно. Он печатает 10. Несмотря на то, что вы ранее напечатали(0 0 1 1 2 2 3 3 4 4)
(что, если вы добавляете вместе, равно 20). Что здесь происходит? -
Как уже говорилось, ваш преобразователь имеет недостаток. Это не работает должным образом. проблема в том, что вы вызываете свой
rf
, а затем вызываете его еще раз в своем arity 2. В clojure материал не изменчив, если он каким-то образом внутренне изменяемый для оптимизации:). Здесь проблема заключается в том, что иногда clojure использует мутацию, и вы получаете дубликаты даже если вы никогда должным образом не фиксируете результат своего первого(rf)
в вашей функции arity 2 (в качестве аргумента для вашегоprintln
).
Устранить функцию , но оставить второй rf
вызов там:
(defn my-identity2 [rf]
(fn
([]
(println "Arity 0.")
(rf))
([result]
{:post [(do (println "Arity 1 " %) true)]
:pre [(do (println "Arity 1 " result) true)]}
(rf result))
([result input]
{:post [(do (println "Arity 2 " %) true)]
:pre [(do (println "Arity 2 " result input) true)]}
(rf (rf result input) input))))
Примечание:
- Я переименовал
xf
вrf
, как указано выше. - Теперь мы видим, что вы используете результат
rf
и передаете его на второй вызовrf
. Этот преобразователь не является преобразователем идентичности, но удваивает каждый элемент
Соблюдайте осторожность:
(transduce my-identity + (range 5));; => 10
(transduce my-identity2 + (range 5));; => 20
(count (into '() my-identity (range 200)));; => 200
(count (into [] my-identity (range 200)));; => 400
(count (into '() my-identity2 (range 200)));; => 400
(count (into [] my-identity2 (range 200)));; => 400
(eduction my-identity (range 5));;=> (0 0 1 1 2 2 3 3 4 4)
(eduction my-identity2 (range 5));;=> (0 0 1 1 2 2 3 3 4 4)
(into '() my-identity (range 5));;=> (4 3 2 1 0)
(into [] my-identity (range 5));;=> [0 0 1 1 2 2 3 3 4 4]
(into '() my-identity2 (range 5));;=> (4 4 3 3 2 2 1 1 0 0)
(reduce + 0 (eduction my-identity (range 5)));;=> 10
(reduce + (sequence my-identity (range 5)));;=> 20
(reduce + 0 (eduction my-identity2 (range 5)));;=> 20
(reduce + (sequence my-identity2 (range 5)));;=> 20
Чтобы ответить на ваши вопросы:
-
eduction
не передаетnil
в качестве аргументаresult
, когда он уменьшается. Он печатается только при печати, который вызываетIterable
интерфейс. -
nil
действительно исходит отTransformerIterator
, который является специальным классом созданный для преобразователей. Этот класс также используется дляsequence
, как вы заметили. Как указано в документах:
Получаемые элементы последовательности вычисляются пошагово. Эти последовательности будет потреблять вход постепенно по мере необходимости и полностью реализовать промежуточные операции. Это поведение отличается от эквивалентных операций на ленивых последовательности.
Причина, по которой вы получаете nil
в качестве аргумента result
, состоит в том, что итератор не имеет результирующей коллекции, которая удерживает элементы, которые повторяются до сих пор. Он просто перебирает каждый элемент. Состояние не накапливается.
Вы можете увидеть функцию уменьшения, которая используется здесь TransformerIterator
как и внутренний класс:
https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/TransformerIterator.java
Сделайте a CTRL+f
и введите xf.invoke
, чтобы узнать, как вызывается ваш преобразователь.
Функция sequence
не такая ленивая, как по-настоящему ленивая последовательность, но я
подумайте, что это объясняет эту часть вашего вопроса:
Являются ли преобразователи clojure?
sequence
просто вычисляет результаты преобразователя постепенно. Ничего
иначе.
Наконец, правильная идентификационная функция с некоторыми операциями отладки:
(defn my-identity-prop [xf]
(fn
([]
(println "Arity 0.")
(xf))
([result]
(let [r (xf result)]
(println "my-identity(" result ") =" r)
r))
([result input]
(let [r (xf result input)]
(println "my-idenity(" result "," input ") =" r)
r))))