Почему этот код использует несколько типов кавычек - PullRequest
2 голосов
/ 28 марта 2019

От: Радость Clojure

(defn contextual-eval [ctx expr]
  (eval
   `(let [~@(mapcat (fn [[k v]] [k `'~v]) ctx)]
      ~expr)))

Теперь, если я позвоню

(contextual-eval '{a 1 b 2} '(+ a b))

Он возвращает 3 , как и ожидалось.

Нопочему использование `'~ v вместо просто v ?

Если я удаляю eval .., чтобы функция выглядела так

(defn contextual-eval [ctx expr]
  `(let [~@(mapcat (fn [[k v]] [k v]) ctx)]
    ~expr))

, а затем позвоните

(eval (contextual-eval '{a 1 b 2} '(+ a b)))

Он по-прежнему возвращает 3 , как и ожидалось.

Так что я не уверен, почему `'~ v используется, когда eval находится внутри тела функции.

1 Ответ

0 голосов
/ 28 марта 2019

Это опечатка, я думаю.Этот код показывает, что происходит и что должно произойти:

(defn contextual-eval [ctx expr]
  (spy :01 ctx)

  (spy :02 (mapcat (fn [[k v]] [k `'~v]) ctx))
  (spy :03 `(let [~@(mapcat (fn [[k v]] [k `'~v]) ctx)] ~expr) )

  (spy :02a (mapcat (fn [[k v]] [k v]) ctx))
  (spy :03a `(let [~@(mapcat (fn [[k v]] [k v]) ctx)] ~expr) )

  (eval `(let [~@(mapcat (fn [[k v]] [k v]) ctx)] ~expr) )
  )

(dotest
  (nl)
  (spy :10
    (contextual-eval '{a 1 b 2}
      '(+ a b)))
  (nl)
  (spy :20
    (contextual-eval '{a 1
                       b (+ a 3)}
      '(+ a b)))
  )

Мы получаем результаты для первого запуска:

:01 => {a 1, b 2}

:02 => (a (quote 1) b (quote 2))
:03 => (clojure.core/let [a (quote 1) b (quote 2)] (+ a b))

:02a => (a 1 b 2)
:03a => (clojure.core/let [a 1 b 2] (+ a b))

:10 => 3

Второй запуск:

:01 => {a 1, b (+ a 3)}

:02 => (a (quote 1) b (quote (+ a 3)))
:03 => (clojure.core/let [a (quote 1) b (quote (+ a 3))] (+ a b))

:02a => (a 1 b (+ a 3))
:03a => (clojure.core/let [a 1 b (+ a 3)] (+ a b))

:20 => 5

преобразуя "` '~ v "в просто" v ", он делает то, что должен.


PS Хотя в JoC много хороших вещей, он очень продвинут и должен больше походить на вашу пятую книгу Clojure.не первая (это была моя первая книга на Clojure, и я очень растерялся).Вы бы хорошо обслужили, начав с:

  • Получение Clojure
  • Living Clojure
  • Clojure для Brave & True

Обновление:

Я бы сделал это немного по-другому.Там действительно нет необходимости для mapcat.Обратите внимание на предложение (:use ...) в форме (ns ...):

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

(defn contextual-eval [ctx expr]
  (spy :01 ctx)
  (spy :02 (seq ctx))
  (spy :03 (vec (apply concat (seq ctx))))
  (spy :04 `(let ~(vec (apply concat (seq ctx)))
              ~expr))
  (eval `(let ~(vec (apply concat (seq ctx)))
           ~expr)))

(dotest
  (nl)
  (spy :100
    (contextual-eval '{a 1 b 2}
      '(+ a b)))
  (nl)
  (spy :200
    (contextual-eval '{a 1
                       b (+ a 3)}
      '(+ a b)))
  )

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

:01 => {a 1, b 2}
:02 => ([a 1] [b 2])
:03 => [a 1 b 2]
:04 => (clojure.core/let [a 1 b 2] (+ a b))
:100 => 3

:01 => {a 1, b (+ a 3)}
:02 => ([a 1] [b (+ a 3)])
:03 => [a 1 b (+ a 3)]
:04 => (clojure.core/let [a 1 b (+ a 3)] (+ a b))
:200 => 5

и в project.clj вам нужно добавить зависимость к Tupeloбиблиотека

[tupelo "0.9.138"]

Обновление № 2:

@ amalloy правильно, что его пример работает.Однако затем происходит сбой в исходном примере:

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

(defn contextual-eval [ctx expr]
  (println :01 ctx)
  (println :02 (seq ctx))
  (println :04a (map (fn [[k v]] [k `'~v]) ctx))
  (println :04b  (mapcat (fn [[k v]] [k `'~v]) ctx))
  (println :05 `(let [~@(mapcat (fn [[k v]]
                                  [k `'~v]) ctx)]
                  ~expr))
  (spy :99 (eval
             `(let [~@(mapcat (fn [[k v]]
                                [k `'~v]) ctx)]
                ~expr))))

(dotest
  (newline)
  (throws?
    (println :100
      (contextual-eval '{a 2, b 5}
        '(- (first a) b))))

  (newline)
  (is= -4
    (let [a (range 5)
          b (last a)]
      (spy :300
        (contextual-eval {'a a, 'b b}
          '(- (first a) b)))))  )

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

:01 {a 2, b 5}
:02 ([a 2] [b 5])
:04a ([a (quote 2)] [b (quote 5)])
:04b (a (quote 2) b (quote 5))
:05 (clojure.core/let [a (quote 2) b (quote 5)] (- (first a) b))

:01 {a (0 1 2 3 4), b 4}
:02 ([a (0 1 2 3 4)] [b 4])
:04a ([a (quote (0 1 2 3 4))] [b (quote 4)])
:04b (a (quote (0 1 2 3 4)) b (quote 4))
:05 (clojure.core/let [a (quote (0 1 2 3 4)) b (quote 4)] (- (first a) b))
:99 => -4
:300 => -4

Первая попытка завершается с исключением:

  actual: java.lang.IllegalArgumentException: Don't know how to create ISeq from: java.lang.Long

Итак,Я бы сказал, что что-то такое хрупкое, возможно, не лучший пример для преподавания цитирования / отмены цитирования в Clojure.Кроме того, если вы не пишете компилятор или аналогичный код, такой код редко требуется.И, если вам когда-нибудь понадобится что-то такое запутанное и подверженное ошибкам, я бы предложил добавить дополнительные проверки, разложить шаги и показать промежуточные результаты, чтобы помочь читателю понять и отладить сопровождающим.

...