NKolotey's blog

By NKolotey, 13 years ago, In Russian

Во время соревнований некогда эстествовать, поэтому чаще всего голова переключается в обычный императивный режим и руки пишут стандартные циклы, стандартные привычные процедуры, их и дебажить проще. Уже после контеста решил переписать решения с использованием функциональных возможностей C#, получилось вот что:

Задача A (поливание цветов)

        static void Main(string[] args)
        {
            int k = int.Parse(Console.ReadLine());
            if (k == 0)
                Console.WriteLine(0);
            else
            {
                var q = from s in Console.ReadLine().Split()
                        let i = int.Parse(s)
                        orderby i descending
                        select i;

                int sum = 0;
                int count = q.TakeWhile(x => (sum += x) < k).Count();
                Console.WriteLine(sum < k ? -1 : count + 1);
            }
        }

Стиль записи var q = from x in ... join ... where ... orderby ... select ... похож на язык sql запросов, по сути выражение и является запросом, только источником данных служит не СУБД, а обычная IEnumerable либо IQueriable коллекция, в данном случае массив строк, получившихся в результате распиливания входной строки Console.ReadLine().Split(). Это же самое можно было записать малость менее читабельно: var q = Console.ReadLine().Split().Select(x => int.Parse(x)).OrderByDescending(x => x); хотя найдутся и те, кому такой поезд из кучи сцепленных вагончиков более по душе. Например, вебстеры, работающие с jquery, постоянно пишут клиент-сайд скрипты в очень похожем стиле. Главное преимущество такой записи — компактность. Но ещё и наличие некоторых дополнительных overload'ов методов с дополнительным аргументом — указывающем на позицию переданного в лямбда-функцию элемента коллекции, этим можно воспользоваться в третьей задаче.

Задача B (марсианские часы)

        static void Main(string[] args)
        {
            string[] hm = Console.ReadLine().Split(':');

            Func<string, int, int> s2i = (s, b) =>
            {
                var a = s.Select(c => (c <= '9') ? c - '0' : c - 'A' + 10).ToArray();
                if (a.Any(x => x >= b)) return 60;
                return a.Aggregate((acc, x) => acc < 60 ? acc * b + x : acc);
            };

            var possible = Enumerable.Range(1, 100).Where(x => s2i(hm[0], x) <= 23 && s2i(hm[1], x) <= 59).ToArray();

            if(!possible.Any())
                Console.WriteLine(0);
            else if(possible.Last() == 100)
                Console.WriteLine(-1);
            else
                foreach(var x in possible)
                    Console.Write("{0} ", x);
        }

С функциями можно обходиться, как с локальными переменными: описывать их в текущем контексте видимости, они захватывают весь текущий контекст со всеми локальными переменными. Короче, обычное "замыкание" в терминах функциональных языков.

Задача C (деление на команды)

        static void Main(string[] args)
        {
            int n = int.Parse(Console.ReadLine());
            var q = Console.ReadLine().Split().Select((x, pos) => new { i = pos + 1, a = int.Parse(x) })
                .OrderBy(x => x.a).ToArray();

            Console.WriteLine((q.Length + 1) / 2);
            foreach (var x in q.Where((x, pos) => pos % 2 == 0))
                Console.Write("{0} ", x.i);
            Console.WriteLine();

            Console.WriteLine(q.Length / 2);
            foreach (var x in q.Where((x, pos) => pos % 2 == 1))
                Console.Write("{0} ", x.i);
            Console.WriteLine();
        }

.Select((x, pos) => ...) и .Where((x, pos) => ...) имеют одним из аргументов лямбду, принимающую на вход не только сам item, но и его позицию. В linq стиле (который похож на sql запрос, то есть начинается с from x in collection ...) эту текущую позицию получить, к сожалению, нельзя, либо я не знаю как это можно сделать. Сначала генерится коллекцию пар { позиция, значение }: Select((x, pos) => new { i = pos + 1, a = int.Parse(x) }) затем сортируется OrderBy(x => x.a), ну и т.к. результатом потребуется воспользоваться дважды (один раз для вывода чётных позиций, другой для нечётных), поэтому нужно материализовать во что-нибудь ToArray().

В четвёртой задаче места linq'у у меня вообще не нашлось, а пятую я не сделал.

  • Vote: I like it
  • +50
  • Vote: I do not like it