В этой челлендж-серии статей попробуем использовать PostgreSQL как среду для решения задач Advent of Code 2024.
Возможно, SQL не самый подходящий для этого язык, зато мы рассмотрим его различные возможности, о которых вы могли и не подозревать.
В этой части будет очень простой код, с чуть-чуть сложным регулярным выражением.
Оригинальная постановка задачи и ее перевод:
Advent of Code 2024, Day 3: Mull It Over
--- День 3: Обдумайте это ---
«У нас проблемы с компьютерами, так что я понятия не имею, есть ли у нас на складе главные историки ! Но вы можете проверить склад», — говорит слегка взволнованный владелец магазина по прокату санок North Pole Toboggan . Историки отправляются на поиски.
Хозяин магазина поворачивается к вам. «Есть ли шанс, что вы понимаете, почему у наших компьютеров снова возникли неполадки?»
Компьютер, похоже, пытается запустить программу, но его память (ваши входные данные для головоломки) повреждена . Все инструкции перепутаны!
Похоже, что цель программы — просто умножить несколько чисел. Она делает это с помощью инструкций типа mul(X,Y)
, где X
и Y
— это 1-3-значные числа. Например, mul(44,46)
умножает 44
на , 46
чтобы получить результат 2024
. Аналогично, mul(123,4)
умножает 123
на 4
.
Однако, поскольку память программы была повреждена, есть также много недопустимых символов, которые следует игнорировать, даже если они выглядят как часть инструкции mul
. Последовательности вроде mul(4*
, mul(6,9!
, ?(12,34)
, или mul ( 2 , 4 )
ничего не делают.
Например, рассмотрим следующий фрагмент поврежденной памяти:
xmul(2,4)%&mul[3,7]!@^do_not_mul(5,5)+mul(32,64]then(mul(11,8)mul(8,5))
Только четыре выделенных раздела являются реальными mul
инструкциями. Сложение результатов каждой инструкции дает 161
( 2*4 + 5*5 + 11*8 + 8*5
).
Просканируйте поврежденную память на наличие неповрежденных mul
инструкций. Что получится, если сложить все результаты умножений?
--- Часть вторая ---
Просматривая поврежденную память, вы замечаете, что некоторые условные операторы также остались нетронутыми. Если вы обработаете некоторые неповрежденные условные операторы в программе, вы, возможно, сможете получить еще более точный результат.
Вам придется выполнить две новые инструкции:
Инструкция
do()
позволяет выполнять будущие инструкцииmul
Инструкция
don't()
отключает будущие инструкцииmul
В начале программы mul
инструкции включены.
Например:
xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5))
Эта поврежденная память похожа на пример из предыдущего, но на этот раз инструкции mul(5,5)
и mul(11,8)
отключены , потому что перед ними есть инструкция don't()
. Другие инструкции функционируют нормально, включая ту, что в конце, которая снова включается инструкцией do().
На этот раз сумма результатов равна 48
( 2*4 + 8*5
).
Обработайте новые инструкции: что получится, если сложить все результаты только включенных умножений?
Часть 1
Поскольку на входе у нас "битая" строка, ограничим ее не апострофами, как обычно, а формой
$token$..$token$
.Воспользуемся уже знакомой нам функцией
regexp_matches
, чтобы найти все подходящие пары чисел.Просуммируем произведения - и все!
SELECT
sum(exp[1]::numeric * exp[2]::numeric) -- суммируем значения
FROM
regexp_matches(
$source$xmul(2,4)%&mul[3,7]!@^do_not_mul(5,5)+mul(32,64]then(mul(11,8)mul(8,5))$source$
, 'mul\((\d{1,3}),(\d{1,3})\)' -- скобки экранируем, числа 1..3-значные
, 'g' -- по всей строке
) exp;
Часть 2
Фактически, надо убрать из исходной строки все части от
don't()
доdo()
.regexp_replace
отлично подойдет для этого.Главное, не забыть два момента: заменять надо на непустую строку, чтобы не "склеились" части до/после, и не забыть убрать "хвост" после последнего
don't()
.
SELECT
sum(exp[1]::numeric * exp[2]::numeric)
FROM
regexp_matches(
regexp_replace(
$source$xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5))$source$
, 'don''t\(\).*?(?:do\(\)|$)' -- "выстригаем" все блоки don't-do или don't-конец
, '_' -- непустая строка
, 'g' -- по всей строке
)
, 'mul\((\d{1,3}),(\d{1,3})\)'
, 'g'
) exp;