6

虽然我可能错误地解释了同音性的概念,但我将其理解为“代码即数据”。

所以,我可以这样写代码:

(def subject "world")
(def helo '(str "Hello " subject))

此时,helo只是数据,但可以像这样执行代码:

(eval helo)

返回“Hello world”。

我还可以继续helo当作数据来对待:

(first helo)
(count helo)

分别返回str3

到目前为止,一切都很好。但是,一旦我将代码包装在一个函数中,我似乎就失去了将代码视为数据的能力:

(defn helofn [subject]
  (str "Hello " subject))

我该如何分解helofn?看来我不能把它当作数据;如果我这样做:

(count helofn)

我得到一个例外:

java.lang.UnsupportedOperationException:此类型不支持计数:user$helofn

是否有另一种分解方法helofn,或者我只是对同音性期望过高?

4

4 回答 4

9

helofn定义数据,但您要对其进行评估(就像您明确评估helo列表一样)。如果您以与 相同的方式处理定义helo,那么它将保留数据,并适用于您要应用的任何转换:

(def helofndata '(defn helofn [subject]
                   (str "Hello " subject))

=> (second helofndata)
helofn
=> (eval helofndata)
#'user/helofn
于 2013-05-23T16:20:00.707 回答
6

defn只是一个

(macroexpand '(defn helofn [subject]
  (str "Hello " subject)))

(def helofn (clojure.core/fn ([subject] (str "Hello " subject))))

如果您定义了您定义helofn的方式helo,您将能够将其视为数据:

(def helofn '(fn [subject]
  (str "Hello " subject)))

现在您可以评估并调用此函数:

((eval helofn) "world")

并将其视为数据:

(count helofn)

但是,当您使用defn宏时,您会将helofn变量与已编译的函数相关联,而不是与它的代码相关联。

这不仅仅是功能。假设您hello使用以下代码定义:

(def helo (str "Hello " subject))

现在hello与“Hello world”字符串相关联,而不是与(str "Hello " subject)代码相关联。因此,现在无法获取构建此字符串的代码。

注意如果您想将 clojure 代码视为数据,您应该查看它的。传递给宏的任何代码都被视为数据,宏返回的任何数据都被视为代码。

于 2013-05-23T16:19:21.373 回答
3

Homoiconicity 是一个非常强大的概念,我不认为你对它有太多期望。

defn实际上是一个使用def特殊形式定义函数的宏,所以:

(defn sq [x]
  (* x x))

实际上相当于:

(def sq (fn ([x] (* x x))))

所以defn这里是接收 args sq [x] (* x x),然后构建 list (def sq (fn ([x] (* x x)))),将其作为宏的结果返回,然后进行评估。这一切都是通过defn宏对列表、映射、向量、符号等的操作来完成的。

在 Clojure 中您无法获得定义函数的原始符号列表这一事实与在 Clojure 中编译所有代码的事实有关。(fn [x] 1)这就是为什么在 REPL 中进行评估会返回类似#<user$eval809$fn__810 user$eval809$fn__810@10287d> . 但是,正如前面的回答中提到的,被评估的代码数据。

也许我在这方面走得太远了,但是如果你想为你定义的每个函数,创建它的数据,你可以通过创建你自己的自定义宏将它添加到它的元数据中。

这是这样一个宏的简单实现:

(defmacro defn* [x & body ]
  (let [form `'~&form
        x    (vary-meta x assoc :form form)]
    `(defn ~x ~@body)))
;=> #'user/defn*

(defn* sq [x]
  (* x x))
;=> #'user/sq

(:form (meta #'sq))
;=> (defn* sq [x] (* x x))

&form是一个隐式参数(与 &env 一起),包含调用宏的整个(未计算)形式(即编译器计算的数据)。

希望这会有所帮助,并且不会带来更多混乱。

于 2013-05-23T17:28:44.290 回答
1

它看起来像没有基于

获取 clojure 函数的代码

你能在 Clojure 中获取加载函数的“代码即数据”吗?

基本上,您可以从 .clj 文件中定义的函数获取源代码,但没有可靠的方法来检索仅从函数构建函数的数据结构。

编辑:另外我认为你对同质性期望过高。代码本身是数据,但是不能根据该代码发出的工件检索原始源代码是相当标准的。就像我有 2 一样,我无法知道它是由 (+ 1 1) 或 (- 4 2) 生成的,就像函数是通过调用 fn 来创建的一段数据作为代码。

于 2013-05-23T16:07:09.657 回答