【问题标题】:What's the fastest/most efficient way to count lines in Rebol?在 Rebol 中计算行数最快/最有效的方法是什么?
【发布时间】:2013-01-23 19:56:12
【问题描述】:

给定一个字符串string,计算其中行数最快/最有效的方法是什么?将接受任何风格的 Rebol 的最佳答案。我一直在假设 parse [some [thru]] 组合是遍历字符串的最快方法,但我不确定这一点,因此转向 SO:

count-lines: func [string [string!] /local count][
    parse/all string [
        (count: 1) some [thru newline (count: count + 1)]
    ]
    count
]

或者:

count-lines: func [string [string!] /local count][
    count: 0
    until [
        count: count + 1
        not string: find/tail string newline
    ]
    count
]

那么计数器呢?重复的效率如何?

count-lines: func [string [string!]][
    repeat count length? string [
        unless string: find/tail string newline [
            break/return count
        ]
    ]
]

更新:行数符合文本编辑器原则:

空文档的行数仍然为 1。所以:

>> count-lines ""
== 1
>> count-lines "^/"
== 2

【问题讨论】:

  • 此外,我们欢迎任何帮助测试速度/效率声明的真实性。
  • Rebol 程序员会认为空字符串只有 1 行。这种对零的恐惧是怎么回事? :-P
  • 任何性能考虑的另一件事是输入的性质。我会研究您尝试使用不同输入的任何技术...一些示例:空字符串,“所有换行符的长字符串”,“零换行符的长字符串"...

标签: string performance newline rebol


【解决方案1】:

这是我能想到的最简单的非parse 版本:

count-lines: function [text [string!]] [
    i: 1
    find-all text newline [++ i]
    i
]

它使用来自 Rebol 较新版本的 function 和 ++,以及来自 R3 或 R2/Forward 的 find-all。您可以查看find-all 的源代码并内联您找到并优化的内容,但这样的情况正是我们编写find-all 的目的,所以为什么不使用它呢?

【讨论】:

  • Rebolek 的parse 解决方案可能更快,但只有分析器才能确定。
  • 又一个有趣的新功能,谢谢!
  • find-all 函数非常适合更改为原生函数。
【解决方案2】:

不是最有效的,但可能是最快的解决方案之一(无论如何,如果运行基准测试,我想看看这个解决方案的表现如何):

>> s: "1^/2^/ ^/^/3"
>> (length? s) - length? trim/with copy s newline
== 4

