Поиск строк в Rebol или Red

Мне интересен поиск в большом количестве длинных строк, чтобы попытаться взломать sed-подобную утилиту в rebol как учебное упражнение. В качестве детского шага я решил найти персонажа:

>> 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". Но в чем преимущество интерпретатора, взорвающегося вместо того, чтобы делать что-то разумное, например, вернуть тестируемый "нулевой" char в pos?

Мне сказали, что я должен был это сделать:

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

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

>> pos
== 18

Действительно? Я должен искать строку TWICE; в первый раз, чтобы быть уверенным, что "безопасно" искать СНОВА?

Итак, у меня есть трехчастный вопрос:

  • Как бы мастер реализовал мою функцию поиска? Я предполагаю, что есть волшебный лучший способ лучше, чем это....

  • Красный собирается ли это изменить? В идеале я бы подумал, что find должно возвращать правильную позицию строки или NULL, если она попадает в конец строки (NULL с разделителями, могу ли я предположить?). NULL является FALSE, поэтому он будет настроен на очень простой тест.

  • Каков самый эффективный способ для работы с процессором после того, как у меня есть действующий индекс? В Rebol (хорошо) появляется так много вариантов, что можно застрять в выборе или застревать в субоптимальном выборе.

Ответ 1

Мне сказали, что я должен был это сделать:

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

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

>> pos
== 18

Действительно? Я должен искать строку TWICE; в первый раз, чтобы быть уверенным, что "безопасно" искать СНОВА?

Вам, разумеется, не нужно дважды искать строку. Но 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

Возможность бросать заданные слова в произвольных точках в среднем выражении довольно мощна, и почему такие вещи, как множественная инициализация (a: b: c: 0), не являются особыми особенностями языка, а что-то, что выпадает из модели оценщика.

Красный собирается ли это изменить?

Неверно, что преимущества index? (cough index-of) возвращают NONE! значение, если дано NONE! вход перевешивает проблемы, которые он может вызвать, будучи настолько терпимыми. Это всегда баланс.

Обратите внимание, что FIND действительно ведет себя так, как вы ожидаете. НАЙДЕННЫЙ? это просто синтаксическое удобство, которое преобразует позицию, найденную в истинное значение, и NONE! вернулся в ложный. Это эквивалентно вызову TRUE? (но немного более грамотным при чтении). Нет необходимости использовать его в состоянии IF или UNLESS или EITHER... поскольку они будут обрабатывать результат NONE, как если бы он был ложным, и любое положение, как если бы оно было истинным.

Каков наиболее эффективный способ для эффективного выполнения замены, как только у меня есть действующий индекс?

То, что было бы самым быстрым, вероятно, было бы нависшим на этой позиции, и сказал change pos #"x". (Хотя внутренние "позиции" реализуются с помощью индекса плюс серии, а не независимого указателя. Таким образом, преимущество не в том, что важно в мире микрооптимизации, где мы учитываем такие вещи, как добавление смещений...)

Как для какой операции с индексом: я бы сказал, как выбрать, как вам лучше всего, и микро-оптимизировать позже.

Я лично не думаю, что STR/:index: #"x" выглядит так здорово, но он самый короткий в символах.

STR/(index): #"x" делает то же самое и выглядит лучше ИМО. Но из-за того, что структура исходного кода взорвалась немного. Это SET-PATH! серия, содержащая PAREN! серии, за которой следует CHAR!... все встроенные в исходный ряд "вектор", содержащий код. Под капотом будут проблемы с местностью. И мы знаем, насколько важно это в наши дни...

Вероятно, самый наивный POKE - самый быстрый. poke STR index #"x". Он может выглядеть как "4 элемента вместо 2", но "2 элемента" случаев пути являются иллюзией.

В Rebol всегда сложно догадаться, поэтому вам нужно собрать данные. Вы можете запустить несколько повторных итерационных тестов, чтобы узнать. Ко времени блока кода см. Встроенный delta-time.

В Red, скомпилированные формы должны быть эквивалентными, но если каким-то образом это завершается, то вы, вероятно, будете иметь аналогичные тайминги для Rebol.

Ответ 2

Никаких сюрпризов, что ответ HostileFork не покрывает все красиво! +1

Просто хотел добавить альтернативное решение для пункта 1, которое я использую регулярно:

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

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

Онлайн-документация для Rebol 2 attempt и Rebol 3 attempt

Ответ 3

Поиск строк в 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

Это были примеры безопасных способов его достижения, в зависимости от ваших потребностей. Обратите внимание, что none действует как false для условных тестов в if или either, так что использование found? в этом случае является излишним.

Теперь давайте пролить свет на основную проблему, которая вызвала у вас путаницу.

Языки реболов имеют фундаментальное понятие, называемое series, из которого выводится тип данных string!. Понимание и использование правильных серий является ключевой частью возможности использования языков Rebol в идиоматическом ключе. Серии выглядят как обычные списки и строковые типы данных на других языках, но они не совпадают. Серия состоит из:

  • список значений (для строк, это список символов)
  • неявный индекс (мы можем назвать его курсором для простоты)

В следующем описании будут сосредоточены только строки, но те же правила применяются ко всем типам данных серии. Я буду использовать функцию index? в приведенных ниже примерах, чтобы отобразить неявный индекс как целое число.

По умолчанию при создании новой строки курсор находится в главном положении:

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

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

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

Теперь вернитесь в свой прецедент для поиска в строках.

>> 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, чем на простые индексы целого числа.

Теперь, вот мои вопросы:

  • Не требуется волшебство, просто используйте функции серии и find, как показано выше.

  • Красный не изменит это. Серия является краеугольным камнем того, что делает языки Rebol простыми и мощными.

  • change должен быть самым быстрым способом, хотя, если у вас много замен для работы с длинной строкой, восстановление новой строки вместо изменения оригинала приводит часто к лучшим характеристикам, поскольку это позволит избежать перемещение кусков памяти вокруг, когда строки замены не имеют того же размера, что и часть, которую они заменяют.