我有兴趣搜索很多长串,尝试在rebol中破解类似sed的实用程序作为学习练习。作为一个婴儿步骤,我决定寻找一个角色:
>> STR: "abcdefghijklmopqrz"
>> pos: index? find STR "z"
== 18
>> pos
== 18
大!让我们寻找别的东西......
>> pos: index? find STR "n"
** Script Error: index? expected series argument of type: series port
** Where: halt-view
** Near: pos: index? find STR "n"
>> pos
== 18
什么? : - (
是的,我正在搜索的字符串中没有“n”。但是解释器爆炸而不是做一些明智的事情会带来什么好处,例如在pos中返回一个可测试的“null”字符?
我被告知我应该这样做:
>> if found? find STR "z" [pos: index? find STR "z"]
== 18
>> if found? find STR "n" [pos: index? find STR "n"]
== none
>> pos
== 18
真的?我必须搜索字符串TWICE;第一次只是为了确保搜索AGAIN是“安全的”?
所以我有一个由三部分组成的问题:
向导如何实现我的搜索功能?我认为有一种更好的方式比这更好....
Red会改变这个吗?理想情况下,我认为find应该返回一个有效的字符串位置,如果它遇到字符串的结尾,则返回NULL(NULL分隔,我可以假定吗?)。 NULL为FALSE,因此可以设置一个非常简单的测试。
拥有有效索引后,最有效的CPU替换方法是什么? Rebol似乎有很多选择(一件好事),有可能在选择或陷入次优选择时陷入困境。
答案 0 :(得分:4)
我被告知我应该这样做:
>> if found? find STR "z" [pos: index? find STR "z"] == 18 >> if found? find STR "n" [pos: index? find STR "n"] == none >> pos == 18
真的?我必须搜索字符串TWICE;第一次只是为了确保搜索AGAIN是“安全的”?
你当然不必两次搜索字符串。但是index?
(可能是未来的名称,因为它不返回是/否:index-of
)不会返回NONE!没有价值的话!输入。它假定调用者想要一个整数位置,如果它不能给你一个错误就会引发错误。
要消除双重搜索,您可以使用短路评估...
>> all [pos: find STR "z" pos: index? pos]
== 18
>> pos
== 18
>> all [pos: find STR "n" pos: index? pos]
== none
>> pos
== none
但请注意,如果不引入第二个变量,您将覆盖之前的pos
。假设您调用变量index
而pos是临时变量:
>> all [pos: find STR "z" index: index? pos]
== 18
>> index
== 18
>> all [pos: find STR "n" index: index? pos]
== none
>> index
== 18
在中间表达式中的任意点处抛出set-words的能力非常强大,这就是为什么像多次初始化(a: b: c: 0
)之类的东西不是该语言的特殊功能,而是一些不属于评估模型。
index?
(咳嗽 index-of
)的好处不太可能无效!没有价值的话!输入超过了它如此宽容会导致的问题。它始终是一种平衡。
请注意,FIND确实表现得如您所愿。发现了什么?只是一种语法上的便利,可以将找到的位置转换为真正的值,而且无需!回到假的。它相当于调用TRUE? (但阅读时只是更有文化)。没有必要在IF或UNLESS或EITHER的条件下使用它......因为它们会将NONE结果视为错误,并将任何位置视为真实。
本来最快的可能是挂在这个位置,并说change pos #"x"
。 (虽然内部“位置”是由索引加系列实现的,而不是一个独立的指针。所以优势不是 在微优化世界中很重要,我们在那里计算诸如补充偏移之类的东西。 ..)
关于索引的哪个操作:我会说选择你最喜欢的方式并稍后进行微优化。
我个人认为STR/:index: #"x"
看起来并不那么好,但它是最简洁的角色。
STR/(index): #"x"
做同样的事情并且看起来更好IMO。但是以源代码结构为代价炸毁了一点。这是一个SET-PATH!包含PAREN的系列!系列后跟一个CHAR!...全部嵌入原始系列“vector”中,它持有代码。引擎盖下会出现局部问题。 And we know how important that is these days...
看似天真的POKE可能是最快的。 poke STR index #"x"
。它可能看起来像“4个元素而不是2个”,但路径案例的“2个元素”是一种幻觉。
在Rebol中,猜测总是有点困难,所以你必须收集数据。你可以运行一些重复的迭代测试来找出答案。要计算一段代码,请参阅内置delta-time
。
在Red中,编译后的表单应该是等价的,但如果以某种方式将其解释,你可能会有与Rebol类似的时间。
答案 1 :(得分:3)
毫无疑问,HostileFork的答案涵盖了一切! 1
只是想为我经常使用的第1点添加替代解决方案:
>> attempt [index? find STR "z"]
== 18
>> attempt [index? find STR "n"]
== none
答案 2 :(得分:2)
在Red / Rebol中搜索字符串非常简单方便。关于您遇到的问题,让我为您解开详细信息:
首先,口译员会以错误信息的形式给你一个关于你做错事的好提示:index? expected series argument of type: series port
。这意味着您在错误的数据类型上使用了index?
。那是怎么发生的?只是因为find
函数在搜索失败时返回none
值:
>> str: "abcdefghijklmopqrz"
>> find str "o"
== "pqrz"
>> type? find str "o"
== string!
>> find str "n"
== none
>> type? find str "n"
== none!
因此,直接在index?
的结果上使用find
不安全,除非您知道搜索不会失败 。如果您还需要提取索引信息,安全的方法是首先测试find
的结果:
>> all [pos: find str "o" index? pos]
== 14
>> all [pos: find str "n" index? pos]
== none
>> if pos: find str "o" [print index? pos]
== 14
>> print either pos: find str "n" [index? pos][-1]
== -1
根据您的需要,这些只是实现目标的安全方法的示例。请注意,none
在false
或if
中对条件测试充当either
,因此在这种情况下使用found?
是多余的。
Rebol语言有一个称为series
的基本概念,从中导出string!
数据类型。正确理解和使用系列是能够以惯用方式使用Rebol语言的关键部分。系列看起来像其他语言中的常用列表和类似字符串的数据类型,但它们不相同。系列由以下内容组成:
以下描述仅关注字符串,但相同的规则适用于所有系列数据类型。我将在下面的示例中使用index?
函数将隐式索引显示为整数。
默认情况下,当您创建新字符串时,光标位于 head 位置:
>> s: "hello"
>> head? s
== true
>> index? s
== 1
但是光标可以移动到指向字符串中的其他位置:
>> next s
== "ello"
>> skip s 3
== "lo"
>> length? skip s 3
== 2
正如您所看到的,带有移动光标的字符串不仅会从光标位置显示,还会所有其他字符串(或系列)函数都会考虑该位置。
此外,您还可以为指向字符串的每个引用设置光标:
>> a: next s
== "ello"
>> b: skip s 3
== "lo"
>> s: at s 5
== "o"
>> reduce [a b s]
== ["ello" "lo" "o"]
>> reduce [index? a index? b index? s]
== [2 4 5]
正如您所看到的,您可以根据需要为给定的字符串(或系列)提供尽可能多的不同引用,每个引用都有自己的 cursor 值,但所有引用都指向基础值列表。
系列属性的一个重要结果:你不需要像在其他语言中那样依赖整数索引来操纵字符串(和其他系列),你可以简单地利用光标来任何系列引用都可以执行您需要的任何计算,并且您的代码将简短,干净且易读。尽管如此,整数索引有时在序列中很有用,但你很少需要它们。
现在让我们回到您的用例进行字符串搜索。
>> STR: "abcdefghijklmopqrz"
>> find STR "z"
== "z"
>> find STR "n"
== none
这就是你所需要的,你不必提取索引位置,以便将结果值用于你需要做的几乎任何计算。
>> pos: find STR "o"
>> if pos [print "found"]
found
>> print ["sub-string from `o`:" pos]
sub-string from `o`: opqrz
>> length? pos
== 5
>> index? pos
== 14
>> back pos
== "mopqrz"
>> skip pos 4
== "z"
>> pos: find STR "n"
>> print either pos ["found"]["not found"]
not found
>> print either pos [index? pos][-1]
-1
这是一个简单的例子,展示如何在不明确使用整数索引的情况下进行子字符串提取:
>> s: "The score is 1:2 after 5 minutes"
>> if pos: find/tail s "score is " [print copy/part pos find pos " "]
1:2
通过一些练习(控制台非常适合此类实验),您将看到完全依赖Rebol语言中的系列而不仅仅是普通整数索引更简单,更有效。
现在,这是我对你的问题的看法:
不需要任何魔法,只需充分使用系列和find
功能,如上所示。
红色不会改变这一点。系列是Rebol语言简单而强大的基石。
change
应该是最快的方式,但是,如果你有很多替换来操作一个长字符串,重新构建一个新的字符串而不是改变原来的字符串,通常会带来更好的表现,因为它当替换字符串与他们替换的部分大小不同时,会避免移动内存块。