【讨论】:

    【解决方案3】:

    呵呵呵呵读/行长度? temp 是我认为关于 read/lines -> foreach lines temps [ count: count + 1]

    另一种方法是这样做

    temp: "line 1 ^M line2 ^M  line3 ^M "
    length? parse temp newline ; that cuts the strings into a block 
    ;of multiple strings that represent each a line [ "line 1" "line2" "line3" ] 
    :then you count how much  strings you have in the block with length? 
    

    我喜欢用 rebol 写代码,太有趣了

    编辑我没有阅读整篇文章,所以我的解决方案已经以不同的方式提出......

    可以修正我发布已发布解决方案的罪过,我将对该解决方案的意外行为提出洞察评论。不计算多个链式回车(使用 rebol3 linux ...)

    >> a: "line1 ^M line2 ^M line3 ^M^M"
    == "line1 ^M line2 ^M line3 ^M^M"
    
    >> length? parse a newline 
    == 3
    

    【讨论】:

      【解决方案4】:

      为什么没有人提供我想知道的最简单的解决方案:)

      t: "abc^/de^/f^/ghi"
      i: 0 until [i: i + 1 not t: find/tail t newline] i
      == 4
      

      不确定性能,但我认为它相当快,因为​​ UNTIL 和 FIND 是本地人。 WHILE 也可以使用。

      i: 1 while [t: find/tail t newline] [i: i + 1] i
      == 4
      

      只需要检查空字符串。如果它是一个函数,则需要对参数系列进行 HEADed。

      【讨论】:

      • 这里的while 解决方案就是我的“内联find-all 和优化”评论的意思。 until 解决方案可能更快,因为until 是一个更简单的本机。 i: i + 1 在 R2 中比 ++ i 快,但在 R3 中较慢。
      【解决方案5】:

      BrianH 建议的增强型 PARSE 版本:

      i: 1 ; add one as TextMate
      parse text [any [thru newline (++ i)]]
      print i
      

      【讨论】:

      • 应该是++ i? (没注意到有++ :)
      • 也可以开始 i: 0 并说 [newline | end] (++ i)。
      • 如果要包含计数,请将初始化移到规则中,如下所示:(i: 1)
      • 如果我可以单线化它:count-lines: func [text [string!] /i][parse text [(i: 1) any [thru newline (++ i)]] i]
      • @HostileFork /i 也创建一个本地。 /local 是一个没有正式意义的约定。虽然我会在正式场合使用/local。
      【解决方案6】:

      remove-each 可以很快,因为它是原生的

      s: "1^/2^/3"
      a: length? s
      print a - length? remove-each v s [v = #"^/"]
      ; >> 2
      

      或作为函数

      >> f: func [s] [print [(length? s) - (length? remove-each v s [v = #"^/"])]]
      >> f "1^/2^/3"
      == 2
      

      【讨论】:

      • 我需要将其与上面的 'parse 函数进行基准测试,看看哪个更快。我还担心需要复制's,以免修改原始字符串从而影响效率。
      • 样式注释:函数返回换行数,而不是行数——应该根据“文本编辑器原则”返回s + 1(参见问题)。
      • 请注意,在 Rebol 3 中,REMOVE-EACH 返回删除计数,而不是修改后的系列。所以你的建议可以只是 Rebol 3 中的remove-each v s [v = #"^/"]。(这实际上是我遇到的第一个用例,其中 R3 中相当奇怪的 REMOVE-EACH 返回值很有用。)
      【解决方案7】:
      count-lines: func [
          str
          /local sort-str ][
      sort-str: sort join str "^/"
      1 + subtract index? find/last sort-str "^/" index? find sort-str "^/"
      ]
      

      【讨论】:

      • 有趣的方法——任何想法(任何人)它是如何执行的?
      • 有趣,大流士!顺便说一句,如果您想加入我们,我们有一个SO chat room for Rebol...
      • 任何修改方法对于长字符串都可能会很慢,因为在修改过程中转移序列会产生开销。但是分析器会告诉我们哪种方法会更好。
      【解决方案8】:

      不知道性能和最后一行规则(r3)。

      >> length? parse "1^/2^/3" "^/"
      == 3
      

      【讨论】:

      • 我在这里担心的是,它创建的新字符串与行数一样多——这是一个非常简洁的单行,但我担心它比问题中的parse 示例更多。想听听其他意见...
      • 这种方法的另一个缺点是使用parse 作为split 的一个怪癖——它在这种情况下会中断:parse {one^/"two^/three"} "^/"
      • 好点。我怀疑额外的内存会是一个问题。我作弊并且不对额外的 gc 进行基准测试 :) 但是报价处理是一个坏惊喜。
      【解决方案9】:

      这是最适合我的:

      temp: read/lines %mytext.txt
      length? temp
      

      【讨论】:

      • 字符串源不是文件,你的意思是把字符串写入文件以便读取/行吗?
      • 好的,那么我们将不得不增加将值写入文件的开销,然后再将其读回。或者你可以试试deline/lines,它在内存中做同样的事情而不需要文件。但请注意:在 R2 中,deline/lines 是夹层,因此它不会像其他一些解决方案那样快。在 R3 中,它是原生的。无论如何,read/lines 和deline/lines 都会复制源字符串,所以我们必须看看这种开销是否会超过作为单个本地调用的优势。不过,我可以确认这种方法有效。
      • BriaH 是一个真正的程序员,我只是提出解决方案。如果您需要时间优化或其他高技术解决方案,请联系他。 :-)
      猜你喜欢
      • 1970-01-01
      • 2011-07-13
      • 2023-03-13
      • 1970-01-01
      • 2017-06-27
      • 2014-05-04
      • 2011-01-26
      • 1970-01-01
      • 2011-02-25
      相关资源
      最近更新 更多