Мне с трудом удалось найти элегантное решение для сегодняшней головоломки, но это сделали несколько других людей. Прежде всего, вот часть 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()
для домов
и сэкономить немного времени).