从REBOL/Core Users Guide和What is Red,我已经学会了,无论雷博尔和Red使用的定义范围界定。
从导,我知道它是静态的作用域的一种形式,并且也被称为runtime lexical scoping,并且是a dynamic form of static scoping that depends on context definitions“当其上下文被定义被确定的变量的范围”。
我知道,在COM-SCI,有两种形式的范围界定:词法范围(静态范围)和动态作用域。这个定义上作用域搞糊涂了。
那么,什么是定义性的作用域?
雷博尔却没有在所有的作用域。
让我们这样的代码:
rebol []
a: 1
func-1: func [] [a]
inner: context [
a: 2
func-2: func [] [a]
func-3: func [/local a] [a: 3 func-1]
]
因此,随着加载的代码,如果有雷博尔词汇范围,这是你会看到什么:
>> reduce [func-1 inner/func-2 inner/func-3]
== [1 2 1]
这将是因为func-1
使用a
从外范围,通过a
使用的func-2
是从内部范围,func-3
调用func-1
,它仍然使用a
从那里它无论什么在func-3
定义外部范围。
如果雷博尔有动态作用域,这是你会看到什么:
>> reduce [func-1 inner/func-2 inner/func-3]
== [1 2 3]
这将是因为func-3
重新定义a
,然后调用func-1
,它只是用a
的最近活动的定义。
现在对于雷博尔,你得到的第一个结果。但雷博尔没有词法范围。所以为什么?
雷博尔假货吧。下面是它如何工作的。
在编译语言,你有作用域。由于编译器经过该文件,它使当前范围的轨迹,那么当它看到成为当前范围嵌套范围。对于词法作用域,编译器保持到外范围的引用,然后查找当前范围由以下的链接到外部范围没有定义的话,直到它找到字,或没有。动态范围的语言做类似的事情,但在运行时,会调用堆栈。
雷博尔没有做任何的是什么;特别是它没有被编译,它的建成,在运行时。你认为的代码是什么实际上是一个数据,文字,数字和这样的块。这些话是在他们的指针称为“绑定”的数据结构。
当第一次加载该脚本中的所有脚本的话都添加到脚本的环境对象(我们不当称之为“上下文”,虽然它不是)。当正在收集的话,脚本数据被改变。在脚本的“上下文”中发现的任何字被链接到“背景”,或“绑定”。这些绑定意味着你可以按照一个链接,并获得其中存储了字的值对象。它的真快。
然后,一旦这样做了,我们开始运行该脚本。然后我们得到这个位:func [] [a]
。这是不是一个真正的声明,这是一个函数命名func
这需要一个规范块和一个代码块,并使用它们来构建一个函数的调用。这个函数也都有自己的环境对象,但在功能的规范申报的话。在这种情况下,有在规格没有的话,那么它是一个空的对象。然后将码块被绑定到该对象。但是,在这种情况下,存在这样的对象没有a
,所以没有做是为了a
,它保持绑定它已经从当它被束缚前了。
这同样适用于context [...]
电话 - 是的,这是一个不恰当地命名context
功能,通过调用make object!
建立一个对象的调用。所述context
函数接受数据的一个块,并且它搜索组字(这些东西与后冒号,像a:
),然后建立与它的那些字的对象,那么它结合所有的话在该块中,所有该嵌套块到处于上述目的,在这种情况下a
,func-2
和func-3
词语的这意味着,a
在代码块都有自己的绑定改变,以指向对象来代替。
当func-2
被定义,在其代码块中的a
的结合不覆盖。当func-3
被定义,它具有在其规范的a
,所以a:
已与其结合覆盖。
关于这一切有趣的是,有没有任何范围可言。首先a:
和a
的代码体的func-1
只能绑定一次,所以他们保持第一装订。在a:
的代码块的inner
和a
年代func-2
绑定的两倍,因此他们保持第二装订。在a:
的代码func-3
势必三次,所以它也保持其最后的约束力。这不是瞄准镜,它只是代码的束缚和代码,然后小位再次被束缚,依此类推,直到它完成。
结合每一轮由被“定义”的东西(说真的,构建它)的函数来执行,然后在运行代码,并调用该定义别的东西等功能,这些功能进行另一轮的结合,其代码小集。这就是为什么我们叫它“的定义范围界定”;而果然不是划定范围,它是什么服务在Rebol的划定范围的目的,这是足够接近词法作用域的行为乍一看你不能分辨出来。
当你意识到这些绑定直接的,你可以改变它们(在某种程度上,可以使新词具有相同的名称和不同的结合)这真的变得不同。同样的功能,这些定义函数调用,你可以调用自己:它的命名bind
。随着bind
可以打破作用域的错觉,并结合到任何对象,你可以访问的话。你可以做精彩的技巧与bind
,甚至让你自己定义的功能。这是负载的乐趣!
至于红色,红色是编译的,但它也包括一个雷博尔般的解释,装订,所有的好东西的。当它的定义与解释的东西它的定义范围界定为好。
这是否有助于使事情更清楚了吗?
这是一个老问题,@ BrianH的回答这里是技工非常彻底。不过,我想我会给一个有一个稍微不同的重点,更有点“故事”。
在Rebol的,有一个名为话类型类别。这些基本上是符号,所以它们的字符串的内容进行扫描,他们进入一个符号表。因此而"FOO"
将是一个字符串,<FOO>
将是另一个被称为标签... FOO
,'FOO
,FOO:
串的“味道”和:FOO
具有相同符号ID的话都不同的“口味”。 (A“字”,一个“上火字”,一个“集字”和“获取字”的分别。)
被折叠成一个符号使得它不可能修改一次加载一个字的名字。他们坚持,用绳子,每个都有自己的数据和是可变的比较:
>> append "foo" "bar"
== "foobar"
>> append 'foo 'bar
** Script error: append does not allow word! for its series argument
不变性有一个优点,即作为一种象征速度非常快就一个字数据进行比较。但还有另一块拼图:一个词的每个实例都可以任选具有一种无形财产也称为绑定。该结合让它“点”被称为其中值可以被读取或写入的背景的键/值的实体。
注:与@BrianH我不认为调用这个类的约束性指标“上下文”是那么糟糕 - 至少我不认为今天它。以后再问我,我可能会,如果新的证据明朗化改变我的想法。我只想说,这是一个类似对象的事情,但并不总是一个对象......这可能是一个引用转换成函数的栈帧上,例如。
谁带来了字到系统在说什么情况下它被势必得到第一枪。很多当时那是LOAD,所以如果你说load "[foo: baz :bar]"
和回来的3个字块[foo: baz :bar]
他们将被绑定到“用户上下文”,用备用的“系统环境”。
下面结合是一切是如何工作的,每个字的“味道”做不同的东西。
>> print "word pointing to function runs it"
word pointing to function runs it
>> probe :print "get-word pointing to function gets it"
make native! [[
"Outputs a value followed by a line break."
value [any-type!] "The value to print"
]]
== "get-word pointing to function gets it"
注:第二种情况没有打印该字符串。它探测功能规范,则该字符串只是在评估的最后一件事,因此评估了这一点。
但是,一旦你有在你的手在它字的数据块,绑定是群雄争霸。只要上下文有在一个字符号,你可以重新定位这个词对于该上下文。 (也即该块未得到保障或不可修改假设...)
这个级联的重新绑定机会链是重要的一点。由于FUNC是一个“函数发生器”,需要一个规范,并且你给它一个机构,它把尸体与它绑定的“生物质”,并覆盖其决定者为准能力。怪异的也许,但看看这个:
>> x: 10
>> foo: func [x] [
print x
x: 20
print x
]
>> foo 304
304
20
>> print x
10
实际情况是,FUNC收到两大块,一是表示参数列表和第二代表身体。当它得到了身体,无论是print
s被绑定到本地打印功能(在这种情况下 - 并指出,当你从该控制台其他地方得到的材料,他们可以各自不同的约束是很重要的!)。 x
被绑定到哪个抱着值10.如果FUNC没有做任何改变的情况下,事情会保持这种方式用户上下文(在这种情况下)。
但它把画面一起,并决定自参数列表中有它的一个X,它看起来通过身体并覆盖x的符号ID的话有一个新的绑定...本地的功能。这就是,它没有覆盖全球与x: 20
的唯一原因。假如你省略了[X]在规范FUNC不会做的事,那就会被覆盖。
在定义的链每件得到合格的事情之前的机会。因此,定义性的作用域。
有趣的事实:因为如果你不提供参数给FUNC的规范,它不会在体内重新绑定任何东西,这导致了错误的印象,“一切在Rebol的是在全球范围内”。但事实并非如此,因为在所有的@BrianH说:“雷博尔却没有在所有(...)雷博尔假货它划定范围。”事实上,这就是功能(而不是FUNC)那样 - 它会在体内狩猎设置的话就像X:当它看见他们将它们添加到本地帧,并结合他们。效果看起来像本地范围,但再次,它是不是!
如果这听起来有点鲁贝 - 戈德堡式的想象,这些符号与无形的指针被抛去,这是因为它是。就我个人而言,显着的是那种,它在所有工作......我见过的人拉它的绝技,你会不会直觉地认为这样一个简单的技巧可以用来做。
案例分析:在令人恼火有用的收集和保存(Ren-C version):
collect: func [
{Evaluates a block, storing values via KEEP function,
and returns block of collected values.}
body [block!] "Block to evaluate"
/into {Insert into a buffer instead
(returns position after insert)}
output [any-series!] "The buffer series (modified)"
][
unless output [output: make block! 16]
eval func [keep <with> return] body func [
value [<opt> any-value!] /only
][
output: insert/:only output :value
:value
]
either into [output] [head output]
]
这种毫不起眼的前瞻性工具扩展在下面的样式语言(同样,任-C版本...在R3-α或Rebol2替代foreach
for for-each
和length?
for length of
)
>> collect [
keep 10
for-each item [a [b c] [d e f]] [
either all [
block? item
3 = length of item
][
keep/only item
][
keep item
]
]
]
== [10 a b c [d e f]]
用的定义作用域这里的技巧是什么我上面提到的最好的理解。 FUNC只会覆盖的东西绑定在它的参数列表,并把一切在身体其他不变。那么,什么情况是,它需要你通过收集和使用身体,作为一个新的功能,它会覆盖保留任何绑定的身体。然后,它集KEEP来调用时将数据添加到一个聚合的功能。
在这里,我们看到在拼接块KEEP功能的多功能性到收集输出或不经由/只有开关(呼叫者选择,如果我们看到长度3的项目,不仅拼接)。但这只是表面文章。这只是一个深深的功能强大的语言功能 - 事后由用户添加的 - 从这么少的代码发起这几乎是可怕的。当然,还有很多更多的故事。
我在这里增加一个答案已经引起填补了关键缺失环节进行的定义范围界定,被称为“definitionally范围的回归”一个问题:
https://codereview.stackexchange.com/questions/109443/definitional-returns-solved-mostly
这就是为什么<with> return
是一起在规范的KEEP。它的存在,因为COLLECT是想告诉FUNC就是了“使用其服务”作为粘合剂和亚军的代码。但身体是由别人撰写的已经在其他地方。所以,如果它在一回,然后返回已经有地方返回的想法。 FUNC只是“重新范围”的守,而是独自留下任何的回报,而不是增加自己的。因此:
>> foo: func [x] [
collect [
if x = 10 [return "didn't collect"]
keep x
keep 20
]
]
>> foo 304
== [304 20]
>> foo 10
== "didn't collect"
这是<with> return
使COLLECT能足够聪明,知道FOO的身体里面,它不希望返回的反弹使得它认为从它的参数只是[保持]而不是函数返回。
还有是关于“为什么”的范围界定的定义一点点,对刚刚“什么”。 :-)