Адвент кода 2015 — день 3

Мне с трудом удалось найти элегантное решение для сегодняшней головоломки, но это сделали несколько других людей. Прежде всего, вот часть 1:

Нас просят следить за Сантой, когда он движется на север, восток, юг и запад, и записывать, сколько мест он посетил. Задумываясь о второй части, я представлял, что нам придется найти, какое место он посетил больше всего, но Эрик Уостл придумал кое-что другое.

Мой подход заключался в создании dict, ключом к которому служил кортеж координат Санты x и y. На самом деле я записывал, сколько раз он посетил каждый случай, потому что, кто знает, что будет во второй части?! Мой код для записи его перемещений выглядит немного неуклюже по сравнению с некоторыми более питоническими решениями, которые придумали другие люди, но он работает. Сначала он был встроен в основной код, но в конце концов я поместил его в отдельную функцию из-за второй части:

def increment_house(x, y, houses):
    if (x, y) in houses:
        houses[(x, y)] += 1
    else:
        houses[(x, y)] = 1

def calc_move(x, y, move):
    if move == '^':
        y += 1
    elif move == 'v':
        y -= 1
    elif move == '>':
        x += 1
    else:
        x -= 1
    return x, y

santaX = 0
santaY = 0
houses = {}
increment_house(santaX, santaY, houses)
for char in data:
    santaX, santaY = calc_move(santaX, santaY, char)
    increment_house(santaX, santaY, houses)
print(len(houses))
Вход в полноэкранный режим Выход из полноэкранного режима

Много кода, но, надеюсь, достаточно читабельного, и он справляется со своей задачей, так что переходим ко второй части!

Вместо того, чтобы вести счет визитам Санты, мы знакомимся с новым персонажем! Теперь мы также следим за роботом Санты, что объясняет, насколько общим является calc_move() в моем коде. Используя те же две вышеуказанные функции, мое решение части 2 выглядит следующим образом:

santaX = 0
santaY = 0
robX = 0
robY = 0
houses = {}
increment_house(santaX, santaY, houses)
increment_house(robX, robY, houses)
moveSanta = True
for char in data:
    if moveSanta:
        santaX, santaY = calc_move(santaX, santaY, char)
        increment_house(santaX, santaY, houses)
        moveSanta = False
    else:
        robX, robY = calc_move(robX, robY, char)
        increment_house(robX, robY, houses)
        moveSanta = True
print(len(houses))
Вход в полноэкранный режим Выход из полноэкранного режима

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

Становимся более питоничными!

Просматривая чужие решения в мегатреде, я обнаружил, что для перемещения символов можно использовать дикту, используя всего одну строку кода:

def calc_move(c, p):
    return { '>': (p[0] + 1, p[1]), '<': (p[0] - 1, p[1]), '^': (p[0], p[1] + 1), 'v': (p[0], p[1] - 1) }[c];
Вход в полноэкранный режим Выйти из полноэкранного режима

В этом коде текущие координаты передаются в виде кортежа p, а направление перемещения — символ c. Этот символ является ключом к четырем элементам в словаре, которые одновременно просматриваются, и координаты обновляются соответствующим образом. Я нашел это действительно элегантным; элегантность, похоже, является одним из ключей к тому, что кодер находит истинно пифоническим.

Воистину гольф!

Я также нашел эту однострочную программу на Python, которая, как я предполагаю, работает (я не проверял ее), но, несмотря на то, что я смотрел на нее и выпил множество чашек кофе, я все еще не могу понять, что она делает. Тем не менее, впечатляет!

len(set.union(*(set((tuple(map(sum, zip(*({">": (1, 0), "<": (-1, 0), "^": (0, 1), "v": (0, -1)}[v] for v in y[:z])))) for z in range(len(y)))) for y in (data[x::2] for x in [0, 1]))))
Вход в полноэкранный режим Выход из полноэкранного режима

Я не использовал объект set ни для чего другого, кроме как для того, чтобы коллекции содержали только по одному элементу*, поэтому я могу только догадываться, что здесь делает set.union(). Аналогично, хотя я видел zip(), используемую во многих кодах Python, я еще не копался в документации, чтобы выяснить, что она делает на самом деле.

(* Очевидно, что, как оказалось, мне не нужно было вести счет того, сколько раз было посещено каждое место, поэтому я мог бы использовать set() для домов и сэкономить немного времени).

Оцените статью
devanswers.ru
Добавить комментарий