99 бутылок: Psql
Давно чесались руки написать очередную «песню о пиве»
75. Psql — не совсем язык программирования, это название клиентской программы, распространяемая вместе с СУБД «Постгрес». У неё есть довольно скромный встроенный язык, в который относительно недавно добавили условный блок.
Тем не менее на этом языке можно требуемое, если привлечь для математики и сравнения эскуэль — сам psql такого не умеет. В качестве цикла пришлось применить включение файла в самого себя, повезло, что цикл относительно короткий, а то бы я упёрся в лимит на количество открытых файлов в системе.
Добавлено позднее: мне всё же удалось реализовать «песню о пиве» без использования эскуэля.
\if 0
99.psql
Psql beer song
Written by Evgeny Stepanischev https://bolknote.ru
\endif
\if :{?beer} \else \set beer 99 \endif
\echo :beer bottles of beer on the wall, :beer bottles of beer.
select :beer-1 beer, :beer > 2 notend \g /dev/null
\gset
\if :notend \set s s \else \set s \endif
\echo Take one down and pass it around, :beer bottle:s of beer.'\n'
\if :notend
\include_relative 99.psql
\else
\echo 1 bottle of beer on the wall, 1 bottle of beer.
\echo Take one down and pass it around, no bottle:s of beer.'\n'
\echo No more bottles of beer on the wall, no more bottles of beer.
\echo Go to the store and buy some more, 99 bottles of beer on the wall.
\endifВроде код понятен почти без объяснений, но несколько моментов без знакомства с утилитой вряд ли возможно расшифровать самостоятельно, поэтому я их вкратце хочу объяснить.
:{?beer} — синтаксис проверки существования переменной, это нужно чтобы понять запускаем ли мы программу в первый раз или включили её в себя же, чтобы имитировать цикл.
select :beer-1 beer, :beer > 2 notend —
\g /dev/null — запуск запроса в выводом результата в /dev/null (то есть в никуда), поскольку его результат на экране нам не нужен.
\gset — записываем результат запроса в переменные, имена которых равны именам столбцов запроса.
У меня есть одна идея, как сделать вычитание, не привлекая эскуэль, надо будет попробовать. А вот с остановом хуже — там никаких идей.
Остатки иной цивилизации
Я уже почти заядлый рыбак — фото улова пока ещё не выкладываю, но уже дважды ездил на рыбалку, правда в прошлый раз на моторном катере, а в этот — на двухместной резиновой лодке без мотора. То ещё приключение, зато теперь я знаю, что могу грести часа
К счастью, к вечеру ветер совсем стих и вернулись мы вдвое быстрее, правда не без трудностей — стемнело и пришлось плыть по спутниковой карте, которую мы рассматривали через навигатор Яндекса.
На берегу реки, где мы в этот раз угнездились, валяется немало старых артефактов — в основном, это различное кованое железо, обломки кирпичей, а в одном месте лежит огромный мельничный жернов. Видимо это всё навело меня на следующую мысль, пока я созерцал неподвижный поплавок.
Большинство моих хобби сейчас связано с разъездами по различным медвежьим углам, в результате я постоянно натыкаюсь
И вот мне подумалось, что это сильно напоминает все те книги и фильмы, когда на развалинах одной цивилизации, неизвестно куда пропавшей, прорастает другая, живущая среди обломков первой
В отличие от героев подобных книг и фильмов, мы, конечно, отлично знаем на обломках (самовластья, конечно же) чего мы живём и куда делись те, после кого они остались, но почти никто эти обломки не узнаёт и не замечает, даже когда о них буквально спотыкается.
В фильмах эта ситуация для зрителя обычно выглядит немного странно, но если подумать, мы живём ровно в такой же реальности.
99 бутылок: Psql (переработанная версия)
Я уже публиковал сегодня одну попытку написать «песню про пиво» на языке утилиты psql, но, к сожалению, результат получился не совсем честный — для математики и сравнения мне пришлось использовать вставку на эскуэеле.
Чисто интуитивно чувствовалось, что
Реализовать задуманное получилось через таблицу замен (об этом ниже) и кодогенерацию — psql позволяет писать в файлы произвольное содержимое, а потом эти же файлы подключать. К сожалению, после этого остаётся немного мусора во временной папке, но тут уж ничего не попишешь.
Таблицу замен я реализовал через девять переменных с числовыми именами, значением которых является число на единицу меньше имени. Например: \set 9 8 — тут переменная с именем 9 получает значение 8.
Чтобы реализовать вычитание единицы, пришлось разделить счётчик цикла на две переменные — beer_h (старший разряд) и beer_l (младший разряд), вычитание производится для каждой цифры отдельно. На диск записывается код, в который помещается имя переменной и её новое значение, используя текущее значение, как имя переменной, из которой надо взять следующее. Понимаю, что запутанно, надеюсь на примере понять будет проще.
Вот этот кусок: \qecho '\\set beer_h :':beer_h. Если текущее значение переменной beer_h равно 9, то на диск будет помещен скрипт с содержимым \set beer_h :9, после его подключения код выполнится, а так как значение переменной 9 — 8, то beer_h получит значение 8.
Одновременно я таким же способом создаю логические переменные beer_разряд_value_значение со значением 1, которые я использую потом как флаги, чтобы остановить цикл или отключить использование множественного числа, когда число «бутылок» доходит до единицы.
С кодогенерацией, кстати, работать веселее — сгенерированный код потом можно использовать как некий аналог подпрограмм, экономя размер кода.
В общем, в результате получилось реализовать «песню про пиво» на чистом языке утилиты psql, не привлекая другие языки программирования.
\if 0
99.psql
Psql beer song v.2
Written by Evgeny Stepanischev https://bolknote.ru
\endif
\if :{?beer_l} \else
\set 9 8 \set 8 7 \set 7 6 \set 6 5 \set 5 4 \set 4 3 \set 3 2 \set 2 1 \set 1 0
\set print /tmp/beer_print.sql
\set minus /tmp/beer_minus.psql
\out :print
\qecho '\\if :{?beer_h_value_0} \\echo -n :beer_l'
\qecho '\\else \\echo -n :beer_h:beer_l \\endif'
\set beer_h 9
\set beer_l 9
\set s s
\endif
\include :print
\echo -n ' bottle':s' of beer on the wall, '
\include :print
\echo ' bottle':s' of beer.'
\if :{?beer_l_value_0}
\unset beer_l_value_0
\set beer_l 9
\out :minus
\qecho '\\set beer_h :':beer_h '\\set beer_h_value_:':beer_h 1
\include :minus
\else
\out :minus
\qecho '\\set beer_l :':beer_l '\\set beer_l_value_:':beer_l 1
\include :minus
\endif
\if :{?beer_h_value_0}
\if :{?beer_l_value_1}
\set s
\endif
\if :{?beer_l_value_0}
\echo Take one down and pass it around, no bottles of beer.'\n'
\echo No more bottles of beer on the wall, no more bottles of beer.
\echo Go to the store and buy some more, 99 bottles of beer on the wall.
\q
\endif
\else
\unset beer_l_value_1
\endif
\echo -n 'Take one down and pass it around, '
\include :print
\echo ' bottle':s' of beer.\n'
\include_relative 99.psql