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

    +1

    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
    14. 14
    class Program
    {
            static void Main()
            {
                UInt64 num;
                Console.WriteLine(num = F(Convert.ToUInt64(Console.ReadLine())));
                Main();
            }
    
            static UInt64 F(UInt64 number)
            {
                return number <= 0 ? 1 : number * F(number - 1);
            }
    }

    Считывание числа и выдача его факториала while(true).

    Запостил: alexey_70707, 15 Сентября 2015

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

    • и хде тут говнокод ткни пальцем в упор невижу
      Ответить
      • Наверно это
        UInt64 num;
        Console.WriteLine(num = F(Convert.ToUInt64(Console.ReadLine()))) ;

        Эклипс уже бы матюкнулся наверно
        Ответить
        • в C# присваивание как и в С возвращает значение
          F() вызов рекурсивной функции всё остальное тоже нормально
          Ответить
      • Всё нашел. Пока не посмотрел под отладчиком не заметил рекурсивный вызов Main() в 7 строке
        Ответить
        • Факториал же.
          Ответить
          • Факториал это рекурсивный вызов F()
            а рекурсивный вызов Main() это говнокод с переполнением стэка от бесконечной рекурсии
            Ответить
            • Точон
              Ответить
            • Console.ReadLine вызывает прерывание ОС, которое позволит выйти из рекурсии для Main() в нужный момент
              Ответить
              • Да просто исключение кинет, когда числа кончатся.

                Кстати, а шарп хвостовую рекурсию оптимизирует?
                Ответить
                • Нахуя? Нахуя это в императивных языках?
                  Ответить
                  • > в императивных языках
                    Иногда алгоритмы ощутимо проще выразить (и корректно написать с первого раза) хвостовой рекурсией, чем циклом.
                    static long sum(Iterator<Long> i, long acc) {
                        return i.hasNext() ? sum(i, i.next() + acc) : acc;
                    }
                    vs
                    static long sum(Iterator<Long> i, long acc) {
                        while (i.hasNext()) {
                            acc += i.next();
                        }
                        return acc;
                    }
                    Ответить
                    • Мне второе вполне понятно.
                      Ответить
                      • Оно и должно быть понятно, это тривиальный алгоритм, но даже в нём рекурсивная версия компактней и лучше отражает намерения "добавляем число к сумме и складываем оставшиеся".
                        На более сложных примерах профит может быть более заметным. Быстрое возведение в степень:
                        // Require: n >= 1
                        public static long pow1(long a, long b, int n) {
                          while (n > 1) {
                            if (n % 2 != 0) {
                              a *= b;
                              n -= 1;
                            } else {
                              b *= b;
                              n /= 2;
                            }
                          }
                          return a * b;
                        }
                        public static long pow(long a, long b, long n) {
                          if (n <= 1)     return a * b;
                          if (n % 2 != 0) return pow(a * b, b,     n - 1);
                          else            return pow(a,     b * b, n / 2);
                        }
                        В какой версии проще проверить, что a * b ^ n == const?
                        Ответить
                        • Если и есть, разница не такая большая чтобы изъябываться с оптимизацией хвостовой рекурсии.
                          Ответить
                        • конечно в первой потому-что при
                          pow(1, 1, Int64.MaxValue)
                          тебе стек распидорасит, а с циклом он хотя-бы не упадёт и вообще
                          // Require: n >= 1
                          public static long pow1(long a, long b, int n) {
                            while (n > 1) {
                              if (n & 1 != 0) {
                                a *= b; n--;
                              } else {
                                b *= b; n >>= 1;
                              }
                            }
                            return a * b;
                          }

                          деление тут не нужно
                          Ответить
                          • Гость, worst case за 2*количество битов в степени операций.
                            Ответить
                          • нет тут деления и не было
                            Ответить
                          • > деление тут не нужно

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

                            > тебе стек распидорасит, а с циклом он хотя-бы не упадёт

                            Опять умник появился? тред не читай @ комментируй очевидное
                            Ты обсуждение про tail call recursion optimization читал?

                            Ну и глубина рекурсии даже в твоём случае не будет больше 64. Алгоритм логарифмический.
                            Ответить
                            • Руснявенький, во-первых, воняешь здесь ты, во-вторых, я умножение сдвигами упоминал в немного другом контексте
                              Ответить
                        • Проще так:
                          public static long pow(long a, long n) {
                            if (n<=0) return 1;
                            if (n == 1)     return a;
                            if (n % 2 != 0) return a*pow(a,n-1);
                            else            return sqr(pow(a,n/2));
                          }

                          Переменная-накопитель нужна лишь для нерекурсии.
                          Ответить
                          • > Переменная-накопитель нужна лишь для нерекурсии

                            Я умышленно написал так, чтобы вменяемый компилятор мог применить tail call recursion optimization и превратить рекурсивный код в цикл.
                            Ответить
                • > шарп хвостовую рекурсию оптимизирует?
                  На SO говорят, что нет
                  http://stackoverflow.com/questions/491376/why-doesnt-net-c-optimize-for-tail-call-recursion
                  Ответить
                  • МС так на фа-диез пытается народ подсадить
                    ну а хули? программисту же, кроме как иль мерджить, делать нехуй
                    Ответить
    • Это функциональный стиль! Зачем нам циклы, если есть рекурсия!
      Ответить
      • Функциональщики либо пишут c хвостовой рекурсией, либо
        Enumerable.Range(1, number).Aggregate(1, (x, y) => x * y)
        Ответить
        • Это говнокод, тут каждый пишет как ему вздумается.
          Ответить
          • Более того - люди пишут как им вздумается не только на ГК.
            Ответить
        • А второй вариант плох?
          Ответить
          • Второй это который?
            Ответить
            • Enumerable.Range(1, number).Aggregate(1, (x, y) => x * y)
              Ответить
              • а кто-то утверждал, что он плох?
                Ответить
                • из контекста и строения фразы показалось, что вы противопоставляете метод с Enumerable хвостовой рекурсией.
                  Типа, функциональщики пушит (нормально) с хвостовой рекурсией или (через жопу) с Enumerable
                  Ответить
                  • Это только ваши домыслы, я ничего такого не подразумевал. Оба варианта вполне приемлимы. Вариант с хвостовой рекурсией в теории лучше поддаётся оптимизациям компилятора, но это далеко не всегда приносит какой-то профит.
                    Ответить

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