在rebol中,我写了这个非常简单的函数:
make-password: func[Length] [
chars: "QWERTYUIOPASDFGHJKLZXCVBNM1234567890"
password: ""
loop Length [append password (pick chars random Length)]
password
]
当我连续多次运行时,事情变得非常混乱:
loop 5 [print make-password 5]
给出(例如)此输出:
看起来这个函数记住了过去的执行并存储了结果而不是再次使用它!
我没有问这个!
我希望输出类似于以下内容:
如何实现这一结果?
答案 0 :(得分:5)
一个好问题。
Rebol代码实际上被认为是一种非常风格化的数据结构。该数据结构“碰巧是可执行的”。但是你需要了解它是如何工作的。
例如,来自@ WiseGenius的建议:
make-password: func[Length] [
chars: "QWERTYUIOPASDFGHJKLZXCVBNM1234567890"
password: copy ""
loop Length [append password (pick chars random Length)]
password
]
查看包含append password...
的块。该块在那里“成像”;引擎盖下的真实情况是:
chars: **pointer to string! 0xSSSSSSS1**
password: copy **pointer to string! 0xSSSSSSS2**
loop Length **pointer to block! 0xBBBBBBBB**
password
所有系列在解释器加载时都以这种方式工作。字符串,块,二进制文件,路径,parens等等。鉴于它是“乌龟一直向下”,如果你遵循该指针,块0xBBBBBBBB在内部:
append password **pointer to paren! 0xPPPPPPPP**
这样做的一个结果是可以在多个地方引用一系列(因此“成像”):
>> inner: [a]
>> outer: reduce [inner inner]
[[a] [a]]
>> append inner 'b
>> probe outer
[[a b] [a b]]
这可能会让新手感到困惑,但是一旦了解了数据结构,就会开始知道何时使用COPY。
所以你已经注意到了这个函数的一个有趣含义。考虑一下这个程序:
foo: func [] [
data: []
append data 'something
]
source foo
foo
foo
source foo
这产生了可能令人惊讶的结果:
foo: func [][
data: []
append data 'something
]
foo: func [][
data: [something something]
append data 'something
]
我们多次调用foo
,似乎函数的源代码正在改变。从某种意义上说,它是自修改代码。
如果这困扰你,R3-Alpha中有工具可以攻击它。您可以使用PROTECT保护函数体不被修改,甚至可以创建自己的替代方法,如FUNC和FUNCTION,它们将为您完成。 (PFUNC?PFUNCTION?)在Rebol版本3中,您可以写:
pfunc: func [spec [block!] body [block!]] [
make function! protect/deep copy/deep reduce [spec body]
]
foo: pfunc [] [
data: []
append data 'something
]
foo
当你跑步时,你得到:
*** ERROR
** Script error: protected value or series - cannot modify
** Where: append foo try do either either either -apply-
** Near: append data 'something
因此迫使你复制系列。它还指出FUNC只是一个功能!本身,功能也是如此。你可以制作自己的发电机。
这可能会打破你的大脑,你可能会尖叫说“这不是任何理智的软件编写方式”。或许你会说“我的上帝,它充满了星星。”反应可能有所不同。但这对于为系统提供动力并赋予其灵活性的“技巧”来说是相当基础的。
(注意:Ren-C branch of Rebol3基本上已经使得函数体 - 和一般的源序列 - 在默认情况下被锁定。如果一个人想要一个函数中的静态变量,你可以说 foo: func [x <static> accum (copy "")] [append accum x | return accum]
,该功能将在accum
跨越呼叫累积状态。)
我还建议密切关注每次运行中实际发生的事情。在运行foo
函数之前,数据没有值。每次执行函数时,会发生什么,评估者看到SET-WORD!后跟一个系列值,它执行对变量的赋值。
data: **pointer to block! 0xBBBBBBBB**
在该分配之后,您将对该块存在两个引用。一个是它存在于在LOAD时间建立的代码结构中,在该函数运行之前。第二个引用是存储在数据变量中的引用。通过这个第二个参考,你正在修改这个系列。
请注意,每次运行该功能时都会重新分配数据。但是一遍又一遍地重新分配给相同的值......那个原始的块指针!这就是为什么你必须COPY,如果你想在每次运行中都有一个新的块。
掌握评估者规则中的基本简单性是令人头晕目眩的一部分。这就是简单化打扮成语言的方式(在某种程度上你可以根据自己的方式扭曲)。例如,没有“多项任务”:
a: b: c: 10
这只是评估员将 a:作为SET-WORD!符号并说“好吧,让我们将其绑定上下文中的变量 a 与下一个完整表达式生成的任何内容相关联。” b:也是如此。 c:执行相同操作但由于整数值为10而命中终端...然后也计算为10.因此它看起来像多次分配。
所以请记住,系列文字的原始实例是挂在已加载源中的实例。如果评估者有办法做这种SET-WORD!或SET赋值,它将借用指向源中该文字的指针来戳入变量。这是一个可变的参考。您(或您设计的抽象)可以通过PROTECT或PROTECT / DEEP使其不可变,并且您可以使其不是COPY或COPY / DEEP的参考。
相关说明
有些人认为你永远不应该写复制[] ...因为(a)你可能养成忘记写COPY的习惯,并且(b)你正在制作一个未使用过的系列每次你这样做。那个“空白系列模板”被分配,必须由垃圾收集器扫描,没有人真正接触它。
如果你写 make block! 10 (或者你想要预先分配块的大小)你可以避免这个问题,保存一个系列,并提供一个大小提示。
答案 1 :(得分:2)
默认情况下,此表示法不会将字符串""
的值复制到password
。相反,它将password
设置为指向位于函数体块中的字符串。因此,当您在append
上执行password
时,您实际上会附加到它指向的字符串,该字符串位于函数的主体块中。你实际上正在改变函数体部分的一部分。要查看正在发生的情况,您可以使用??
检查您的功能,以便在每次使用时查看其中发生的情况:
make-password: func[Length] [
chars: "QWERTYUIOPASDFGHJKLZXCVBNM1234567890"
password: ""
loop Length [append password (pick chars random Length)]
password
]
loop 5 [
print make-password 5
?? make-password
]
这应该给你类似的东西:
TWTQW
make-password: func [Length][
chars: "QWERTYUIOPASDFGHJKLZXCVBNM1234567890"
password: "TWTQW"
loop Length [append password (pick chars random Length)]
password
]
TWTQWWEWRT
make-password: func [Length][
chars: "QWERTYUIOPASDFGHJKLZXCVBNM1234567890"
password: "TWTQWWEWRT"
loop Length [append password (pick chars random Length)]
password
]
TWTQWWEWRTQWWTW
make-password: func [Length][
chars: "QWERTYUIOPASDFGHJKLZXCVBNM1234567890"
password: "TWTQWWEWRTQWWTW"
loop Length [append password (pick chars random Length)]
password
]
TWTQWWEWRTQWWTWQTTQQ
make-password: func [Length][
chars: "QWERTYUIOPASDFGHJKLZXCVBNM1234567890"
password: "TWTQWWEWRTQWWTWQTTQQ"
loop Length [append password (pick chars random Length)]
password
]
TWTQWWEWRTQWWTWQTTQQTRRTT
make-password: func [Length][
chars: "QWERTYUIOPASDFGHJKLZXCVBNM1234567890"
password: "TWTQWWEWRTQWWTWQTTQQTRRTT"
loop Length [append password (pick chars random Length)]
password
]
要将字符串复制到password
而不是指向它,请尝试以下方法:
make-password: func[Length] [
chars: "QWERTYUIOPASDFGHJKLZXCVBNM1234567890"
password: copy ""
loop Length [append password (pick chars random Length)]
password
]
答案 2 :(得分:2)
没有足够的声誉评论HostileFork的答案,我这样反应。关于你的相关注释&#34;,它指出了我从未意识到的事情。
&#34;有些人认为&#34;暗示你不在其中,但是你让我觉得我最好写 str:make string! 0 和 blk:制作块! 0 从现在开始,不仅仅是在功能范围内。尺寸提示一直困扰着我。如果您不知道最终的幅度,是否有任何建议可供选择? (当然不低于你的最低期望,也不超过最高期望。)