1. C# / Говнокод #9194

    +127

    1. 1
    2. 2
    3. 3
    4. 4
    5. 5
    foreach(string s in File.ReadAllText(input_text).Replace('ё', 'е').ToLower().Split(new char[] { ',', ' ', '.', '\t', '\r', '\n', '-', '?', '!', '\\', '/', ':', ';', '<', '>', '\'', '\"', '(', ')' }, StringSplitOptions.RemoveEmptyEntries).
    AsParallel().Where<string>(s => (s.Length == 4)).GroupBy(x => x).Select(g => new { Value = g.Key, Count = g.Count() }). OrderByDescending(x => x.Count).Select(f => (f.Value + ' ' + f.Count.ToString())).ToArray())
    {
           Console.WriteLine(s);
    }

    Найдено в коде одного из участников олимпиады сосницких
    Я понимаю, LINQ - это круто, но зачем так издеваться над проверяющим?

    Запостил: kasthack, 22 Января 2012

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

    • Шикарно! А ещё его можно записать в 1 строку:
      foreach (string s in File.ReadAllText(...)) Console.WriteLine(s);
      Ответить
    • Между прочим всё понял с первого взгляда.
      Но сарказм автора оценил:
      {
             Console.WriteLine(s);
      }
      Ответить
      • и что интересного в той части, что ты процитировал?
        Ответить
        • То, что желательно было записать в несколько строчек - автор записал в одну, а то что можно было записать в одну - автор написал в несколько (фигурные скобочки не нужны).
          Ответить
          • >а то что можно было записать в одну - автор написал в несколько (фигурные скобочки не нужны).

            существуют соглашения по оформлению кода, в которых подобное является обзательным, потому говорить "не нужны" в корне не правильно.
            Ответить
            • Можно записать в одну и без смерти фигурных скобочек. Достаточно заменить цикл foreаch его LINQ братом
              Ответить
              • Имеешь в виду Parallel.ForEach? Попробуй ;). Попробуй обеспечить вывод отсортированных значений этим способом.

                Имеешь в виду ParallelQuery.ForAll()? Аналогично ;). Но тут есть способы выхода из положения.

                Самое простое, наверное, использовать Array.ForEach или List.ForEach. Но это напрочь убивает ленивость.
                Ответить
                • Ну он и так .ToArray() сделал, так что простой .ForEach(a=>Console.WriteLine(a)) однозначно заставит улететь мозги в потолок у большинства проверяющих
                  Ответить
    • Ну как бы справедливо: функциональный подход обламался на функции, которая ничего не возвращает :)
      Ответить
    • А можно прокомментировать каждую составляющую ЛИНКВА? А то я только некоторые понимаю.
      Ответить
      • Данная функция должна параллельно выделять слова длиной четыре символа в тексте без учета регистра и буквы "ё", а затем выводить к-во повторений для каждого из них
        Ответить
      • foreach (string s in File
        
            // читаем весь текст из файла
            .ReadAllText(input_text)
        
            // заменяем ё на е
            .Replace('ё', 'е')
        
            // переводим строку в нижний регистр
            .ToLower()
        
            //расщепляем строку по указанным символам
            .Split(new char[] { ',', ' ', '.', '\t', '\r', '\n', '-', '?', '!', '\\', '/', ':', ';', '<', '>', '\'', '\"', '(', ')' },
                StringSplitOptions.RemoveEmptyEntries) // удаляя пустые строки
        
            // распараллеливаем процесс по количеству ядер в системе
            .AsParallel()
        
            // выбираем такие строки, у которых длина 4
            .Where<string>(s => (s.Length == 4))
        
            // группируем: одинаковые строки - в одну группу
            .GroupBy(x => x)
        
            // выбираем, создавая анонимный тип: Value - это строка (которая длиной четыре символа),
            // Count - количество таких строк (одинаковых) в группе
            .Select(g => new { Value = g.Key, Count = g.Count() })
        
            // сортируем по убыванию по количеству символов
            .OrderByDescending(x => x.Count)
        
            // выбираем, создавая строку, состоящую из значений и количества
            .Select(f => (f.Value + ' ' + f.Count.ToString()))
        
            // переводим в массив - в принципе, это лишнее в данном случае
            //.ToArray()
            )
        {
            Console.WriteLine(s);
        }

        Выведется что-то вроде:
        мама 10
        папа 5
        вася 1
        Ответить
        • А как будет выглядеть в виде синтаксиса запроса? Кому не лень:)
          Ответить
          • будет нечитаемо
            т.к. в виде запроса можно записать только where, group by и select
            а остальное - всё равно в виде методов
            так что лучше оставить как есть, просто добавить добавить перенос строки перед каждым новым чейном (точкой)
            Ответить
          • Перевёл почти в лоб:
            foreach (var str in
                from s in File
                    .ReadAllText(input_text)
                    .Replace('ё', 'е')
                    .ToLower()
                    .Split(new char[] { ',', ' ', '.', '\t', '\r', '\n', '-', '?', '!', '\\', '/', ':', ';', '<', '>', '\'', '\"', '(', ')' },
                        StringSplitOptions.RemoveEmptyEntries)
                .AsParallel()
                where s.Length == 4
                group s by s into g
                let count = g.Count()
                orderby count descending
                select g.Key + " " + count
                )
            {
                Console.WriteLine(str);
            }

            Как уже замечено выше, получается почти нечитаемый уродец.
            Ответить
            • >получается почти нечитаемый уродец
              Люди незамутненные сиквелом.
              Ответить
              • Ага, я тут на днях пару кастомных запросов для jira набросал для MSSQL, вот уж где нечитаемый п...ц.
                Ответить
        • AsParallel? Вот так одну волшебная функция делает код параллельным?
          Или она возвращает новый хитрожопый контейнер, у которого все методы сделаны как параллельнные?
          Ответить
          • Linq вроде как ленивый, скорее всего, запуск нескольких тредов будет произведён при непосредсвенной необходимости получения значения. Не уверен, что от этого будет хоть какой-то бенефит в данном случае.
            Ответить
            • если в файле строк этак 100 тыс. - я думаю будет
              Ответить
              • Да, положительный эффект будет лишь при определённых условиях.
                Хорошо бы ещё Replace и ToLower перенести в параллельную часть.
                Ответить
              • linq ускорит считывание файлов с моего жесткого диска?
                Ответить
                • LINQ ускорит обработку данных прочитанных с твоего диска
                  Ответить
                  • Ога. А SSE ускоряет интернет :))
                    Ответить
                  • Вот вам, дорогие друзья, хороший пример тотальнейшего обыдления шарпом.
                    Дейскать "напейшу AsParallel() и оно само в 2-4 раза ускорится"

                    Я поясню мысль. При нормальный трейдинге один тред потихоньку читает содержимое файла. Второй же парсит и обновляет статистику.
                    То есть время выполнения T будет следующим
                    T=t_считывания+t'
                    Где t' - время, которое нужно чтобы закончить работу второму треду. Оно ничтожно.
                    Т.к. второй тред работает быстрее жесткого диска и большую часть времени он будет простаивать в ожидании новой порции данных с HDD.

                    LINQ будет работать иначе:
                    Сначала он прочитает файл ReadAllText(input_text) в память.
                    Далее создаст список на 100000 элементов
                    Потом сделает распаралеленый проход for i=1 to 100000 Replace('ё', 'е')
                    Потом еще один распаралеленый цикл for i=1 to 100000 ToLower()
                    итд.
                    Возможно я неправ и LINQ будет обрабатывать конвейерно, не создавая промежуточных списков, но в конце-концов все-равно имеем:

                    T=t_считывания+t_обработки_всех_100000_э лементов

                    Ну о побочном эффекте - загрузке всего файла в память я умолчу.

                    Но проблема даже не в том, что паралелится не то что нужно.
                    Ответить
                    • здесь распаралеливается обработка строк (группировка тех что длиной=4 итд)
                      и только это
                      всё остальное происходит по-очереди
                      Ответить
                      • >здесь распаралеливается обработка строк (группировка тех что длиной=4 итд). и только это
                        Отлично!

                        Тут мы имеем вот что:
                        (С вашего позволения я перенесу стадии Replace('ё', 'е') и ToLower() после стадий Split и Where. Для простоты и оптимальности)
                        - Чтение из памяти слова
                        - Выполнение на нём простейших операций - проверка Where, Replace('ё', 'е'), ToLower()
                        - Нахождение в списке статистики (память, хотя очень вероятно кеш) этого слова
                        - Опциональное создание новой статистической записи
                        - Инкремент статистики (количество таких слов)

                        ЦПУ очень быстро осуществит Where, Replace('ё', 'е'), ToLower(), и оставшееся время будет ждать пока оно запишется в память/кеш.
                        и новой порции данных из памяти. Ибо латентность памяти очень высока.

                        Зачем это распаралеливать? Чтобы два ядра простаивали в ожидании данных?
                        Мало того затраты на распаралеливание в данном случае могут запросто получится дороже, чем сама задача.
                        Ответить
                        • я не защищаю применение разпар-ия в данном случае. тем более не являясь специалистом по TPL. я защищаю разпар-ие в принципе как теоретически-оптимизирующую технику, тем более различающуюся в производительности от задачи к задаче
                          Ответить
                    • Прочитал я всё, что ты тут пишешь.
                      Воистину, если тебе так не нравится C#, .NET и прочее, то не морочь голову ни себе, ни людям.

                      Возможности параллельного программирования в дотнете воистину шикарны. Ты увидел лишь одну команду, и начал исходить желчью. Ты пробовал, например, OpenMP? Я пробовал, и немало. Там тоже влёгкую добиваешься распараллеливания. И так же как в дотнете, во многих случаях не получается никакого выигрыша. Естественно! Потому что алгоритм под параллельность нужно менять.

                      Я тут упоминал, что ToLower и Replace нужно перенести в параллельную часть. Но, естественно, ты прицепился к неэффективному алгоритму, написанному студентотой, и громогласно заявляешь, что linq сосёт.

                      Да, ты прав: в хорошей многопоточке вычисления нужно проводить параллельно с поступлением данных. Но опять же, - здесь мы видим неэффективный элементарный код, написанный на коленке. В дотнете есть ленивое чтение из файла. В данном случае можно применять его, только и всего.
                      Ответить
                      • >Я тут упоминал, что ToLower и Replace нужно перенести в параллельную часть.
                        Не спорю, более того все мои рассуждения основывались именно на этом предположении:
                        >перенесу стадии Replace('ё', 'е') и ToLower() после стадий Split и Where. Для простоты и оптимальности)

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

                        >если тебе так не нравится C#, .NET и прочее
                        Вы на говнокоде, сер.

                        >что linq сосёт.
                        Факт замедления кода в 20 раз просто не позволяет мне сказать, что оно рулит.
                        Ответить
                • Дело в том, что в данном примере AsParallel() может замедлить "обработку уже прочитанных данных".
                  Как бы парадоксально это не звучало.
                  Ответить
                  • а может ускороить
                    а может не изменит время
                    up to CLR
                    Ответить
                    • >up to CLR
                      Дабы мои слова не казались беспочвенными наездами на чудесную технлогоию linq - предлагаю провести кому-нибудь, имеющему шарп и не менее 2-х физ. ядер простой эксперимент.

                      Создать список с 1000000 рандомных чисел.
                      И замерить скорость вычислений на нем (суммирования, например)
                      1. цикл for
                      2. linq
                      3. parallel linq

                      Запостить результаты в тред.
                      Ответить
                      • суммирования - нет (это последовательный алгоритм)
                        поиска (не)четных чисел в отсортированном мастак - да
                        сделаю завтра если никто раньше меня
                        Ответить
                        • мля. массивах. пишу с телефона
                          Ответить
                          • распидорасило штоле? сложение - вообще не алгоритм
                            коммутативность, ассоциативность, первый класс церковно-приходской
                            Ответить
                            • "суммирование - последовательный алгоритм","нельзя распаралелить"
                              Просто жуть, что шарп с мозгами делает.
                              Всё еще хуже, чем я предполагал.
                              Ответить
                            • алгоритм сложения чисел в массиве - алгоритм
                              Ответить
                        • Если не можешь в суммирование по частям - тогда сделай group by по 4-х значным числам. Это будет более-менее эквивалентно исходному заданию.
                          А randomы генерируй в [0,10000);

                          Хотя я зашел в тред и хотел предложить более честный и приближенный к практике вариант - умножение чисел по некоему модулю n.
                          Подобные дискретные умножения/возведения в степень используются не одним криптоалгоритмом.
                          Ответить
                          • var list = new List<Int32>();
                                        Random rand = new Random();
                                        for (int i = 0; i < 100000; i++)
                                        {
                                            list.Add(rand.Next(0, 999));
                                        }
                            
                                        var forStopwatch = new Stopwatch();
                                        var LINQStopWatch = new Stopwatch();
                                        var PLINQStopWatch = new Stopwatch();
                            
                                        forStopwatch.Start();
                                        int sum = 0;
                                        for (int i = 0; i < 100000; i++)
                                        {
                                            sum += list[i];
                                        }
                                        forStopwatch.Stop();
                            
                                        LINQStopWatch.Start();
                                        sum = list.Sum();
                                        LINQStopWatch.Stop();
                            
                                        PLINQStopWatch.Start();
                                        sum = list.AsParallel().Sum();
                                        PLINQStopWatch.Stop();

                            Простое суммирование. LINQ в среднем вдвое медленнее for(4000 тиков против 2000), PLINQ вообще хренью занимается в 10 раз больше обычного(~80000 тиков).
                            А вообще - не стоит ковырять инты, нужно придумывать более сложные задачи с более сложными типами. Никто на дотНете высокопроизводительный вычислительный софт не пишет.
                            Ответить
                            • > Никто на дотНете высокопроизводительный вычислительный софт не пишет

                              http://www.microsoft.com/en-us/sqlazurelabs/labs/numerics.aspx
                              Ответить
                            • >PLINQ вообще хренью занимается в 10 раз больше обычного(~80000 тиков).
                              Просто шикарно! Я думал оно процентов так на 20-30 замедлится.
                              Но это вообще замечательно - чудесная параллельная технология замедляющая вычисления в 10 раз.
                              Ответить
                              • учитывая то, сколько времени занимают обычные вычисления, накладные расходы попросту не окупаются. Так что в принципе логичный результат.
                                Ответить
                                • Он-то логичный. Мне это изначально было ясно.
                                  И тут я подробно описал почему: http://govnokod.ru/9194#comment127899
                                  Ну ладно, пусть чудо-LINQ замедляет код в 2 раза. Но тут торможение еще в 20 (двадцать)! раз от уже тормознутого LINQ.

                                  PS. Благодарю за тесты.
                                  Ответить
                              • Ну просто прикручена она неверно: параллельным-то нужно делать Map, а не Reduce.
                                Ответить
                              • луп надо выполнять не единожды, а тысяч 10 раз.
                                + аппаратную начинку тестовой станции в студию
                                вы совсем измерять производительность не умеете, да?
                                Ответить
                      • тут всё проще, если в наличии N > 1 процов, то простой цикл не оптимален, и здесь на помощь приходит linq, каторый сам програмы пишит, прикинь!!!!
                        вот и получается быстрее.
                        Ответить
                        • > linq, каторый сам програмы пишит, прикинь!!!!
                          Абсолютно в говно правильно!
                          Зогчем мне голова? Пусть за меня думает Майкрософт.
                          Ответить
                    • >а может ускороить
                      >а может не изменит время
                      Как я и говорил чудес не бывает и этот ваш PLINQ соснул. Причем с причмоком.
                      Ответить
                      • пруф или не было
                        нашел выше
                        Ответить
                      • какой облом! PLINQ быстрее LINQ в вашем доморощенном тесте
                        Ответить
                        • у вас просто числа были недостаточно рандомные
                          Ответить
                        • Я наверное постараюсь попросить какого-нибудь незаинтересованного шарпотестера.
                          Хотя ваши результаты тоже довольно интересны:
                          >for: 306
                          >linq: 2645
                          Замедление в 8 раз, против обычного цикла.
                          Ответить
              • Суть задания как раз состояла в обработке "Войны и мира"
                Ответить
                • ОХОХО жесть.
                  Мне знакомый гоп рассказывал, как они по приколу написали программу и обработали "Мастера и Маргариту", заменив
                  "," на ", бля, "
                  "?" на ", нахуй?"
                  В итоге получилось что-то типа "я, бля, пойду, нахуй?".
                  Ответить
          • А что так удивляет? Вон в OpenMP http://ru.wikipedia.org/wiki/OpenMP#C пара прагм тоже легко делают распараллеливание цикла.

            Ты прав, - вместо IEnumerable становится ParallelQuery, с тем же набором методов. Но параллельных.
            Ответить
          • это же си шарп! эта функция распаралеливает код на несколько процессоров.
            там еще есть аргумент -- на сколько именно.
            например: asParralel(32) делает код оптимальным для 32х ядерных машин.
            Ответить
        • внимание вопрос! зачем нужен ToLower()?
          Ответить
          • вероятно, чтобы группировка работала для "мама" и "МаМа"
            Ответить
            • хм.. и правда. странно что нет перегрузки принимающей StringComparison или IEqualityComparer
              Ответить
        • .Write() // Сразу выводим на консоль
          .Print(DEFAULT_PRINTER) // Печатаем
          .MakeCoffee("Black") // Делаем кофе

          И вся программа из одной строки.
          Не технология - мечта.
          Ответить
    • Вот так сосницкие
      Ответить

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