1. C++ / Говнокод #17558

    +53

    1. 1
    2. 2
    3. 3
    4. 4
    5. 5
    6. 6
    for (int i = 1; i <= s.Length; ++i) {
        if (s[i] == '/') {
            s = s.SubString(1, i) + s.SubString(i, MaxInt);
            ++i;
        }
    }

    Кручу-верчу запутать хочу. Кто с первого раза догадается, в чём задача кода - получит пирожок с полочки.

    P.S. Строки билдеровские, нумерация с 1. SubString принимает индекс начала и количество символов.

    Запостил: bormand, 02 Февраля 2015

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

    • Убирает одиночные /, но превращает // в /, так что ли?
      Ответить
      • Боюсь, что наоборот.
        Ответить
        • Да, АнсиРеплейсСтр(с, '/', '//').
          Ответить
          • Кстати, Делфи бы аналогичный код бы зарезало при компиляции. Знаешь, на чём?
            И да, я считаю это ограничение очень полезным.
            Ответить
            • На
              s.SubString(i, MaxInt)

              ?
              Ответить
            • На инкременте i ?
              Ответить
            • Ну да, нехуй счётчик менять в цикле... Но суть в том, что в делфи подобный код был бы написан через while (т.к. длина s меняется по ходу работы, а паскаль кеширует границы цикла перед его стартом). А там уже никто не запретит менять i...
              Ответить
              • i := 1;
                while i <= s.Length do
                  begin
                    if s[i] = '/' then
                      begin
                        s := s.SubString(1, i) + s.SubString(i, MaxInt);
                        inc(i);
                      end
                    inc(i);
                  end
                Ответить
                • как говаривал Викинг из SC2 - нет предела беспределу
                  Ответить
                • всё это безобразие на дельфях реализуется одной строкой:
                  s := AnsiReplaceStr(s, '/', '//');
                  или регистронезависимо:
                  s := AnsiReplaceText(s, '/', '//');

                  Зачем изобретать велосипед?
                  Ответить
                  • > Зачем изобретать велосипед?
                    Чтобы был, ради стёба же. А настоящее решение с AnsiReplaceStr() я и так выше упомянул. Оно и для билдера и для делфи подходит, либа то одна.

                    > регистронезависимо
                    Заглавный слеш это что-то новое...
                    Ответить
              • Конечно, любое ограничение можно обойти, но сам факт того, что компилятор обращает внимание, может заставить быдлокодера задуматься, а правильный ли способ он выбрал вообще.
                Ответить
                • Компилятор Дельфи заставляет также задуматься, правильный ли язык он выбрал вообще ;D
                  Ответить
                  • Честно говоря, компилятор крестов задаёт этот вопрос намного чаще. Особенно когда шаблоны, отлаженные в студии, портируешь на жосиси.
                    Ответить
          • а в плюсах обычного реплейса нет?
            Ответить
            • Дык s это делфёвый AnsiString...

              А вообще, емнип, нету.
              Ответить
              • чет я ничего не понял, ну да ладно.
                Ответить
              • std::string::replace
                Ответить
                • Ага... ты сам то этот std::string::replace() юзал? Эту хуйню разве что в цикл совместно с find пихать... Это скорее splice, чем replace.
                  Ответить
                  • Юзал, в цикле с find.
                    Ответить
                    • любишь квадратичные алгоритмы?
                      Ответить
                      • >>любишь квадратичные алгоритмы?

                        спанч боб чтоли?
                        Ответить
                      • Для маленьких строк сойдет. Хотя можно крутить итераторы, чтобы за один проход.
                        Ответить
                        • Зачем?
                          На входе характер c
                          На выходе 'c' != '/' ? c : "//"
                          Ответить
                          • 1)'c' != '/' это всегда true
                            2)Нахуй типизацию!
                            Ответить
                            • Прошу прощения, накосячил
                              c != '/' ? c : "//"

                              конечно же
                              Ответить
                            • > Нахуй типизацию!
                              э?
                              Ответить
                              • с - это char
                                "//" - const char *
                                Ответить
                                • в быдлере это скорее broland::turbostring или чтото подобное, но непреодолимым препятствием не является
                                  Ответить
                                  • {facepalm}
                                    Самое ужасное то, что ты даже не понял, какую ужасную хуйню сморозил.
                                    Ответить
    • Заменяет все "/" на "//" штоле?
      Ответить
    • Экранирование такое экранирование. Изи катка.
      Ответить
    • показать все, что скрытоЭтот чудо-алгоритм из разряда: "смотри на мой код и никогда так не делай".
      Даже если он работает, то подобен человеку на одноколёсном велосипеде, который может ебнуться от малейшего изменения.
      Ответить
      • В чем проблема то? Ну неэффективно конечно, но вроде никаких скрытых проблем.
        Ответить
        • > никаких скрытых проблем
          Т.е. то, что 8 из 10 опрошенных сказало "на первый взгляд, этот фрагмент удаляет слеши" не является проблемой? А ведь весь этот код вполне заменяется на один няшный replace...
          Ответить
          • Я думал что одноколесный велосипед что-то более серьезное подразумевает. А так - да, ГК конечно. А может его автор из всех функций только SubString использует т.к. на клавиатуре не все клавиши работают? Хотя он мог бы +'\'+ вставить, для неосиляторов, но он решил им хоть чуть-чуть отомстить, за то что приходится работать на такой клавиатуре.
            Ответить
            • > А может его автор из всех функций только SubString использует
              Так и есть. У него чёрный пояс по SubString()'ам и Pos()'ам. И он любые операции над строками в этом базисе выражал... Вот еще выдержка из того кода (набросал по памяти, не ругайте):
              while (s.Pos('\n') > 0) {
                  int p = s.Pos('\n');
                  s = s.SubString(1, p-1) + "/n" + s.SubString(p+1, MaxInt);
              }
              Ответить
              • Учитывая хреновые способности каплунатора по кешированию, получается печально ваще.
                Ответить
            • показать все, что скрыто>Я думал что одноколесный велосипед что-то более серьезное подразумевает.
              Подразумевает то что автор изобретает велик, но какой-то не очень удобный и медленный. А еще "8 из 10" не понимают как на нём ездить.
              Изменение счётчика в цикле порождает хрупкий и откровенно херово сопровождаемый код.
              Ответить
    • Строки бандеровские
      Ответить
    • Го в доту.
      Ответить
    • > нумерация с 1
      > C++

      Что за ебанутость?
      Ответить
      • Для совместимости с делфнёй они в c++builder так сделали.
        Ответить
        • так надо было пойти дальше и в строке размер в первом байте хранить
          Ответить
          • Зачем? Им же с турбо паскалем не нужна была совместимость.
            Ответить
            • в дельфи уже не паскалевые строчки были?
              я думал паскалевые, но двухбайтные
              Ответить
              • Если я не гоню, то в делфи AnsiString (он же и string) был с полноценной длиной (32 бита) и умел COW.
                Ответить
                • так а длина то где была: в первых 4 байтах или нулом?

                  коу? он был мутабельный? оамама
                  Ответить
                  • Мутабельные конечно. Длина хранилась числом, но и ноль в конце был для совместимости с сишными функциями. Вроде даже не в первых 4 байтах, а что-то в духе {uint32_t size; uint32_t refcount; char* data;}.
                    Ответить
                    • рефкаунт? А он там был автоматом (как у яблочников) или его надо было вручную увеличивать/уменьшать?

                      мутабельные строки это охуенно
                      их удобно использовать в качестве ключа в ассоциативном массиве

                      Кстати! Знаете прикол про жабоебов и substring в седьмой+ жабе?
                      Ответить
                      • > ключа в ассоциативном массиве
                        COW строки прекрасно работают ключами в ассоциативных массивах. Лишь бы контейнер их по неконстатной ссылке не отдавал, когда перечисляет свои ключи.

                        > substring в седьмой+ жабе
                        Ну да, теперь он всегда копирует байтики. Кстати, а они не оставили какой-нибудь конструктор, чтобы создавать именно view на старую строку?
                        Ответить
                        • может быть можно получить массив чаров и оттуда сделать вью, я не уверен
                          Ответить
                      • > автоматом
                        Автоматом.
                        Ответить
                        • я просто дельфи видел три раза примерно 18 лет назад

                          а вот в поцкале управление кучей было ручное, как в сишечке: getmem/freemem

                          я уверен был что и в дельфях было так же
                          Ответить
                          • Так же. Но в Дельфях (начиная с какой-то версии) появились строки с автоматическим управлением.
                            Ответить
                            • А остальные объекты?

                              Просто ващет говоря реализация автоматического рефкаунта это не оч простая задача, очень круто что борланд решил ее
                              Ответить
                              • > борланд решил
                                Там, на самом деле, майкрософт её решил, когда COM проектировали. А в делфи просто добавили поддержку COM-интерфейсов. А обычные объекты один хрен руками контролировали...
                                Ответить
                                • В COM управление рефами полуручное же.
                                  Надо же IUnknown::AddRef делать
                                  нет?
                                  Ответить
                                  • > В COM управление рефами полуручное
                                    От языка зависит... Где прикрутили нативную поддержку COM, как в делфи или VB - там конпелятор в нужных местах сам эти вызовы втыкает.

                                    В крестах, в принципе, тоже можно руками ничего не делать (смартпоинтеры).

                                    Вручную AddRef дёргают разве что в няшной.
                                    Ответить
                                    • понятно. Интересно, ATL как-нить помогает крестовикам прятать это все в смартпоинтеры и вызывать Release в деструкторе?

                                      В общем я понял (благодаря тебе) что рефакаунтинг вовсе не так сложно, как я думал. Правда, в нем некоторое количество лишних действий (например inc на каждый return, если не испозовать policy как у яблока), но мне стало совсем не понятно зачем выдумали GC.

                                      Неужели только ради циклических ссылок (которые рефкаунту обычно не разрулить)?
                                      Ответить
                                      • > Неужели только ради циклических ссылок (которые рефкаунту обычно не разрулить)?

                                        GC собирает объекты в ширину, RC — в глубину. Т.е. если сделать глубокое дерево, наивное RC может порвать стек когда корень выйдет из скопа. Односвязный список — это как раз такое дерево, к примеру.
                                        Ну и GC компактификацию кучи может делать.
                                        Ответить
                                        • Зато он будет рвать его каждый раз в заранее заданный момент:)

                                          >>компактификацию
                                          Это как дефрагментацию, да?

                                          Интересно было бы совместить оба подхода, чтобы программист мог сам выбирать поведение для программы. У яблов можно было когда-то опционально включать GC, но давно уже нет
                                          Ответить
                                        • Ну и GC не тратит время на удаление объектов, только на спасение выживших. Т.е. можно генерить гигабайты мусора без особого оверхеда.

                                          Минус - очень дорогая и недетерминированна я финализация объектов, если она вдруг понадобится.
                                          Ответить
                                      • > например inc на каждый return, если не испозовать policy как у яблока
                                        Это не так, погугли RVO. Оптимизирующий компилятор может выкидывать лишние копирования указателей и за счет этого устранять лишние пары инкримент-декримент. Кроме того, в плюсах и в расте в язык встроена мувсемантика, за счет которой временные смартпойнтеры не копируются, а перемещаются, не меняя счетчик ссылок. Так что там даже без специальных оптимизаций со стороны компилятора не будет лишних операций с рефкаунтером при возврате указателя из функции.
                                        Ответить
                                        • Там Борманд винзу писал про оптимизацию inc/dec.

                                          Как у вас всё хорошо. стану крестобляпрограммистом, пожалуй
                                          Ответить
                                          • > оптимизацию инк/дек
                                            В крестах за счёт мув семантики и RVO лишние вызовы инкремента уйдут.
                                            Ответить
                                          • Смотри, метухом не стань.
                                            Ответить
                                            • Буду писать оче умные шаблоны, а ты будешь потом ошибки компиляции разгребать
                                              Ответить
                            • И интерфейсы которые рефкаунтятся и сами зовут деструктор.
                              Ответить
                              • Ну вот тебе вопрос про самое больное место авторефкаунта

                                Функция Foo создала на куче объект и вернула нанего ссылку.

                                У объекта каунт=1, иначе бы он сразу же умер.

                                Взявший его код увеличил каунт на 1. Получилось 2.
                                А после ухода указателя из области видимости опять стало 1.

                                Кто уменьшит этот 1 до ноля?
                                Ответить
                                • Конпелятор должен воткнуть уменьшение счётчика во всех местах, где ссылка выходит из скопа.
                                  Ответить
                                  • вот тебе пример на псевдокоде

                                    function Foo() {
                                    return new Bar();
                                    }

                                    в каком месте компелятор тут что воткнет?
                                    Ответить
                                    • В месте вызова Foo компилятор должен будет вызвать деструктор временного объекта, который вернется из Foo.
                                      Ответить
                                      • прямо деструктор? А если я его еще собираюсь использовать?

                                        bar = Foo();
                                        //и тут использую bar
                                        Ответить
                                        • Ну вот ты скопировал временный объект (указатель) в bar, увеличив рефкаунтер, и после точки с запятой компилятор снесет этот временный указатель, уменьшив рефкаунтер обратно. А bar компилятор снесет в конце скоупа и удалит объект, на который тот указывал, т.к. рефкаунтер уменьшится до нуля.
                                          Ответить
                                          • Откуда компилятор знает что-то про временный объект?

                                            Он знает что есть функция, возможно в библиотеке, которая возвращает какой-то указатель.

                                            Вот я получил этот указатель. Его рефкаунт должен быть больше ноля (иначе объекта бы уже не было), значит, увеличить должна была функция в момент создания.

                                            Хорошо. Тогда я должен всегда уменьшать его на один как только я получил его из функции.

                                            но что если функция его не создает, а возвращает синглтон или там static storage duration?

                                            Как клиенту узнать -- создала-ли его функция специально для меня (чтобы я уменьшил каунтер) или нет?
                                            Ответить
                                            • > но что если функция его не создает, а возвращает синглтон или там static storage duration?
                                              На них же держит ссылку некая глобалка, не тупи.
                                              Ответить
                                              • Кто держит?

                                                Как этот кто-то узнает что его можно удалить?

                                                А если это филд объекта?
                                                Ответить
                                                • > Как этот кто-то узнает что его можно удалить?
                                                  Ну вот выйдет у тебя глобалка из скопа (аля конец программы) или в неё ссылку на другой объект или null засунут - тогда и удалится.

                                                  > если это филд объекта
                                                  Сдохнет объект и позовётся декремент на всех его полях, которые содержат не null ссылки. Очевидно же.

                                                  З.Ы. Вот теперь настоящий Сёма детектед.
                                                  Ответить
                                                  • допустим там объект который должен удалиться рано или поздно. На него у кого-то есть ссылка


                                                    Foo() возвращает этот объект

                                                    ты хочешь сказать что на каждый вызов Foo() будет декремент каунта?
                                                    Ответить
                                                    • > на каждый вызов Foo() будет декремент каунта
                                                      Да, будет. На каждый, блять, вызов. Хотя конпелятор имеет право убрать пару из подряд идущих инкремента и декремента (например в b = Foo() или return Foo()), чтобы время зря не тратить.

                                                      Всё, заебало, пойду лучше в кружки поиграю.
                                                      Ответить
                                                      • >>Да, будет. На каждый, блять, вызов.

                                                        тогда синглтону пиздец придет

                                                        его каунт станет равен нолю через пару вызовов

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


                                                        То, о чем я говорю, называется Ownership Policy, и это совсем не так просто как вы тут с субару пытаетесь мне рассказать.
                                                        Ответить
                                                        • > его каунт станет равен нолю через пару вызовов
                                                          Дык он же инкрементит его каждый раз, когда отдаёт кому-то. Поэтому баланс инкрементов и декрементов соблюдается.
                                                          Ответить
                                                          • Касается-ли это только синглтона, или любая функция возвращая указатель на что-то должна его инкрементить?
                                                            Ответить
                                                            • > любая функция возвращая указатель на что-то должна его инкрементить
                                                              Да, так. Именно так работают intrusive ptr и shared_ptr в крестах.

                                                              З.Ы. Почитал этот ваш Ownership Policy - те же яйца, только сбоку. If an existing object is returned, its retain count is incremented so it is your responsibility to relinquish ownership.
                                                              Ответить
                                            • > Тогда я должен всегда уменьшать его на один как только я получил его из функции.
                                              Верно.

                                              > что если функция его не создает, а возвращает синглтон или там static storage duration?
                                              Тут есть две опции. Одна - у владеющих и невладеющих указателей должен быть разный тип. Вторая - у указателя должен быть флажок, по которому в рантайме можно узнать, владеет ли он объектом.
                                              Ответить
                                              • да, это два возможных решения

                                                третье это по имени функции понимать policy: создающие объект и возвращающие существующий могут иметь разные префиксы

                                                Как видишь автоматический рефкаунтинг не так прост, все решения не выглядят слишком элегантнор
                                                Ответить
                                                • > создающие объект и возвращающие существующий могут иметь разные префиксы
                                                  Кстати, а как Ownership Policy в ObjectiveC дружит с многопоточностью?

                                                  По Get Rule мне вернули существующий объект и, если он мне нужен, я должен позвать CFRetain. А если за это время объект уже сдох?

                                                  З.Ы. А, ну ясно: Moreover, it is usually not possible to achieve absolute thread safety at this level. You cannot rule out, for example, indeterminate behavior resulting from retaining an object obtained from a collection. The collection itself might be freed before the call to retain the contained object is made.

                                                  Дооптимизировались. Описанная мной в этом треде политика, когда во всех случаях работает Create Rule, выглядит проще и безопасней (хотя и чуть медленнее из-за инкремента/декремента).
                                                  Ответить
                                    • > в каком месте компелятор тут что воткнет?
                                      В принципе, subaru всё правильно объяснил.

                                      Ну всё тривиально же:
                                      1) new возвращает временную ссылку со счётчиком 1
                                      2) a = b инкрементит счётчик у b и декрементит у a (если там был не null)
                                      3) return инкрементит счётчик
                                      4) выход ссылки из скопа декрементит счётчик
                                      5) пару inc+dec можно выбросить, если хочется оптимизации

                                      function Foo() {
                                          return new Bar();
                                          // new вернуло временную ссылку со счётчиком 1
                                          // return увеличил её на 1
                                          // на ; временная ссылка вышла из скопа,
                                          // счётчик уменьшился на 1 (теперь в нём 1)
                                          // конпелятор вправе убрать пару inc+dec
                                      }
                                      
                                      function Baz() {
                                          Bar b = Foo();
                                          // как мы видим выше, Foo() вернуло
                                          // временную ссылку с count=1
                                          // b = ... увеличило её на 1
                                          // на ; уменьшили на 1
                                          // конпелятор вправе убрать пару inc+dec
                                      
                                          // ...
                                      
                                          // b вышло из скопа, счётчик уменьшается до 0
                                          // объект умирает
                                      }
                                      Ответить
                                      • стоп

                                        почему

                                        a = b
                                        вдруг уменьшает рефканут у b?

                                        А если Foo() вернет ссылку на синглтон то через пару вызовов он помрет/
                                        Ответить
                                        • Ок, я не совсем понятно описал - a = b уменьшает счётчик у того объекта, который был в a и увеличивает у того, который был в b.

                                          > ссылку на синглтон
                                          Как он блять помрёт, если реализация синглтона держит в себе ссылку на этот объект? Чтобы её же вернуть второму клиенту, который придёт после тебя?
                                          Ответить
                                          • Ладно, давай в псевдокоде опять

                                            class Foo{

                                            function bar(){return this}
                                            function eggs(){return new Spam();}
                                            }
                                            ///в другом месте////////

                                            foo = new Foo();
                                            foo->bar();
                                            foo->eggs();

                                            На последней строчке нам возвращается объект с рефкаунтом 1 и надо бы как-то его уменьшить, иначе он не помрет никоглда


                                            на предпоследней строчке нам возвращается объект с рефкаунтом 1, но его уменьшать не надо потому что метод его рефакунт не увеличивал.

                                            Учти что компилятор НЕ ЗНАЕТ что там внутри класса Foo происходит.

                                            Вызовы для него одинаковы, а поведение должно быть разное.

                                            Если он всегда будет уменьшать счетчик, то два вызова bar() разрушат объект

                                            а если не будет -- то после вызова bar() будет утечка
                                            Ответить
                                            • > а поведение должно быть разное
                                              Ну схуя? Поведение будет одинаковое - и после foo->bar() и после foo->eggs() он позовёт декремент (rule 4).

                                              return this увеличит счётчик на 1 (см. rule 3), поэтому bar() вернёт объект с рефкаунтом 2 и он выживет.

                                              return внутри eggs() тоже увеличит счётчик (rule 3), но выход временной ссылки из скопа (которую вернуло new) уменьшит его (rule 4), поэтому из eggs() вернётся объект с рефкаунтом 1 и он сдохнет.

                                              Включи мозги уже, блин.
                                              Ответить
                                              • Да понял я уже, понял.

                                                Что ты так разнервничался? Первый раз ламера в Интернете увидел?
                                                Ответить
                                                • > Что ты так разнервничался?
                                                  Сорри, у меня синдром утёнка случился - я не знал, что в ObjectiveC используется более сложная схема рефкаунтинга, чем обычно (вот это самое разделение на Create методы, которые инкрементят и Get методы, которые не инкрементят).
                                                  Ответить

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