Какой самый быстрый/эффективный способ подсчета строк в Rebol?

Учитывая строку 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
]

Or:

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

Обновление: количество строк соответствует принципу текстового редактора:

Пустой документ TextMate

Пустой документ по-прежнему имеет количество строк, равное единице. Так:

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

person rgchris    schedule 08.02.2013    source источник
comment
Кроме того, приветствуется любая помощь в проверке достоверности заявлений о скорости/эффективности.   -  person rgchris    schedule 08.02.2013
comment
Программисты Rebol посчитали бы, что пустая строка состоит из 1 строки. Что за страх нуля? :-П   -  person HostileFork says dont trust SE    schedule 09.02.2013
comment
Еще одна вещь при любом рассмотрении производительности - это характер ввода. Я бы изучил любую технику, которую вы пытаетесь использовать с различными входными данными... некоторые примеры: пустая строка, длинная строка всех новых строк, длинная строка без новых строк...   -  person HostileFork says dont trust SE    schedule 10.02.2013


Ответы (9)


Расширенная версия PARSE, предложенная BrianH:

i: 1 ; add one as TextMate
parse text [any [thru newline (++ i)]]
print i
person rebolek    schedule 19.02.2013
comment
Это должно быть ++ i? (не заметил ++ :) - person rgchris; 20.02.2013
comment
Можно также начать i: 0 и сказать [newline | end] (++ i). - person rgchris; 20.02.2013
comment
Есть ли преимущество/наказание при использовании skip вместо thru? При пропуске правило синтаксического анализа повторяется столько раз, сколько длина строки. - person rgchris; 20.02.2013
comment
Подход с разбором, вероятно, будет самым быстрым, поэтому я проголосовал за него. Тем не менее, это может использовать немного работы. Например, вы можете использовать to или thru. - person BrianH; 20.02.2013
comment
Крис, инициализация 0, а затем выполнение этого блока не только будет медленнее, но и приведет к бесконечному циклу в R2: any [end] никогда не остановится в конце строки в R2. Но это будет медленнее, потому что parse должен вернуться во вложенный блок, а затем вернуться через альтернативный (часть после |). Итак, вместо этого лучше инициализировать 1. thru использовать быстрее, чем skip, а также упрощает правило (упражнение на реболек, но в качестве подсказки: вам не нужно end или отступить). - person BrianH; 20.02.2013
comment
Конечно, о! Я склоняюсь к тому, чтобы счет полностью содержался в правиле, но склонности не всегда рациональны. - person rgchris; 20.02.2013
comment
Если вы хотите, чтобы счетчик содержался, переместите инициализацию в правило, например: (i: 1) - person BrianH; 20.02.2013
comment
Ну, я использовал skip вместо thru, потому что я написал этот ответ только для того, чтобы набрать достаточно очков, чтобы присоединиться к чату... Но мне нравится, что parse и parse ответа не было, поэтому я что-то написал :) - person rebolek; 20.02.2013
comment
Если можно в одну строку: count-lines: func [text [string!] /i][parse text [(i: 1) any [thru newline (++ i)]] i] - person rgchris; 20.02.2013
comment
@rgchris Ты имеешь в виду /local i, я так понимаю. :-) - person HostileFork says dont trust SE; 21.02.2013
comment
@HostileFork /i также создает файл local. /local — это соглашение, не имеющее формального значения. Хотя я бы использовал /local в формальной обстановке. - person rgchris; 27.09.2013

Вот лучшая простая версия без parse, которую я могу придумать:

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

Он использует function и ++ из более поздних версий Rebol и find-all из R3 или R2/Forward. Вы можете посмотреть исходный код find-all и встроить то, что найдете и оптимизируете, но именно для таких ситуаций мы и написали find-all, так почему бы не использовать его?

person BrianH    schedule 20.02.2013
comment
Решение Rebolek parse, вероятно, быстрее, но только профилировщик может сказать наверняка. - person BrianH; 20.02.2013
comment
Еще одна интересная новая функция, спасибо! - person rgchris; 20.02.2013
comment
Функция find-all — хороший кандидат на замену на нативную. - person BrianH; 22.02.2013

Вот лучшее для меня:

