Clojure Ассоциативное разрушение по числовому индексу в List или Seq: неожиданный результат - PullRequest
3 голосов
/ 28 апреля 2019

Ассоциативное деструктурирование Clojure позволяет деструктурировать вектор (и, возможно, seq или список ) с помощью числового индекса.

Этот шаблон пока не упоминается на clojure.org, но - это , упомянутое в Радость Clojure , 2-е изд.Майкл Фогус, Крис Хаузер, май 2014, с.59. Там этот подход появляется в разделе «Ассоциативное деструктурирование» - ошибочно, поскольку эта основанная на индексах деструктуризация является лишь частным случаем «Ассоциативного деструктурирования», который в указанной книге называется «Деструктурирование с картой».1015 *

В любом случае, результаты неожиданные (Clojure 1.10.0):

Во всех случаях извлекайте значения по индексам 0 и 3.

Они работают, как и ожидалось:

(let [{firstthing 0, lastthing 3} [1 2 3 4]] [firstthing lastthing])
;=> [1 4]

(let [{firstthing 0, lastthing 3} (vec '(1 2 3 4))] [firstthing lastthing])
;=> [1 4]

Но в списке:

(let [{firstthing 0, lastthing 3} '(1 2 3 4)] [firstthing lastthing])
;=> [nil 4]

Почему там nil в позиции 0?

Аналогично:

(let [{firstthing 0, lastthing 3} (seq '(1 2 3 4))] [firstthing lastthing])
;=> [nil 4]

Но наС другой стороны:

(let [{firstthing 0, lastthing 3} (vec (seq '(1 2 3 4)))] [firstthing lastthing])
;=> [1 4]

Что здесь происходит?

Приложение:

(let [{firstthing 0, lastthing 3} { 1 2 3 4 } ] [firstthing lastthing])
;=> [nil 4]

... звучит разумно, как картаразрушено ассоциативно на самом деле {1 2, 3 4}.Таким образом, результат поиска не по позиции , а по целочисленной клавише (изменяя, так сказать, значение выражения под нашими ногами) будет в точности [nil 4].Что-нибудь, что не является вектором, выливаемым на карту первым?

(let [{firstthing 10, lastthing 30} (seq '(10 2 30 4))] [firstthing lastthing])
;=> [2 4]

Это, безусловно, выглядит так ...

(let [{firstthing 10, lastthing 30} (seq '(10 2 30 ))] [firstthing lastthing])
; Execution error (IllegalArgumentException) at user/eval367 (REPL:1).
; No value supplied for key: 30

О да.

Ответы [ 2 ]

5 голосов
/ 29 апреля 2019

Почему в позиции 0 ноль?

(let [{firstthing 0, lastthing 3} '(1 2 3 4)] [firstthing lastthing])
;=> [nil 4]

Если вы посмотрите на сгенерированный код для этого let:

user=> (macroexpand '(let [{firstthing 0, lastthing 3} '(1 2 3 4)] [firstthing lastthing]))
(let*
  [map__8012 (quote (1 2 3 4))
   map__8012 (if (clojure.core/seq? map__8012)
               (clojure.lang.PersistentHashMap/create (clojure.core/seq map__8012))
               map__8012)
   firstthing (clojure.core/get map__8012 0)
   lastthing (clojure.core/get map__8012 3)]
  [firstthing lastthing])

Вы видите, что в случае seq? он будет преобразован в карту.Итак:

user=> (def map__8012 (quote (1 2 3 4)))
#'user/map__8012
user=> (clojure.core/seq? map__8012)
true
user=> (clojure.lang.PersistentHashMap/create (clojure.core/seq map__8012))
{1 2, 3 4}

Для этого вы получаете nil для ключа 0 и 4 для ключа 3.

1 голос
/ 29 апреля 2019

Короткий ответ - ассоциативны только карты и векторы. Списки и последовательности не являются ассоциативными. Деструктуризация карты определяется только для ассоциативных структур:

(ns tst.demo.core
  (:use demo.core tupelo.core tupelo.test))

(dotest
  (let [mm {:a 1 :b 2}
        vv [1 2 3]
        ll (list 1 2 3)
        sv (seq vv)
        sl (seq ll) ]
    (spyx (associative? mm))
    (spyx (associative? vv))
    (spyx (associative? ll))
    (spyx (associative? sv))
    (spyx (associative? sl)) ) )

с результатом:

(associative? mm) => true
(associative? vv) => true
(associative? ll) => false
(associative? sv) => false
(associative? sl) => false

Clojure часто принимает (довольно резкое) отношение «мусор-в-мусор» вместо того, чтобы вызывать исключение, если вы вызываете функцию с недопустимыми аргументами.

В ClojureDocs.org есть даже предупреждение

Я бы сказал, что функции должны быть более пуленепробиваемыми по умолчанию (т. Е. Проверять значения / типы arg) и предоставлять только урезанную версию как clojure.core.raw/get или как-то похожее имя.


Обновление

Основываясь на ответе @ cfrick выше, мы видим происхождение ситуации. Поскольку последовательность не является картой, последовательность должна быть преобразована в карту, прежде чем может произойти разрушение. Следовательно, let предполагает, что вы предоставили последовательность пар ключ-значение, таких как [k1 v1 k2 v2 ...], которые должны быть преобразованы в карту как:

(apply hash-map [k1 v1  k2 v2  ...])

Если это не было вашим намерением, вам следует преобразовать последовательность в ассоциативную явно:

(zipmap (range) <the-seq>)

или, проще:

(vec <the seq>)

Или вы могли бы предоставить clojure значение ключа seq, которое, как предполагалось, присутствовало:

(list 0 1  1 2  2 3  3 4)

Обратите внимание, что если бы ваша последовательность была нечетной длины, такой как (list 1 2 3), вы бы получили ошибку при неявном преобразовании из seq в map.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...