我最近在REPL ---
学到了(感谢技术)此操作失败:
user=> (:require [clojure.set :as set])
java.lang.ClassNotFoundException: clojure.set (NO_SOURCE_FILE:24)
然后成功:
user=> (require '[clojure.set :as cs])
nil
加载clojure.set类。
上下文:前一行是从命名空间源文件中复制的。
我的主要问题是:我们所做的改变是什么,通过交换:和'字符,现在允许后一个命令成功?
我的第二个问题是,一般来说 - 与在普通的clojure源文件中做事相比,在REPL中做什么的准则是什么?这里假设我们可以加载我们的repl从LEININGEN项目的根目录开始,所以至少这些jar将在依赖项子目录的磁盘上可用。
答案 0 :(得分:50)
我会从高级到你的特定问题:
REPL或Read-Eval-Print循环是LISP设计的核心:
因此,当您在REPL中输入文本时,它将执行以下每个步骤来处理您的输入并将输出返回到您的终端。
首先是一些clojure读者形式。这将非常简短,我建议您read或关注它(part 1,part 2)。
clojure中的符号是可以表示特定值(如变量)的形式。符号本身可以作为数据传递。它们类似于c中的指针,只是没有内存管理的东西。
前面带冒号的符号是关键字。关键字就像符号一样,但关键字的值总是自身 - 类似于字符串或数字。它们与Ruby的符号(也以冒号前缀)相同。
表单前面的引用告诉评估者按原样保留数据结构:
user=> (list 1 2)
(1 2)
user=> '(1 2)
(1 2)
user=> (= (list 1 2) '(1 2))
true
虽然引用可以应用于不仅仅是列表,但它主要用于列表,因为clojure的求值程序通常会将列表作为类似函数的调用来执行。使用'
是报价宏的缩写:
user=> (quote (1 2)) ; same as '(1 2)
(1 2)
Quoting基本上指定要返回的数据结构,而不是要执行的实际代码。所以你可以引用符号的符号。
user=> 'foo ; not defined earlier
foo
引用是递归的。因此,所有内部数据也被引用:
user=> '(foo bar)
(foo bar)
要在不引用的情况下获取(foo bar)
的行为,您可以评估它:
user=> (eval '(foo bar)) ; Remember, foo and bar weren't defined yet.
CompilerException java.lang.RuntimeException: Unable to resolve symbol: foo in this context, compiling:(NO_SOURCE_PATH:1)
user=> (def foo identity)
#'user/foo
user=> (def bar 1)
#'user/bar
user=> (eval '(foo bar))
1
引用还有很多,但这超出了这个范围。
至于需要陈述,我假设你以下列形式找到了前者:
(ns my.namespace
(:require [clojure.set :as set]))
ns
是macro,它会将:require表达式转换为您描述的后一种形式:
(require '[clojure.set :as set])
还有一些命名空间工作。在REPL中询问ns的文档时描述了基础知识。
user=> (doc ns)
-------------------------
clojure.core/ns
([name docstring? attr-map? references*])
Macro
Sets *ns* to the namespace named by name (unevaluated), creating it
if needed. references can be zero or more of: (:refer-clojure ...)
(:require ...) (:use ...) (:import ...) (:load ...) (:gen-class)
with the syntax of refer-clojure/require/use/import/load/gen-class
respectively, except the arguments are unevaluated and need not be
quoted. (:gen-class ...), when supplied, defaults to :name
corresponding to the ns name, :main true, :impl-ns same as ns, and
:init-impl-ns true. All options of gen-class are
supported. The :gen-class directive is ignored when not
compiling. If :gen-class is not supplied, when compiled only an
nsname__init.class will be generated. If :refer-clojure is not used, a
default (refer 'clojure) is used. Use of ns is preferred to
individual calls to in-ns/require/use/import:
通常,请勿在REPL中使用ns
,只需使用require
和use
函数即可。但是在文件中,使用ns
宏来执行这些操作。
答案 1 :(得分:13)
区别在于require
是用于导入代码的函数,而:require
是关键字。
请记住将关键字用作函数时会发生什么:
=> (type :require)
clojure.lang.Keyword
=> (:require {:abc 1 :require 14})
14
它在地图中看起来很自在。因此,当您将[clojure.set :as set]
传递给关键字时,它会尝试将其评估为向量,并且因为它不知道clojure.set
是什么而失败。 Clojure docs说:
关键字为一个参数(一个映射)的invoke()实现IFn 可选的第二个参数(默认值)。例如(:mykey my-hash-map:none)表示与(get my-hash-map:mykey:none)相同。
您可能对the ns
macro感到困惑:
(ns foo.bar
(:refer-clojure :exclude [ancestors printf])
(:require (clojure.contrib sql sql.tests)) ;; here's :require!
(:use (my.lib this that))
(:import (java.util Date Timer Random)
(java.sql Connection Statement)))
答案 2 :(得分:2)
ns
宏:当您输入时:
(ns some-great-ns
:require my-form)
您使用 :require
引用,在其中声明您希望使用给定命名空间中的内容。相当于写:
(in-ns 'some-great-ns)
(require 'my-form)
请注意,在 ns
形式中(与 in-ns
函数调用不同),您不必用 '
引用您的符号。您永远不必在 ns
内引用符号。
require
函数如前所述,可以在某个给定的命名空间中运行:(require 'some-great-ns)
,以便您可以使用它。要使用它,您必须使用完全限定名称,除非您在需要命名空间后立即使用:refer
function: (refer 'some-great-ns)
。
您可以将这两个功能合二为一:(use 'some-great-ns)
。现在你不需要写:(some-great-ns/my-form
)。简单地说:my-form
。
当然,您也可以在宏引用和函数中使用 :as
、:exclude
、:only
和 :rename
关键字。
(:require)
引用中要求多个库,如下所示:(ns my-great-namespace.core
(:require [some-other-ns.a.b :as ab]
[some-other-other-ns.c.d :as cd]))
在 function
处应该写 2 行:
(in-ns my-great-namespace.core)
(require 'some-other-ns.a.b :as 'ab)
(require 'some-other-other=ns.c.d :as 'cd)
require
引用还允许您引用名称,例如:(ns my-great-namespace.core
(:require [some-other-ns.a.b :refer [some-func]]))
您应该在函数中的哪个位置:
(in-ns my-great-namespace.core)
(require 'some-other-ns.a.b)
(refer 'some-other-ns.a.b :only ['some-func])