Language agnostic 除了Logo和emacslisp之外,还有哪些纯动态范围语言?

Language agnostic 除了Logo和emacslisp之外,还有哪些纯动态范围语言?,language-agnostic,lisp,programming-languages,language-theory,logo-lang,Language Agnostic,Lisp,Programming Languages,Language Theory,Logo Lang,动态范围语言的一些例子是什么?选择这种设计的原因是什么?是因为它很容易实现吗?好吧,有很多网站都在讨论赞成和反对的观点,所以我不打算去那里 XSLT是一种有趣的语言,它的一些特性与动态范围有些相似;尽管XSLT的模板和变量等都是词汇范围的,但XSLT当然都是关于XML的,而且当前在XML树中的位置是“动态范围的”上下文节点是全局的,因此XPath表达式的计算不是根据XSLT的词法范围,而是根据它的动态计算。Mathematica是另一种通过块构造动态确定范围的语言。这在使用公式时实际上非常有用。

动态范围语言的一些例子是什么?选择这种设计的原因是什么?是因为它很容易实现吗?

好吧,有很多网站都在讨论赞成和反对的观点,所以我不打算去那里


XSLT是一种有趣的语言,它的一些特性与动态范围有些相似;尽管XSLT的模板和变量等都是词汇范围的,但XSLT当然都是关于XML的,而且当前在XML树中的位置是“动态范围的”上下文节点是全局的,因此XPath表达式的计算不是根据XSLT的词法范围,而是根据它的动态计算。

Mathematica是另一种通过
构造动态确定范围的语言。这在使用公式时实际上非常有用。它允许你写像这样的东西

 In[1]:= expr = a*t^2 + b*t+ c;

 In[2]:= Block[{a = 1, b = -1, c = 2}, Table[expr, {t, 5}]]
 Out[2]= {2, 4, 8, 14, 22}
如果像
a
t
这样的变量是在词汇范围内的,那么这根本不起作用。它与Mathematica的规则重写系统配合得特别好,如果没有现有的变量定义,那么除其他外,该系统将不计算变量(作为符号表达式)

Mathematica可以通过
模块
构造来伪造词法范围,但这实际上是用新的、据称是唯一的符号来重写表达式(如果预测下一个唯一符号是什么,可能会引起冲突,这在大多数情况下很容易)。这意味着

Module[{x = 4}, 
  Table[x * t, {t, 5}]]
将变成这样:

Block[{x$134 = 4},
  Table[x$134 * t, {t, 5}]
Emacs Lisp在它的一个库中有一个名为
lexicallet
的构造(实际上是一个Lisp宏),它使用完全相同的技巧来伪造词法范围

编译语言时,真正的词法作用域具有性能优势,这是ELisp或Mathematica的假词法无法实现的,因为您需要动态变量与其当前值之间的某种映射,这意味着进行查找(通过哈希表或属性列表或其他方式)以及附加的间接层


编辑:如果您只有词法变量,则可以通过在进入作用域时存储全局词法变量的原始值,并确保在退出作用域时恢复旧值来伪造动态作用域。为了确保这一点,您需要类似于Lisp的
UNWIND-PROTECT
finally
块的东西。我也看到过用C++析构函数来完成这一点,主要是作为一个练习。p> 动态范围语言更容易实现。要访问不在当前activaiton记录/堆栈框架中的变量,只需遵循控制链接即可。这样就不需要静态/词法访问链接,从而使堆栈帧更小

动态变量在运行时可能“不可预测”,因为需要知道实际堆栈帧的顺序,才能知道将使用哪个变量。仅通过查看代码的静态结构无法获得此信息。如果程序的实际调用图在实现时不容易预测,则很容易被发现。这就是为什么今天大多数语言都有静态作用域(然而,大多数异常系统都是动态的,因为这是最实用的)

然而,在某些情况下,动态范围变量非常有用。例如,在重定向输出时,可以使用动态变量为本地代码和从此处调用的所有代码设置标准输出

(let ((*standard-output* *some-other-stream*))
 (stuff))
在这个常见的lisp示例(来自Seibel)中,标准输出在let表单期间绑定到另一个流(在其封闭的paren内)。当执行离开let时,它会回到之前的状态。请参阅Peter Seibels免费的优秀书籍《实用的通用Lisp》,以获得良好的讨论。用塞贝尔自己的话来说:

动态绑定使全局变量更易于管理,但需要注意的是,它们仍然允许远距离操作。绑定一个全局变量会产生两种不同的效果——它可以改变下游代码的行为,并且它还提供了一种可能性,即下游代码将为堆栈上更高的绑定分配一个新值。仅当需要利用其中一个或两个特性时,才应使用动态变量


使用口译员更容易实现动态范围。大多数早期的Lisp解释器都使用动态范围。几年后,人们发现词法作用域具有优势,但最初主要是在Lisp编译器中实现的。出现了几个实现,它们在解释代码中实现了动态范围,在编译代码中实现了词法范围。有些提供了一种特殊的语言构造来提供闭包。Scheme和Common Lisp等Lisp方言要求解释代码和编译代码之间没有区别,因此基于解释的实现也必须实现词法范围

早期的Smalltalk实现实现了动态范围。各种Lisp方言实现都实现了动态范围(INTERISP、UCI Lisp、Lisp Machine Lisp、MacLisp等)


过去20年来,几乎所有新的Lisp方言都默认使用词汇范围,甚至完全使用词汇范围。一些出版物详细描述了如何使用词法作用域实现Lisp,因此没有理由不使用词法作用域。

所有shell语言(bash、ksh等)都使用动态作用域

曾几何时,我所知道的所有Lisp版本都有动态范围。elisp是当时最后的幸存者之一,
壳是一个显著的例外