temp: read/lines %mytext.txt
length? temp
person MaxV    schedule 08.02.2013
comment
Источник строки не является файлом, вы имеете в виду запись строки в файл для чтения/строки? - person rgchris; 08.02.2013
comment
Хорошо, тогда нам придется добавить накладные расходы на запись значения в файл, а затем обратное чтение. Или вы можете попробовать deline/lines, который делает то же самое в памяти без необходимости в файле. Однако будьте осторожны: в R2 deline/lines является мезонином, поэтому он не будет таким быстрым, как некоторые другие решения. В R3 он родной. Несмотря на это, read/lines и deline/lines делают копию исходной строки, так что нам нужно посмотреть, не перевешивают ли эти накладные расходы преимущество одного нативного вызова. Я могу подтвердить, что этот подход работает. - person BrianH; 20.02.2013
comment
BriaH настоящий программист, я просто предлагаю решение. Если вам нужна оптимизация времени или другое высокое техническое решение, обращайтесь к нему. :-) - person MaxV; 25.02.2013

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
person endo64    schedule 18.02.2013
comment
Мне нужно сравнить это с функцией 'parse выше, чтобы увидеть, что быстрее. Я также беспокоился бы о том, что 's нужно будет скопировать, чтобы не изменять исходную строку, что снижает эффективность. - person rgchris; 19.02.2013
comment
Примечания к стилю: функция возвращает количество новых строк, а не количество строк — должна возвращать s + 1 в соответствии с «Принципом текстового редактора» (см. вопрос). - person rgchris; 19.02.2013
comment
Обратите внимание, что в Rebol 3 REMOVE-EACH возвращает количество удалений, а не измененную серию. Таким образом, ваше предложение может быть просто remove-each v s [v = #"^/"] в Rebol 3. (И это на самом деле первый случай использования, с которым я когда-либо сталкивался, когда полезно довольно странное возвращаемое значение REMOVE-EACH в R3.) - person earl; 27.09.2013

Интересно, почему никто не предложил самое простое решение :)

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.

person endo64    schedule 21.02.2013
comment
Решение while здесь — это то, что я имел в виду под своим встроенным комментарием find-all и оптимизации. Решение until может быть быстрее, так как until является более простым родным. i: i + 1 быстрее, чем ++ i в R2, но медленнее в R3. - person BrianH; 22.02.2013

Не самое эффективное, но, вероятно, одно из самых быстрых решений (в любом случае, если запустить тест, я хотел бы посмотреть, как это решение работает):

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

Не знаю насчет производительности и правила последней строки (r3).

>> length? parse "1^/2^/3" "^/"
== 3
person dt2    schedule 11.02.2013
comment
Меня беспокоит то, что он создает столько новых строк, сколько есть строк - это очень краткий однострочник, но я боюсь, что он делает больше, чем пример parse в вопросе. Хотелось бы услышать другие мнения... - person rgchris; 11.02.2013
comment
Другим недостатком этого подхода является причуда использования parse как split — он ломается при следующем условии: parse {one^/"two^/three"} "^/" - person rgchris; 11.02.2013
comment
Хорошие моменты. я сомневаюсь, что дополнительная память будет проблемой. я обманываю и не проверяю дополнительный gc :) Но обработка кавычек - неприятный сюрприз. - person dt2; 23.02.2013

хе-хе-хе длина чтения/строк? 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? 

Мне нравится программировать в реболе, это так забавно

Изменить. Я не читал весь пост, поэтому мое решение уже было предложено по-другому...

хорошо, чтобы исправить мой грех публикации уже опубликованного решения, я приведу комментарий к неожиданному поведению этого решения. Множественные возвраты каретки в цепочке не учитываются (с использованием rebol3 linux...)

>> a: "line1 ^M line2 ^M line3 ^M^M"
== "line1 ^M line2 ^M line3 ^M^M"

>> length? parse a newline 
== 3
person shadwolf    schedule 16.05.2013

person    schedule
comment
Интересный подход — есть мысли (кто-нибудь), как это работает? - person rgchris; 16.02.2013
comment
Интересно, Дариус! Кстати, у нас есть SO чат для Rebol, если вы хотите присоединиться к нам.. . - person HostileFork says dont trust SE; 16.02.2013
comment
Любые подходы к изменению, вероятно, будут медленными для длинных строк из-за накладных расходов на сдвиг ряда во время изменений. Но профайлер подскажет, какой подход будет лучше. - person BrianH; 20.02.2013