【问题标题】:String searching in Rebol or RedRebol 或 Red 中的字符串搜索
【发布时间】:2015-04-30 21:44:35
【问题描述】:

我有兴趣搜索大量长字符串,并尝试在 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 中似乎有很多选择(一件好事),以至于可能会陷入选择或陷入次优选择。

【问题讨论】:

    标签: string rebol rebol2 red


    【解决方案1】:

    在 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!
    

    因此,直接在find 的结果上使用index?不安全,除非您知道搜索不会失败。如果无论如何都需要提取索引信息,安全的做法是先测试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
    

    这些只是实现它的安全方法的示例,具体取决于您的需要。请注意,noneifeither 中充当条件测试的false,因此在这种情况下使用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
    

    如您所见,带有移动光标的字符串不仅从光标位置显示,而且所有其他字符串(或系列)函数都会考虑该位置。 p>

    此外,您还可以设置每个指向字符串的引用的光标:

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

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

    1. 不需要魔法,只要充分利用series和find函数,如上图。

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

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

    【讨论】:

      【解决方案2】:

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

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

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

      Rebol 2 attempt 和 Rebol 3 attempt 的在线文档

      【讨论】:

      • 简洁明了!但是你会为该错误捕获支付 3 倍或更多的性能,比较 delta-time [loop 100000 [all [pos: find STR "n" pos: index? pos]]]delta-time [loop 100000 [attempt [index? find STR "n"]]](当然,对于 Rebol 的优势,大多数情况下,越简短越好……只是扮演魔鬼的拥护者……)
      • 我应该在上面澄清一下,性能损失是一个 Rebol2 问题,这个问题被标记了,并且我在哪里运行这个答案的测试。不应再将尝试的使用视为性能问题...而是语义问题,因为“将所有错误均等地抑制”是一个非常广泛的依赖网络!
      【解决方案3】:

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

      >> 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" 做同样的事情并且看起来更好 IMO。但代价是源代码结构有点爆炸。那是一个设置路径!系列包含一个PAREN!系列后跟一个 CHAR!...所有都嵌入到包含代码的原始系列“向量”中。在引擎盖下会有地方问题。 And we know how important that is these days...

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

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

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

      【讨论】:

        猜你喜欢
        • 2015-07-19
        • 1970-01-01
        • 2015-02-05
        • 1970-01-01
        • 1970-01-01
        • 2011-08-10
        • 2014-12-20
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多