1

必须为一个项目学习javascript,不要错过它!(尽管我认识到它的使用和影响的规模,不仅在桌面,而且在服务器和移动领域也是如此)。许多文献中重复的事情之一是“不要污染全局名称空间”以及许多合理的 理由

据我了解,scheme 是有原因的lisp-1,像这样等等

  1. Scheme中全局变量的约定是什么?
  2. 在使用全局变量和局部变量时有什么需要注意的陷阱吗?(尤其是那些本身不会抛出错误但会导致看不见的错误的那些)。
4

2 回答 2

3

对于您的第一个问题——Scheme 中没有针对全局变量的单一约定。有些人使用*foo*(原因与 CL 中的相同命名约定不同,正如我在上面的评论中所说),有些人使用大写字母(在区分大小写的方案中),但两者都不够流行,不能被视为约定。

第二个问题更微妙。是的,有一个单一的全局命名空间存在缺陷。这些陷阱与 Javascript和Common Lisp中的陷阱相同:事实上,加载随机代码会改变其他代码的含义——所以如果我想加载两个库,并且都定义了一些foo全局函数,那么一个这些库中的一个会破坏另一个。这不是一个新问题,有多种解决方法。

最明显的做法是避免使用不属于公共接口的全局变量。因此,例如,而不是

(define helper ...)
(define foo ...)

你写

(define foo
  (let ()
    (define helper ...)
    (define foo ...)
    foo))

如果您的界面中有多个功能,您可以执行类似的操作(例如,使用 Racketdefine-values或使用功能列表)。请注意,这具有双重保护:内部事物(例如helper,不会污染全局名称空间,因此不会破坏其他代码)-定义 a 的其他代码helper不会破坏此代码。这foo也包括,以防它递归地调用自己。

一个相关的技巧是在你希望它快的时候编写这样的代码:在第一个版本中,Scheme 编译器无法编译foo成快速循环,因为绑定可以在以后发生变化,但在第二个版本中,这样的优化是可能的。

当您没有模块系统时,另一种形式的防御性编程是获取对您的代码很重要的值。例如,您可以这样做:

(define foo
  (let ([+ +] [- -] [* *] [/ /] ... more ...)
    (define helper ...)
    (define foo ...)
    foo))

现在代码对算术运算有了自己的不可变绑定,这可以保护它在将来免受这些运算的更改,并允许安全地优化此代码中的算术运算。

所有这一切都是一种穷人的模块系统惯例,类似于(function() { ... })()Javascript 中的常见用法。但是,当然,适当的模块系统使这更加方便和良好。较新的 Javascript 版本和较新的 Scheme 版本都面临着某种模块系统的问题,而在 Scheme 中,趋势是只使用模块并避免load作为不可靠的 hack。

(因为我提到了 Common Lisp:你可以看到它有同样的问题,因为你可能会覆盖来自其他文件的全局变量函数。解决这个问题的方法是使用 CL 的包系统,它可以充当一种弱模块系统。它弱的原因是它为您提供了单独命名空间的便利,但命名空间仍然是全局的,这意味着优化仍然非常困难。换句话说,这些命名空间没有给您“封闭世界假设”传统的模块系统有,这意味着编译器仍然不能假设绑定不会改变。)

于 2013-04-30T06:26:19.063 回答
2

您可以在 Scheme 中使用 'lisp-2';只需使用一组一致的字符来偏好您的所有功能。您可以从重新绑定 RnRS 中定义的函数开始。像这样:

(define this-is-a-scheme-function-+ +)
(define this-is-a-scheme-function-call-with-current-continuation call-with-current-continuation)

然而,按照惯例,

(define tiasf-call/cc this-is-a-scheme-function-call-with-current-continuation)
于 2013-04-29T20:15:39.727 回答