Rebol中最快/最有效的计算行数的方法是什么?

9
给定一个字符串 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
]

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

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

更新: 行数按照文本编辑器的原则计算:

Empty TextMate Document

空白文档仍然有一行。因此:

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

此外,欢迎任何协助测试速度/效率声明真实性的帮助。 - rgchris
1
Rebol程序员会认为空字符串有1行。这个对零的恐惧是怎么回事?:-P - HostileFork says dont trust SE
在任何性能考虑中,另一件事是输入的性质。我会研究您尝试的任何技术,并使用不同的输入进行测试...例如:空字符串,*"所有换行符的长字符串""零换行符的长字符串"*... - HostileFork says dont trust SE
9个回答

4
count-lines: func [
    str
    /local sort-str ][
sort-str: sort join str "^/"
1 + subtract index? find/last sort-str "^/" index? find sort-str "^/"
]

有趣的方法——有没有人想过这个的性能如何? - rgchris
有趣的,Darius!顺便说一下,如果你想加入我们,我们有一个关于Rebol的SO聊天室 - HostileFork says dont trust SE
任何修改长字符串的方法都可能会很慢,因为在修改期间需要移动序列的开销。但是分析器将告诉我们哪种方法更好。 - BrianH

4

BrianH建议的增强版PARSE:

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

1
应该是++i吗?(没有注意到有一个++:) - rgchris
也可以从 i:0 开始,然后用 [newline|end] (++i) 表示。 - rgchris
1
如果你想要包含计数,将初始化移动到规则中,像这样:(i: 1) - BrianH
1
如果我可以将其一行化: count-lines: func [text [string!] /i][parse text [(i: 1) any [thru newline (++ i)]] i] 就变成了:count-lines: func [text [string!] /i][parse text [(i: 1) any [thru newline (++ i)]] i] - rgchris
1
@HostileFork /i 也会创建一个本地变量。/local 是一种没有正式含义的约定。虽然在正式场合中我会使用 /local - rgchris
显示剩余6条评论

3

下面是我能想到的最简单的非解析版本:

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”解决方案可能更快,但只有分析器才能确定。 - BrianH
另一个有趣的新功能,谢谢! - rgchris
“find-all”函数是一个很好的候选项,可以改为本地化。 - BrianH

2

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”,以避免修改原始字符串,从而影响效率。 - rgchris
风格注释:函数返回换行符的数量,而不是行数 - 应该根据“文本编辑器原则”返回s + 1(请参见问题)。 - rgchris
请注意,在 Rebol 3 中,REMOVE-EACH 返回的是删除计数而不是修改后的序列。因此,在 Rebol 3 中,您可以建议使用 remove-each v s [v = #"^/"]。 (这实际上是我遇到的第一个应用 REMOVE-EACH 在 R3 中相当奇怪的返回值非常有用的情况。) - earl

2

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

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

2

这是对我来说最好的:

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

1
该字符串源不是文件,您是否意味着将字符串写入文件以便读取/行? - rgchris
1
好的,那么我们需要增加将值写入文件并读取回来的开销。或者您可以尝试使用 deline/lines,它在内存中完成相同的操作而不需要文件。但要注意:在 R2 中,deline/lines 是中间层,因此它的速度不如其他解决方案快。在 R3 中,它是本地的。无论如何,read/linesdeline/lines 都会复制源字符串,因此我们需要看看这种开销是否超过了单个本地调用的优势。我可以确认这种方法是可行的。 - BrianH
BriaH是一位真正的程序员,我只是提出解决方案。如果您需要时间优化或其他高技术解决方案,请联系他。 :-) - MaxV

2
我很惊讶为什么没有人提出最简单的解决方案 :)
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

只需要检查空字符串。如果这是一个函数,参数系列需要以HEAD为首。


这里的 while 解决方案是我在 "内联 find-all 和优化" 评论中所说的。until 解决方案可能更快,因为 until 是一个更简单的本地方法。在 R2 中,i: i + 1++ i 更快,但在 R3 中则更慢。 - BrianH

1

我不了解性能,也不知道最后一行的规则(r3)。

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

我在意的是它会创建与行数一样多的新字符串——虽然这只是一个非常简洁的一行代码,但我担心它所做的事情比问题中的“解析”示例更多。想听听其他人的意见... - rgchris
这种方法的另一个缺点是在使用parse作为split时存在一个怪异的问题——它在这种情况下会出错:parse {one^/"two^/three"} "^/" - rgchris
好的观点。我怀疑额外的内存不会成为问题。我作弊,没有对额外的垃圾回收进行基准测试 :) 但引用处理是个坏惊喜。 - dt2

0

呵呵呵,read/lines长度?temp是一个很棒的东西,我想到了read/lines -> 对于每一行temps [计数:计数+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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接