Clojure中符号和变量的区别
我对Clojure中的符号和变量总是有点困惑。 例如,可以安全地说+是一个用来表示变量的符号,而这个变量指向一个值,这个值是一个可以添加数字的函数吗 那么,当我在REPL中输入“+”时,一步一步地发生了什么Clojure中符号和变量的区别,clojure,Clojure,我对Clojure中的符号和变量总是有点困惑。 例如,可以安全地说+是一个用来表示变量的符号,而这个变量指向一个值,这个值是一个可以添加数字的函数吗 那么,当我在REPL中输入“+”时,一步一步地发生了什么 符号被限定为名称空间,在本例中为clojure.core 然后在一些符号表中有+表示变量的信息 当计算此变量时,结果是函数值 见: 名称空间是从简单(非限定)符号到变量和/或类的映射。可以使用def或其任何变体将变量固定在名称空间中,在这种情况下,它们有一个简单的名称符号和对其包含名称空间的
关于第3步:我喜欢变量的定义,它们是值的名称的组合。符号是变量的名称,对其求值将得到其值。有一个符号+,您可以通过引用它来谈论它:
user=> '+
+
user=> (class '+)
clojure.lang.Symbol
user=> (resolve '+)
#'clojure.core/+
因此它解析为#'+,这是一个变量:
user=> (class #'+)
clojure.lang.Var
变量引用函数对象:
user=> (deref #'+)
#<core$_PLUS_ clojure.core$_PLUS_@55a7b0bf>
user=> @#'+
#<core$_PLUS_ clojure.core$_PLUS_@55a7b0bf>
在最后一个示例中,甚至可以忽略deref:
user=> (let [+ -] [(+ 1 2) (#'+ 1 2)])
[-1 3]
这是因为Var通过对自身调用deref来实现IFn(Clojure函数的接口),将结果转换为IFn并将函数调用委托给该函数
使用defn-定义私有函数时使用的可见性机制基于符号上的元数据。您可以通过直接引用Var绕过它,如上所述:
user=> (ns foo)
nil
foo=> (defn- private-function [] :secret)
#'foo/private-function
foo=> (in-ns 'user)
#<Namespace user>
user=> (foo/private-function)
java.lang.IllegalStateException: var: #'foo/private-function is not public (NO_SOURCE_FILE:36)
user=> (#'foo/private-function)
:secret
user=>(ns-foo)
无
foo=>(defn-private函数[]:secret)
#'foo/私有函数
foo=>(在ns的用户中)
#
user=>(foo/private函数)
java.lang.IllegalStateException:var:#'foo/private函数不是公共函数(无源文件:36)
user=>(#'foo/私有函数)
:秘密
这个答案与其他答案没有太大区别,只是不假设您最初希望学习几个新的函数和概念,只是为了了解发生了什么:
+
是位于clojure.core
中的一个符号,默认情况下代码可以访问该符号+
时,
clojure将尝试调用该函数(NullPointerException
,如果该变量恰好未指向函数)。如果作为参数提供给
另一个函数,该函数也可以调用它。那是
函数调用是如何工作的关于总结的进一步意见: 大多数或所有语言都使用符号表。作为一种有点动态的语言,Clojure使用了这个额外的间接层(符号→ 变量→ 功能,而不仅仅是符号→ 因此,动态重写哪个函数与哪个符号相关联更可行、更优雅,这有时是初学者好奇的来源 正如其他答案有点过分强调的那样,您可能会以其他方式执行一些花哨的操作,比如引用它(
'+
),以避免对其求值,或者甚至使用类
和/或解析
检查它,就好像您有兴趣验证它是什么(类
),或者它位于哪个命名空间(解析
)。您还可以通过var
或#
查看它所指向的var。如果您正在编写宏或非常喜欢实验,尤其是在repl中工作时,您通常会做这些奇特的事情;根据您编写宏的风格,您可能会在宏中引用很多内容
还有一个特别探索的人的精美插图:
作为一种灵活的语言,clojure公开了获取符号的api→ 变量→ 你可以自己走路。仅使用函数通常不会这样做,因为这显然是无聊和多余的,但这里可以用它来说明过程:
(deref (resolve '+))
也就是说,首先将符号解析为其Var,然后到达Var所指向的对象。这只是说明了获得函数(符号)的两步过程→ 变量→ 函数),它发生在幕后。我希望你避免读这额外的部分
TL;博士
原始问题的答案很简单:是的。我发现理解符号、函数、文字和变量之间的差异对于探索正在发生的事情是必要的
(def one(fn[]1))
⇒ <代码>#示例/一。它引用函数#函数[示例/一]
(def x one)
⇒ #示例/x
引用函数#函数[example/one]
(定义y'one)
⇒ <代码>#示例/y引用符号one
(def z#one)
⇒ #示例/z
引用var#示例/one
one
是一个解析为var#示例/one
的符号。var引用一个函数,#函数[example/one]
,它返回文本1
每个def
。var由#示例/x
中的#
语法表示。每个变量引用一个值
根据,符号在求值时解析为值、特殊形式或错误。因此可能会有点混乱,因为没有提到var:
(键入一)
⇒ <代码>示例$1(x型)
⇒ <代码>exa
user=> (ns foo)
nil
foo=> (defn- private-function [] :secret)
#'foo/private-function
foo=> (in-ns 'user)
#<Namespace user>
user=> (foo/private-function)
java.lang.IllegalStateException: var: #'foo/private-function is not public (NO_SOURCE_FILE:36)
user=> (#'foo/private-function)
:secret
(deref (resolve '+))