【问题标题】:Clojure Built in Function to access the entire recordClojure 内置函数来访问整个记录
【发布时间】:2013-11-24 14:05:21
【问题描述】:
(defrecord Sample (x y))

(def check (Sample. 1 2))

(:x check) ;returns 1

如果我收到 (:x check) 作为函数的参数,有没有办法访问检查?或者,换句话说,返回

#:user.Sample{:x 1, :y 2}

【问题讨论】:

    标签: clojure record


    【解决方案1】:

    不,如果将函数 (:x check) 作为参数传递,那么该值在进入函数之前已经被评估,你只会收到一个 1 作为值,你不能检索它来自的记录。

    如果你需要函数内部的记录,为什么不传递check作为参数呢?

    【讨论】:

    • 如果我通过 '(:x check) 代替;还是没有办法吗?例如,我知道“#_”阅读器宏可以跳过评估。 (concat #_(list (+ 1 2)) (list (+ 1 1))) 将返回 (2) 而不是 (3 2)。对于我所说的问题,clojure 中是否有类似的东西?
    • (我知道我可以通过检查,但如果我通过评估,我只是想知道是否可以在某种意义上“回溯”)
    • 如果您传递'(:x check),那将只是函数内部的符号列表,无法“回溯”check 是调用函数之前的记录这一事实
    • 由于checkVar,您可以使用resolve/deref 来获得它的值 - (deref (resolve 'check)) => #user.Sample{:x 1, :y 2}。我在下面的答案中包含了更多信息。
    【解决方案2】:

    正如Óscar 所述,(:x check) 不起作用,因为它的结果是1,而'(:x check) 不起作用,因为它的结果是一个包含关键字:x 和符号check 的列表。

    但是,您可以使用 list 函数来代替引号:

    (defn receiver [arg]
      (map class arg))
    
    ;; With a quoted list it receives the symbol `check` rather
    ;; than the Sample record
    (receiver '(:x check))
    ;=> (clojure.lang.Keyword clojure.lang.Symbol)
    
    ;; But this way it does receive the Sample
    (receiver (list :x check))
    ;=> (clojure.lang.Keyword user.Sample)
    

    并且可以评估(list :x check)

    (eval (list :x check))
    ;=> 1
    
    (defn receiver [arg]
      (str "received " arg "; eval'd to: " (eval arg)))
    
    (receiver (list :x check))
    ;=> "received (:x #user.Sample{:x 1, :y 2}); eval'd to: 1"
    

    quotelist 行为如此不同的原因是 quote 没有评估它的论点。而且,当一个列表被引用时,这种效果是递归的:列表中的任何项目都不会被评估。

    还有另一种类型的引用,称为语法引用或反引号(在 Clojure.org 页面上描述了 reader),它允许您选择性地取消引用(即评估)项目。

    (require '[clojure.pprint])
    
    (let [n 1
          x :x
          c check]
      (clojure.pprint/pprint
       (vector `(n x c)
               `(~n x c)
               `(~n ~x c)
               `(~n ~x ~c))))
    

    打印:

    [(user/n user/x user/c)
     (1 user/x user/c)
     (1 :x user/c)
     (1 :x {:x 1, :y 2})]
    

    而且,实际上,我撒了一点谎。在这种特殊情况下,您实际上可以使用'(:x check)resolve 将返回与符号关联的 Varderef(或其@ 读取器宏)将为您获取 Var 的值。

    (resolve 'check)
    ;=> #'user/check
    
    (deref (resolve 'check))
    ;=> #user.Sample{:x 1, :y 2}
    
    (defn resolve-and-deref-symbols [form]
      (map (fn [x] (if (symbol? x)
                     @(resolve x)
                     x))
           form))
    
    (defn receiver [arg]
      (str "received " arg "; eval'd to: " (eval (resolve-and-deref-symbols arg))))
    
    (receiver '(:x check))
    ;=> "received (:x check); eval'd to: 1"
    

    我没有直接提到它,因为虽然它在这个例子中很容易工作,但它根本不适合一般情况。 (例如,它不适用于本地,处理命名空间和嵌套数据结构会很痛苦)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多