首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >包与命名空间与模块

包与命名空间与模块
EN

Stack Overflow用户
提问于 2016-12-25 13:09:28
回答 2查看 641关注 0票数 6

根据http://www.phyast.pitt.edu/~micheles/scheme/scheme29.html

值得一提的是,如果您在实践中使用包系统(如通用Lisp)或命名空间系统(如Clojure),则变量捕获变得非常罕见。而在使用模块系统的Scheme中,卫生是必不可少的.

这里使用的术语在包系统、命名空间系统和模块系统之间有什么区别?

请注意,这并不是关于Lisp-1和Lisp-2 (链接文档分别讨论的)。我猜这可能与这种方式有关,在通用Lisp中,尝试在两个不同的包中使用相同的符号可以得到两个同名的不同符号。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-12-27 12:04:59

我认为包系统和模块系统的一个共同区别是包系统处理将源文本映射到名称,而模块系统处理将名称映射到意义。(我不知道名称空间系统是什么,但我怀疑它是一个包系统,可能没有头等舱包,甚至是头等舱名称?)

在Lisp的上下文中,名称是符号,所以当您读取具有符号语法的东西时,包系统控制您得到的符号(具体来说,它在哪个包中),而在模块系统中,总是得到相同的符号,但它的值取决于模块状态。

下面是一个使用CL包系统和Racket模块系统的例子,说明这些东西是如何区别的。请注意,我是一个CL的人:我更了解CL包系统,比我更了解球拍的模块系统或球拍/方案宏。

假设我想定义某种符号代数系统,我希望能够使用像(+ a b)这样的语法,当ab可能是cl:+不能理解的东西时,比如多项式之类的。

好吧,我可以在CL中使用包定义来实现这一点,比如:

代码语言:javascript
复制
(defpackage :org.tfeb.symalg
  (:use)
  (:export "+" "-" "*" "/"))

(let ((p (find-package :org.tfeb.symalg)))
  (do-external-symbols (s (find-package :cl))
    (ecase (nth-value 1 (find-symbol (symbol-name s) p))
      ((nil)
       (import s p)
       (export s p))
      ((:external)
       nil)
      ((:inherited :internal)
       (error "package botch")))))

(defpackage :org.tfeb.symalg-user
  (:use :org.tfeb.symalg))

(请注意,在现实生活中,你显然会用一种声明式和更灵活的方式写一个宏--做上面的头发:实际上,互联网上的一些人写了一个名为“管道”的系统来完成这个任务,总有一天他可能会重新发布它。)

它所做的是创建一个包,org.tfeb.symalg,它类似于cl,除了某些特定的符号是不同的,还有一个包org.tfeb.symalg-user,它使用这个包而不是cl。在那个包中,(+ 1 2)表示(org.tfeb.symalg:+ 1 2),而(car '(1 . 2))表示(cl:car '(1 . 2))

代码语言:javascript
复制
(defmethod foo (a)
  (:method-combination +))

也意味着

代码语言:javascript
复制
(defmethod foo (a)
  (:method-combination org.tfeb.symalg:+))

现在我遇到了一些麻烦:任何我想使用符号+作为符号的地方,我都必须键入cl:+。(这个具体的例子很容易理解:我只需要为org.tfeb.symalg:+定义一个方法组合,而且我可能希望在任何情况下都这样做,但还有其他情况。)

这使得在我想使用作为语言一部分的名称(符号)作为符号的情况下,这样做“重新定义语言的比特”是一件很痛苦的事情。

比较球拍:以下是球拍中的一个小模块定义,它提供(或实际上不提供)一些算术符号的变体版本):

代码语言:javascript
复制
#lang racket

(provide
 (rename-out
  (plus +)
  (minus -)
  (times *)
  (divide /)))

(define (plus . args)
  (apply + args))

...

