ЙажаСценарий / Говнокод #18737 Ссылка на оригинал

0

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
function coolSplit(str, pattern) {
  var result = [];
  while(1){
    var m = str.match(pattern);
    if(!m) {
      if(str) result.push(str);
      return result;
    }
    if(m.index) result.push(str.substr(0, m.index));
    result.push(str.substr(m.index, m[0].length));
    str = str.substr(m.index + m[0].length);
  }
}

Из шифроскрипта. https://github.com/1024--/govnokod.ru-userscripts/commit/898e6195b9799853b08a01834ce55f8d780757f3#diff-40cd32e1559d395399816968845cb034R151

coolSplit(str, /expr/) ≡ str.split(/(expr)/) // Говнокод обучающий

Запостил: 1024-- 1024--, (Updated )

Комментарии (44) RSS

    • Что именно ты для себя открыл?

      Если уж на то пошло - как сматчить ^(regexp)+\+(regexp)$? Т.е. идет несколько regexp, между ними плюсы. Без re.split.
      Ответить
      • Открыл режим split, который оставляет разделители вместе с разделяемым.

        > как сматчить ^(regexp)+\+(regexp)$? Т.е. идет несколько regexp, между ними плюсы. Без re.split.
        А, то есть regexp может быть фигнёй вида /<[^>]*>/ и соответствовать строке "<+>", из-за чего простой split невозможен...

        В жс можно было бы
        1. в цикле матчить один регексп и откусывать начало строки (как в данном ГК)
        2. в цикле матчить, следя за lastIndex
        3. использовать функци-анальный форич-подход с replace *:
        var pattern = /<[^>]+>/g;
        var fullPattern = new RegExp('^(' + pattern.source + '\\+)+?' + pattern.source + '$');
        var string = '<12>+<3+4>+<5>';
        
        if(!fullPattern.test(string)) throw new Error;
        
        // магия:
        var matches = [];
        string.replace(pattern, function(m){
          matches.push(m);
        });
        
        console.log(matches); // ["<12>", "<3+4>", "<5>"]


        В PHP, кажется, была функция "всё заматченное для шаблона" (по-моему, preg_match_all). Там надо было бы проверить на соответствие этому самому ^(?:(regexp)\+)+(regexp)$, а затем вызвать эту функцию с симметричным регекспом /(regexp)\+?/ - шаблон и необязательный разделитель. *

        ______________
        * если шаблон не начинается с плюса
        Ответить
        • >в цикле
          Нахуй.

          Регексп - str1|str2|str3. Может и + содержать.
          Ответить
          • >>в цикле
            >Нахуй.
            Я же упомянул про preg_match_all. В питоне мог быть и аналог.

            Вышеописанный подход на питоне:
            pattern = r'123|1\+2|1|2' # аккуратно записываем
            string = '123+1+2+2+1+2+1'
            
            # 1. проверка на соответствие
            if re.match('^((' + pattern + r')\+)+(' + pattern + ')$', string) is None:
              raise Exception('Invalid format')
            
            # 2. получение строк
            re.findall(pattern, string) # ['123', '1+2', '2', '1+2', '1']

            Если у шаблона нет вореций, брать или не брать плюс в начале/конце (например, r'1\+|1' или r'\++1'), такую технику можно использовать.

            re.match(r'^((1\+|1)\+)+(1\+|1)$', '1++1+1+') is None # False, всё работает
            re.findall(r'1\+|1', '1++1+1+') # ['1+', '1+', '1+'], фигня
            Ответить
            • Тьфу, к строке можно же добавить разделитель и рассмотреть симметричный случай /^((pattern)\+)+$/ (ну или /^((pattern)\+){2,}$/), это же элементарно:
              pattern = r'1\+|1'
              string = '1++1+1+'
              
              if re.match('^((' + pattern + r')\+)+$', string + '+') is None:
                raise Exception('Invalid format')
              
              re.findall('(' + pattern + r')\+', string + '+') # ['1+', '1', '1+']

              Тогда наверно и ограничений нет, кроме того, что в pattern не должно быть захватывающих скобок.
              (Кстати, в коде из предыдущего комментария в pattern тоже не должно их быть)
              Ответить
              • В 2 приема я и сам умею (только я делал split и проверял есть ли в нем что-то кроме "+").

                Разбей мне в один заход 1+2+3 на \d+ разделенные "+", не сплитя по +, или выдай ошибку если формат не тот.
                Ответить
                • >>> re.split(r"(\d+)", "11+222+3")
                  ['', '11', '+', '222', '+', '3', '']
                  Пойдёт? Или надо проверить на то что там числа подряд не идут, а всегда разделены плюсиком и .т.п?
                  Ответить
                  • > а всегда разделены плюсиком
                    Да, формат строгий, при нарушении надо материться.
                    Ответить
                    • Заебали юзать регулярки для задач, для которых они не предназначены... Тут уже более серьёзные инструменты нужны, типа грамматик:
                      from pyparsing import Word, nums, delimitedList
                      
                      myListParser = delimitedList(Word(nums), "+")
                      print(myListParser.parseString("1+222+33", True))
                      Ответить
                      • Но я ее решил регулярками...

                        В чем прикол начинать модули для питона с py?
                        Ответить
                      • Прикольно, но регулярки легче портировать, например.

                        Да, цифры я для примера привел, в реальности там было
                        >Регексп - str1|str2|str3. Может и + содержать.
                        Ответить
                        • Зато читать и поддерживать код с мало-мальски сложными регулярками - ёбаный ад. А это делается чаще, чем портирование.
                          Ответить
                          • Тру. Но тут регулярки сравнительно простые, а поддерживать особо не надо. Короче, это весьма частный случай.
                            Ответить
                            • Кстати, оказалось, что pyparsing умеет не только матчить с начала строки, но и искать. У него там есть scanString(), которая возвращает генератор, возвращающий найденные куски.

                              Т.е. можно искать фрагменты внутри хтмл и.т.п.
                              Ответить
                        • Дык, pyparsing может парсить, если + - это и разделитель, и часть слов?
                          Ответить
                          • Ну если там какие-нибудь экраны перед плюсом внутри слов есть или слово ограничено какими-нибудь скобками или кавычками - то запросто. А если нет - то как вообще отличить, разделитель это или нет?

                            А вообще - все эти парсеры намного сильнее регулярок. Рекурсивную херню типа XML или выражений со скобочками они запросто сожрут и не подавятся.
                            Ответить
                            • >как вообще отличить, разделитель это или нет
                              Если он входит в возможное слово - то не разделитель. См. здесь http://govnokod.ru/18737#comment298410

                              >А вообще - все эти парсеры намного сильнее регулярок.
                              Да я понял уже. Но у меня ситуация вполне конкретная.

                              А вообще интересно было бы увидеть парсер конфигов на нем. Где-то я что-то похожее видел, в pyload, кажется.
                              Ответить
                                • Да точно так как в коменте по ссылке! т.е. там константы с плюсом, а не выражения.
                                  Ответить
                                    • Работает. Но мне желателен более строгий парсинг (чтобы даже пробелов лишних не было), т.к. это может значить, что изменился формат входных данных и придется менять логику. Да и портируемость лучше.
                                      Ответить
                                      • Пожирание пробелов там отключается в одну строчку. Просто обычно удобнее не писать их в грамматике явно. Поэтому по-умолчанию так.
                                        Ответить
                                        • Ну крч, в данном случае меня регекспы устраивают, но на будущее учту. Читабельность лучше, это да.
                                          Ответить
                                          • > читабельность
                                            Ну и можно делать парсеры для фрагментов, комбинировать их в бОльшие, реюзать, делать рекурсию и т.п. С регулярками такое не катит.
                                            Ответить
                      • > Заебали юзать регулярки для задач, для которых они не предназначены...
                        Юзаем регулярки для задач, для которых они не предназначены с 60х годов и гордимся этим!

                        Но ведь функции для всего, что мы делаем, есть!
                        Проверить на (A\+)+A мы можем. Одновременно проверить и заматчить A в A\+A мы можем. Почему бы одновременно не проверить и заматчить все A в (A\+)+A, не притягивая более серьёзные инструменты?
                        Ответить
  • Вернемся к нашим баранам. Почему (\d+\+)+(\d) не матчит 1,2,3,4 из "1+2+3+4" и как это можно было сделать? Кто подскажет?
    Ответить
    • А это потому что скобки в re не умеют захватывать много раз (захватывается только самое последнее совпадение). Навскидку нагуглился модуль regex, который умеет (см. функцию captures()).

      Ну или юзать унылый цикл с откусыванием по кусочку или по результатам split()/findall().
      Ответить
      • Насчет split я писал- он не сработает, если разделитель входит в возможные значения. Как это с findall сделать?

        > скобки в re не умеют
        Он же вроде PCRE совместимый?
        Ответить
            • Ну, теперь 1 заход + проход по числам:
              pattern = r'1\+|1'
              string = '1++1+1+'
              
              found = re.findall('(' + pattern + r')\+', string + '+')
              
              if reduce(lambda x,y: x+y+1, map(len, found)) != len(string):
                raise Exception('Invalid format')
              Ответить
              • >reduce(lambda x,y: x+y+1, map(len, found)
                Говно. sum(map(len, found)) + len(found) -1 же. Или len("".join(found)) + len(found) -1. Но все равно дополнительная проверка.

                Короче я решил так.
                In [58]: re.split(pattern, string)
                Out[58]: ['', '+', '', '']

                Дальше проверяем чтобы список состоял из пустых строк и +, или если строже - '' в начале и конце, остальное + (что-то в твоем коде не то)
                Ответить
                • > что-то в твоем коде не то
                  Сам такой!

                  > Дальше проверяем чтобы список состоял из пустых строк и +
                  Если просить заматчить ещё и разделитель вместе со строкой, в предложенном мной случае /1\+|1/ и '1++1+1+' оно не съест лишний раз "1+" (что и произошло в коде выше), когда этот плюс будет разделителем.
                  Тогда в моём случае будет
                  re.split('(?:' + pattern + ')\+', string + '+') # ['', '', '', '']

                  И список всегда будет состоять только из пустых строк.
                  Ответить
                  • >Если просить заматчить ещё и разделитель вместе со строкой
                    Но зачем? Он же разделитель.

                    >Сам такой!
                    Ну сам смотри.
                    >'' в начале и конце, остальное +

                    У меня такого не было тк + был внутри, так что я об этом как-то не задумывался.
                    Ответить
                    • > Но зачем? Он же разделитель.
                      > У меня такого не было
                      Вот ради таких случаев, когда разделитель на краю строки.
                      Ну и ради равномерности: ['', '', '', ''] против ['', '+', '+', '']
                      И ради равномерности регулярки для всей строки: ^(A\+)+$ вместо ^A(\+A)*$. С этого-то всё и началось.
                      Ответить
        • > Как это с findall сделать?
          Ну я представлял это как вызвать findall и в цикле проверить, что все найденные куски лежат друг за другом (разделитель бы захватывался в конце кусков). Но хуёвый вариант, да.

          Откусывание по одному фрагменту проще всего смотрится из всего этого говна.

          > Он же вроде PCRE совместимый?
          Хер знает, если честно. Я особо не заморачивался. Сам потестил - скобка с плюсом после неё захватывает только последний кусок (матчит само собой всё - group(0) == "1+2+3+4", но захватывает только последний: group(1) == "3+"). Посмотрел на stackoverflow - народ жалуется, что захватывается только последний и предлагает юзать regex вместо re. В спеке как всегда о таких тонкостях умалчивается (питон стайл, хули). Вот и всё что мне известно по этой проблеме.
          Ответить
            • http://www.rexegg.com/regex-capture.html

              All engines return the last value captured. For instance, if you match the string A_B_C_D_ with ([A-Z]_)+, when you inspect the match, Group 1 will be D_.

              Ну т.е. как в питоне. За исключением движков, которые умеют массивы в группах возвращать (например, тот самый regex, о котором я писал выше).
              Ответить
              • А, т.е. последовательное сканирование в регекспах вообще не было предусмотрено? Интересно.
                Ответить

Добавить комментарий

Я, guest, находясь в здравом уме и твердой памяти, торжественно заявляю:

    А не использовать ли нам bbcode?


    8