1. JavaScript / Говнокод #18737

    +2

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    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--, 18 Сентября 2015

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

    • Спасибо, 3_14dar!
      http://govnokod.ru/18688#comment297468
      Ответить
      • Что именно ты для себя открыл?

        Если уж на то пошло - как сматчить ^(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, кажется.
                                Ответить
                                • > Если он входит в возможное слово - то не разделитель.
                                  Что-то типа такого нужно? http://ideone.com/RKaR6K
                                  Ответить
                                  • Да точно так как в коменте по ссылке! т.е. там константы с плюсом, а не выражения.
                                    Ответить
                                    • > константы
                                      Ну с константами даже проще...

                                      http://ideone.com/5E5v2p
                                      Ответить
                                      • Работает. Но мне желателен более строгий парсинг (чтобы даже пробелов лишних не было), т.к. это может значить, что изменился формат входных данных и придется менять логику. Да и портируемость лучше.
                                        Ответить
                                        • Пожирание пробелов там отключается в одну строчку. Просто обычно удобнее не писать их в грамматике явно. Поэтому по-умолчанию так.
                                          Ответить
                                          • Ну крч, в данном случае меня регекспы устраивают, но на будущее учту. Читабельность лучше, это да.
                                            Ответить
                                            • > читабельность
                                              Ну и можно делать парсеры для фрагментов, комбинировать их в бОльшие, реюзать, делать рекурсию и т.п. С регулярками такое не катит.
                                              Ответить
                        • > Заебали юзать регулярки для задач, для которых они не предназначены...
                          Юзаем регулярки для задач, для которых они не предназначены с 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 совместимый?
          Ответить
          • > Как это с findall сделать?
            Уже было в http://govnokod.ru/18737#comment298410
            Ответить
            • В один заход же.
              Ответить
              • Ну, теперь 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. В спеке как всегда о таких тонкостях умалчивается (питон стайл, хули). Вот и всё что мне известно по этой проблеме.
            Ответить
            • А как PCRE должно себя вести?
              Ответить
              • 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, о котором я писал выше).
                Ответить
                • А, т.е. последовательное сканирование в регекспах вообще не было предусмотрено? Интересно.
                  Ответить

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