(define plus-symbol '+)

这就是说,如果使用这个模块,那么符号+的值就是模块中符号plus的值,依此类推。但这个符号是同一个符号。如果您使用模块,例如(eq? '+ plus-symbol),您可以轻松地检查它,它将返回#t:在输入+时所得到的符号没有什么有趣的地方,所有这些符号都是从这些符号到它们的值的映射。

所以这要好得多:如果Racket有一个CLOS风格的对象系统(它可能有大约6个,其中一些可能是工作的),那么+方法组合就能正常工作,通常任何关心符号的东西都会按照你的意愿工作。

除了您在CL中最常见的操作符号为符号的事情之一是宏。如果你试着用CL方法来处理球拍中的宏,到处都是头发。

在CL中,这种方法通常如下所示:

  1. 包在某种程度上得到了定义,在系统的编译、加载和评估过程中这些定义是相同的。
  2. 宏被定义。
  3. 该程序是通过扩展宏来编译的。
  4. 程序被加载并运行。

这一切都很好:如果我的宏是在+意为org.tfeb.symalg:+的情况下定义的,那么,如果它的扩展涉及到+,那么它实际上涉及到org.tfeb.symalg:+,而且只要在编译时(编译时和宏扩展时间相互交织在一起)和在运行时(定义在那里)就会存在。

但在球拍中,这不是一回事:如果我写了一个宏,那么我就知道符号+就是符号+。但是,根据模块状态的不同,+的含义在不同的时间可能完全不同。这可能意味着,例如,在编译时,+意味着Racket的本机加法函数,所以编译器可以将其优化到底层,但是在运行时它可能意味着其他的东西,因为现在的模块状态是不同的。符号+没有包含足够的信息来知道它应该意味着什么。

如果您还考虑到Scheme的人们真正关心以一种干净的方式分类这类事情的信息,并且肯定不满意CL方法对宏的‘停止如此努力的思考&只是使用gensyms,这一切都会很好’,没有一个方案是Lisp-1的帮助,你可以看到这里有一些相当重要的问题需要解决。

票数 5
EN

Stack Overflow用户

发布于 2016-12-25 22:36:32

通用Lisp有一个用于符号的包系统。

符号

  • cl-user::create;包CL-USER中的符号CREATE
  • cl-user:create;在包CL-USER中导出符号CREATE
  • create;在读取时当前包中的符号CREATE
  • :create;包KEYWORD中的符号CREATE
  • #:create;符号CREATE,不在任何包中

包CL-USER中的示例

让我们告诉读者,CL-USER是当前的包:

代码语言:javascript
复制
(in-package "CL-USER")

函数CL-USER::CREATE

代码语言:javascript
复制
(defun create (x)        ;  CL-USER::CREATE
  (list :list x))

使用上述函数创建代码的宏。

代码语言:javascript
复制
(defmacro m (x)
  `(create ,x))    ; here we use CL-USER::CREATE

如果我们停留在这个包中,我们可以定义一个局部函数,它将隐藏上面的全局函数。

代码语言:javascript
复制
(defun foo ()
  (flet ((create (x)             ; CL-USER::CREATE
           (vector :vector x)))
    (m (create 1))))             ; CL-USER::CREATE

所以(m (create 1))(cl-user::create (cl-user::create 1))。两者都使用本地函数。

包栏中的示例

现在,我们可以将我们的FOO函数移到另一个包BAR中,我们将在这里定义它:

代码语言:javascript
复制
(defpackage "BAR"
  (:use "COMMON-LISP")
  (:export "FOO")
  (:import-from "CL-USER" "M"))

让我们告诉读者使用这个包BAR作为当前包:

代码语言:javascript
复制
(in-package "BAR")

现在,如果我们定义类似于上面的函数FOO

代码语言:javascript
复制
(defun foo ()
  (flet ((create (x)            ;   BAR::CREATE
           (vector :vector x)))
    (m (create 1))))            ;   BAR::CREATE

所以(m (create 1))(cl-user::create (bar::create 1))。这意味着用户代码没有隐藏宏使用的函数。

但是我们可以显式地使用CL-USER::CREATE符号:

代码语言:javascript
复制
(defun foo ()
  (flet ((cl-user::create (x)            ;   CL-USER::CREATE
           (vector :vector x)))
    (m (cl-user::create 1))))            ;   CL-USER::CREATE

所以(m (create 1))(cl-user::create (cl-user::create 1))

使用非内联符号的

通用Lisp还提供了没有在包中嵌入的符号。一个特殊的读取器宏符号使得在表达式中多次引用这样的符号成为可能:#1=是标记,#1#引用标记对象。

代码语言:javascript
复制
(defun foo ()
  (flet ((#1=#:create (x)             ; #:CREATE
           (vector :vector x))) ;
    (m (#1# 1))))                     ; #:CREATE from above

因此,(m (#1# 1))(cl-user::create (#:create 1)),其中`#:create符号是上面引用的非内部符号。

使用计算符号

通用Lisp还允许使用#.读取器宏在读取时运行代码。

代码语言:javascript
复制
(defun foo ()
  #.(let ((create-symbol (gensym "CREATE-")))
      `(flet ((,create-symbol (x)              ; #:CREATE-NNN
                (vector :vector x))) 
         (m (,create-symbol 1)))))             ; #:CREATE-NNN from above

它可能会创建类似于以下内容的代码:

代码语言:javascript
复制
(DEFUN FOO ()
  (FLET ((#:CREATE-815 (X)                 ; #:CREATE-815
           (VECTOR :VECTOR X)))
    (M (#:CREATE-815 1))))                 ; #:CREATE-815 from above

所以(m (#:CREATE-815 1))(cl-user::create (#:create-815 1)),其中#:create-815符号是从上面引用的符号。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41321390

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档