10

我有兴趣搜索很多长字符串,尝试在 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”char?

有人告诉我我应该这样做:

>> if found? find STR "z" [pos: index? find STR "z"]
== 18

>> if found? find STR "n" [pos: index? find STR "n"]
== none

>> pos
== 18

真的吗?我必须搜索字符串两次;第一次只是为了确保再次搜索是“安全的”?

所以我有一个三部分的问题:

  1. 向导将如何实现我的搜索功能?我认为有比这更好的魔法更好的方法......

  2. Red会改变这种情况吗?理想情况下,我认为 find 应该返回一个有效的字符串位置,或者如果它到达字符串的末尾(NULL 分隔,我可以假设?)。NULL 是 FALSE,因此可以设置一个非常简单的 if 测试。

  3. 一旦我有一个有效的索引,最有效的 CPU 替换方法是什么?Rebol 中似乎有很多选择(一件好事),以至于可能会陷入选择或陷入次优选择。

4

3 回答 3

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

真的吗?我必须搜索字符串两次;第一次只是为了确保再次搜索是“安全的”?

您当然不必搜索字符串两次。但是index? (可能是未来的名称,因为它不返回是/否index-of:)不返回 NONE!如果给定一个 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) 这样的东西不是语言的特殊功能,而是不属于评估模型的东西。

Red会改变这种情况吗?

index?咳嗽 index-of)返回 NONE的好处不太可能!如果给定一个 NONE! 投入超过了因如此宽容而导致的问题。它始终是一种平衡。

请注意,FIND 的行为确实如您所愿。成立?只是一种语法便利,可以将找到的位置转换为真实值,并且是 NONE!返回到一个虚假的。相当于调用TRUE?(但在阅读时稍微识字)。没有必要在 IF 或 UNLESS 或 EITHER 的条件下使用它......因为他们会将 NONE 结果视为假,而将任何位置视为真。

一旦我有一个有效的索引,最有效的 CPU 替换方法是什么?

最快的可能就是挂在这个位置上,然后说change pos #"x"。(虽然内部“位置”是由索引加系列实现的,而不是独立的指针。所以在微优化世界中优势并不那么显着,我们正在计算诸如添加偏移量之类的东西......)

至于使用索引的哪个操作:我会说选择你最喜欢的方式,然后再进行微优化。

我个人认为STR/:index: #"x"看起来不是那么好,但它是角色中最简短的。

STR/(index): #"x"做同样的事情,看起来更好的国际海事组织。但代价是源代码结构有点爆炸。那是一个设置路径!系列包含一个PAREN!系列后跟一个 CHAR!...所有都嵌入到包含代码的原始系列“向量”中。在引擎盖下会有地方问题。 我们知道这些日子有多重要......

很可能看似天真的POKE是最快的。 poke STR index #"x". 它可能看起来像“4 个元素而不是 2 个”,但路径案例的“2 个元素”是一种错觉。

在 Rebol 中,猜测总是有点困难,因此您必须收集数据。您可以运行一些重复的迭代测试来找出答案。要为代码块计时,请参阅内置的delta-time.

在 Red 中,编译后的表单应该是等价的,但如果最终以某种方式被解释,您可能会有与 Rebol 类似的时间安排。

于 2015-02-28T03:56:24.580 回答
3

毫不奇怪,HostileFork 的回答完美地涵盖了所有内容!+1

只是想为我经常使用的第 1 点添加一个替代解决方案:

>> attempt [index? find STR "z"]   
== 18

>> attempt [index? find STR "n"] 
== none

attemptRebol 2和 Rebol 3的在线文档attempt

于 2015-02-28T11:28:22.850 回答
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

这些只是实现它的安全方法的示例,具体取决于您的需要。请注意,在or中none充当false条件测试,因此在这种情况下使用是多余的。ifeitherfound?

现在让我们对给您带来困惑的核心问题进行一些说明。

Rebol 语言有一个称为 a 的基本概念,数据类型series从该概念string!派生而来。理解和正确使用系列是能够以惯用方式使用 Rebol 语言的关键部分。系列在其他语言中看起来像通常的列表和类似字符串的数据类型,但它们并不相同。一个系列由:

  • 值列表(对于字符串,它是字符列表)
  • 隐式索引(为简单起见,我们可以称其为游标)

以下描述将仅关注字符串,但相同的规则适用于所有系列数据类型。我将index?在下面的示例中使用函数来将隐式索引显示为整数。

默认情况下,当您创建一个新字符串时,光标位于头部位置:

>> 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]

正如您所看到的,您可以根据需要对给定字符串(或系列)有尽可能多的不同引用,每个引用都有自己的光标值,但都指向相同的基础值列表

系列属性的一个重要结果:您不需要像在其他语言中那样依赖整数索引来操作字符串(和其他系列),您可以简单地利用任何系列引用附带的光标来执行您需要的任何计算,并且您的代码将简短、干净且非常易读。尽管如此,整数索引有时在系列中可能很有用,但您很少需要它们。

现在让我们回到您在字符串中搜索的用例。

>> 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 语言中的系列而不是单纯的整数索引是多么简单和高效。

现在,这是我对您的问题的看法:

  1. 不需要魔法,只要find充分利用系列和功能,如上图。

  2. 红色不会改变这一点。系列是使 Rebol 语言简单而强大的基石。

  3. change应该是最快的方法,但是,如果您有许多替换要对长字符串进行操作,则重建一个新字符串而不是更改原始字符串,通常会带来更好的性能,因为它可以避免在没有替换字符串时移动内存块与他们更换的零件尺寸相同。

于 2015-12-14T08:43:00.967 回答