这里有龙!
The reword
function 是一个实验,旨在以符合我们做事方式的方式向 Rebol 添加 shell 风格的字符串插值。与 Rebol 的许多系列函数不同,它确实针对仅处理字符串类型进行了优化,设计也反映了这一点。当前版本是一个设计原型,最终将作为原生版本重做,但它确实按照设计工作,因此讨论它的工作原理和使用方法是有意义的。
什么是reword
do?
基本上是这样的:
>> reword "$a is $b." [a "This" b "that"]
== "This is that."
它采用模板字符串,搜索转义序列,并将其替换为相应的替换值。这些值也以对象、映射或键和值块的形式传递给函数。键几乎可以是任何东西,甚至数字:
>> reword "$1 is $2." [1 "This" 2 "that"]
== "This is that."
如果键还不是字符串,则它们将转换为字符串。如果键被转换为相同的字符串,则它们被认为是相同的,当您执行以下操作时会发生这种情况:
>> reword "A $a is $a." [a "fox" "a" "brown"]
== "A brown is brown."
它不像正则表达式替换那样是位置性的,而是基于关键字的。如果您在值块中多次指定一个键,则该键的最后一个值就是被使用的值,正如我们刚刚看到的那样。任何未设置或无值都会被跳过,因为这些值在将内容放入字符串时没有任何意义。
您也可以使用其他转义标志,甚至是多字符标志:
>> reword/escape "A %%a is %%b." [a "fox" b "brown"] "%%"
== "A fox is brown."
或者甚至根本没有转义标志,它会在所有地方替换密钥:
>> reword/escape "I am answering you." [I "Brian" am "is" you "Adrian"] none
== "Brian is answerBrianng Adrian."
哎呀,那没用。这是因为键不区分大小写,并且它们不需要被空格或其他此类分隔符包围。但是,如果将它们指定为字符串,则可以在键本身中添加空格,这样效果更好:
>> reword/escape "I am answering you." ["I am" "Brian is" you "Adrian"] none
== "Brian is answering Adrian."
仍然在做reword
没有转义字符的模板往往很棘手并且速度有点慢,因此不经常这样做。
不过,还有一个更好的技巧......
功能替换
Where reword
真正有趣的是当您使用函数作为替换值时,因为该函数得到called每一次改写。假设您想用计数器替换:
>> reword "$x then $x and $x, also $x" object [y: 1 x: does [++ y]]
== "1 then 2 and 3, also 4"
或者甚至是位置,因为它可以将字符串位置作为参数:
>> reword "$x then $x and $x, also $x" object [x: func [s] [index? s]]
== "1 then 9 and 16, also 25"
等等,这看起来不太对劲,那些数字似乎不对劲。这是因为该函数返回的索引template字符串,而不是result细绳。在编写这些函数时最好记住这一点。该功能甚至不必只分配给一个键,它可以检测或使用它:
>> reword "$x or $y" object [x: y: func [s] [ajoin ["(it's " copy/part s 2 ")"]]]
== "(it's $x) or (it's $y)"
请参阅模板变量、转义符等等。并且该函数可能会产生副作用,例如这个行计数器:
>> reword/escape "Hello^/There^/nl" use [x] [x: 0 map reduce ["^/" does [++ x "^/"] "nl" does [x]]] ""
== "Hello^/There^/2"
它甚至还带有/into
选项,因此您可以使用它来分阶段构建字符串。
但对于来自内置插值语言的人来说,最大的问题是……
为什么要使用值块,为什么不像普通语言那样使用变量呢?
Because Rebol 并不是这样工作的。 Rebol 没有词法绑定,它会做其他事情,因此在字符串中,如果不说出来,就无法知道从哪里获取变量的值。在其中一种具有插值功能的 shell 语言中,这相当于必须将对整个环境的引用传递给插值函数。但是,嘿,我们可以在 Rebol 中做到这一点:
>> use [x] [x: func [s] [index? s] reword "$x then $x and $x, also $x" bind? 'x]
== "1 then 9 and 16, also 25"
That bind?
方法将工作在use
、绑定循环和函数。如果你在一个对象中,你也可以使用self
:
>> o: object [x: func [s] [index? s] y: func [s] [reword s self]]
== make object! [
x: make function! [[s][index? s]]
y: make function! [[s][reword s self]]
]
>> o/y "$x then $x and $x, also $x"
== "1 then 9 and 16, also 25"
但要小心,否则你最终可能会做这样的事情:
>> o/y "$x then $x and $x, also $x, finally $y"
** Internal error: stack overflow
龙!这是将变量和替换键分开的一个很好的理由......