• Задача с датами

    Прочитал, что в одной фирме дают задание: не используя библиотеки и Гугл, написать функцию, которая для заданной даты вернет название дня. Скажем, для 2021-03-25 — вторник, для 2015-09-11 — среду (беру с потолка). Я бы эту задачу не решил, и вот почему.

    Во-первых, я не помню все критерии високосного года. Каждый четвертый — это понятно, но есть еще два критерия, которые встречаются реже. Нужно смотреть их в википедии.

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

    В-третьих, нужна точка отсчета. Даже если я переведу дату в число дней и возьму остаток от деления на семь, нужно знать, какой день был первым. Считается, что Иисус родился в понедельник? В любом случае это нужно сверять с википедией.

    Еще был переход с юлианского календаря на григорианский, отсюда “старый стиль”, “старый новый год” и прочее.

    Наконец, текущая система дат и времени — это костыль на костыле, которые достались нам от древних греков. Самое худшее, что можно вообразить — это работать с датами без библиотеки.

    “Что ж, свою кандидатуру я снимаю сам.” ©

    А вы бы решили?

  • Еще про Silent Hill 2

    Еще одна заметка про Silent Hill. Понимаю, что всем пофиг, но это для себя. В этот раз — еще одна партия наблюдений, незамеченных в детстве.

    Как вы знаете, герой ищет пропавшую жену Мэри. Город знакомит его с Марией — женщиной, которая выглядит как жена, но противоположна ей по характеру: общительна, сексуально раскрепощена. Часть локаций Джеймс проходит с Марией, у нее есть ключи от закрытых дверей.

    Во второй половине игры происходят странные вещи: Марию начинают убивать, причем каждый раз с большей жестокостью. В первый раз ее пронзает ножом Пирамида, во второй раз Джеймс находит ее истерзанной в тюрьме, в третий раз ее казнят кольями. Каждый случай Джеймс переживает все тяжелее, и это расшатывает его рассудок. Примечательно, что после смерти Мария ничего не помнит и ведет разговор о посторонних вещах.

    Не монстры, не ужасы города, а именно Мария становится главным мучением Джеймса. В каждой ее смерти он винит себя. Это напоминает наказание в аду, когда преступник переживает преступление снова и снова. Вот в чем заключается наказание Джеймса: город дает ему идеальную женщину и постоянно убивает ее, сводя с ума.

    Только в последней сцене, увидев Марию живой в четвертый раз, Джеймс отвергает ее. Этим он разрывает порочный круг, и начинается финал.

    Другой сложный символ, который сопровождает игру — это кровать, и появляется он в двух значениях. Первый — медленный уход из жизни, когда человек проводит дни в страданиях. Кровать становится его тюрьмой, превращаясь в могилу. Пустая кровать появляется, когда речь идет об умершей жене. Пустые кровати часто встречаются в игре как немой упрек: раньше здесь был человек, а теперь его нет.

    Второе значение кровати — страсть, секс. Кровать встречается в сценах с Марией, альтер-эго умершей жены. Иногда Мария ложится якобы отдохнуть, взглядом и жестом приглашая Джеймса занять место рядом. После первого убийства Джеймс находит Марию в тюрьме в комнате длительных свиданий — проще говоря, месте, отведенном для секса. Мария сидит на кровати за решеткой, и нужно найти к ней другой путь. Когда Джеймс отправляется на поиски хода, она откидывается назад, намекая, какой будет встреча.

    Когда Джеймс приходит к ней подземным ходом, Мария оказывается убитой. Шокирует резкая смена контекста: еще недавно она лежала на кровати соблазняя, а теперь лежит, будучи жертвой. Такой быстрый переход от любви к смерти подействовал на меня сильно. В детстве я не оценил мощь этой перемены, видимо, я просто не мог. С возрастом появились какие-то струнки, которые теперь задеты.

    Интересный факт насчет трех лет. Джеймс утверждает, что жена умерла три года назад. Он продолжает это делать даже когда вылазят зловещие нестыковки. Ближе к концу мы узнаем, откуда взялись эти три года. В лодочной станции есть магнитофон с записями диспетчеров. Если послушать их, послышится диалог Джеймса с доктором в госпитале. Джеймс пытается выжать из доктора, сколько осталось жене. Доктор дает крайне широкую оценку: возможно, три года, возможно, несколько месяцев. И хотя все произошло за неделю, ум Джеймса ухватился за эти три года, стараясь задвинуть трагедию как можно дальше в прошлое.

    Сексуализация монстров, которые выглядят как женские фигуры, связана с подавленным желанием Джеймса. В игре не обсуждается его сексуальная жизнь, но судя по долгому угасанию жены, в ней значительный пробел. Навещая жену в госпитале, Джеймс смотрел на ноги и грудь медсестер — в основном из этих частей состоят монстры. В середине игры есть сцена, где обыграно изнасилование одного из существ. Очевидно, это фантазии Джеймса.

    Каждый персонаж живет в своем мире, и только изредка они пересекаются. Каждому предназначено свое мучение. Об этом не сказано явно, нужно догадаться из диалогов. Когда Джеймс видит Анжелу в последний раз, он замечает, что все в огне (намек на то, что Анжела сожгла дом вместе с насильником). На это Анжела отвечает, что “для нее всегда так”. Неудачника Эдди преследуют выдуманные им же люди, которые насмехаются над ним. Джеймс не видит этих людей, потому что они в мире Эдди. Пересекаясь с ним, он видит только трупы убитых насмешников. В мире Лоры нет ни монстров, ни опасностей, только кошки. Догадаться об этом можно по диалогу, когда Джеймс находит Лору в подвале госпиталя и спрашивает, как ей удалось не получить ни царапины. Лора искренне не понимает, что ей может угрожать.


    Много всего я надумал, ненадолго вернувшись к этой игре. Ощущение, что перечитываешь Достоевского или Булгакова — нет-нет да найдешь то, что раньше не замечал. Может быть, через пятнадцать лет еще что-то найду — и разумеется, дам вам знать.

  • Саундтрек из Silent Hill 2

    Один знакомый, зная, что я неравнодушен к серии Silent Hill, прислал ссылку на саундтрек ремастера. Оказалось, это не полноценный саундтрек, а нарезка по 20 секунд из каждого трека. Но поскольку там 90 треков длительностью три часа, сумма огрызков набегает на час. Послушайте и вы по ссылке.

    Вдохновившись музыкой, пересмотрел ключевые сцены из оригинальной второй части. Лишний раз убедился, что Silent Hill 2 — уникальная в своем роде игра. Другой такой нет, да и не нужно — пусть она останется неповторимой.

    Дело в том, что в основе игры лежит глубокая личная драма заурядного человека. Главный герой Джеймс не спасает мир от монстров и не ищет волшебные артефакты. Его проблема — медленный уход из жизни близкого человека. Боль, борьба желания и долга, терзания, что сказать и сделать в ситуации, когда и то, и другое бесполезно.

    Пересматривая кат-сцены, не переставал удивляться глубокому символизму игры. Каждый кадр, каждая сцена выстроена так, чтобы сообщать что-то между строк. Это и окружающие предметы, и антураж, и одежда героев.

    Красной нитью сквозь игру идет тема суда — подавленное желание наказать себя за преступление. Это преступление тщательно забыто, но прорывается наружу флешбеками и уликами. Темы суда и казни особенно сильны в конце, когда герой поднимается на крышу по железному коридору. Формально железные ограждения нужны, чтобы человек не упал с лестницы, но на самом деле это отсылка к тюремному коридору, по которому заключенный идет к месту казни. Когда Джеймс ступает на крышу, коридор падает — обратно он не вернется.

    Другой назойливый символ игры — нож. В меню игры его форму имеет курсор. Нож передает Джеймсу Анжела. Оружие Пирамиды есть увеличенный в размерах нож — тот самый, что носит Джеймс. Его гротескный размер, вероятно, имеет отсылку к картинам Сальвадора Дали, где гигантизм означает дутую немощь.

    Ближе к концу игры Анжела просит отдать нож: она хочет покончить с собой, чтобы избежать мучений. Джеймс отказывается, на что Анжела замечает: бережешь для себя? И хотя Джеймс несколько раз за игру произносит, что не убил бы себя, именно это случается в главной концовке. Я в курсе, что есть и другие, в том числе когда он уходит из города с новой женщиной, а в лучшем случае — даже с ребенком. Но получить хорошую концовку при первом прохождении, скажем мягко, затруднительно. Поэтому суицидальный конец истории я считаю каноничным.

    Завершение самоубийством по-своему хорошо тем, что вписывается в концепцию игры. Она становится абсолютно, полностью завершенной. Дело в том, что игра начинается на парковке возле пристани. Оттуда Джеймс начинает путешествие в город. На парковке стоит его машина. При попытке открыть багажник Джеймс говорит, что почему-то не хочет этого делать — в то время как взять вещи из машины, казалось бы, самое логичное дело.

    Здесь и таится главная недосказанность игры: в багажнике машины лежит тело его жены. Повидав ужасы города, лишенный всех надежд Джеймс, пережив смерть жены дважды, возвращается на парковку. Там он заводит автомобиль и топит себя с телом жены. Не все из этого показано в игре, но концепция не оставляет других толкований.

    На мой взгляд, второй Silent Hill — это “Преступление и наказание” нашего времени. В нем заложен такой плотный концепт, что можно разбирать очень долго. Это самообман, оправдание преступления, различные приемы, чтобы убедить совесть в обратном, подавленная сексуальность, ненависть к зависимому человеку и целый короб подобных штучек. И это только Джеймс, не считая второстепенных персонажей вроде Анжелы, Эдди и Лоры. Подобно Раскольникову или Соне, каждого персонажа можно брать и рассматривать часами.

    Пишу это, чтобы напомнить: второй Silent Hill — важный культурный продукт нашего времени. Понимаю, это несколько странно: применять к игре формулировки, которые больше подходят книге. Да, из-за формата не все могут ознакомиться с ней. Но будь у вас лишние несколько часиков — не знаю, правда, откуда они в сорок лет — ознакомиться не помешает.

  • Микросервисы

    Беда микросервисов в том, что их разработчики не понимают одну вещь. Хотя формально микросервисы отделены друг от друга, на самом деле они связаны, потому что подчиняются общим требованиям.

    Обычно об этом не думают и колбасят пачки сервисов. Доходит до того, что один сервис пишут на Питоне, второй на Ноде, а в третьей команде экстремисты протащили Хаскель. И все такие — а что такого, у нас микросервисы, общение по HTTP JSON, какая разница, что крутится в кубернетисе?

    Не все равно хотя бы по следующим причинам.

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

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

    Далее трассировка. Каждый сервис трекает в общей базе request-id, с которым его вызвали. Записывается начало запроса, конец и статус. Должна быть админка, где вводишь request-id и система рисует граф вызовов с метриками. Опять же, где вы такое видели?

    Лимиты. У каждого сервиса должен быть механизм рейт-лимита, чтобы его не ддосили. Как бывает обычно? Ты вызвал сервис 1000 раз, и начинаются крики “кто нас дидосит!!11”. Так ведь нигде не написано, сколько раз можно вызывать! Должна быть прокладка, которая считает число запросов в разрезе потребителей и внятно отвечает, сколько ждать до обнуления. Такое хоть где-то бывает?

    Длины массивов. Сервис принимает список айдишников. Передаю 10 тысяч и получаю статус 500. Начинается восточный базар: передавай по сто. Нет, это мало, давай по тысяче? Нет, давай по пятьсот. Ну ок.

    Разумеется, этого не должно быть. У каждого массива или строки должен быть верхний лимит — чего я не наблюдаю.

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

    Здесь и кроется предубеждение: хотя формально микросервисы не связаны друг с другом, на практике это клоны одного проекта. Изменения в клоне отражаются в его копиях, поэтому ни о какой самобытности не может быть и речи. Правильные микросервисы — это унификация, полный отказ от самостоятельности.

  • Нейролица

    В интернете все больше лиц, нарисованных нейросетью. На картинке выше — какой-то медицинский стартап. Предполагается, что все эти улыбчивые люди — довольные клиенты сервиса.

    Мне неприятно смотреть на эти лица, потому что они выглядят как натертые воском яблоки. Каждый раз смотрю и не понимаю: откуда эти блики? Они что, собрались на oiled-вечеринку? Ясно, что иллюстраторы любят градиенты и блики, но почему нельзя обучить алгоритм на каких-то реальных фото?

    Моральных аспект: искусственные лица подаются как клиенты бизнеса, которых на самом деле нет. Тут включается внутренний голос и говорит: будь на фотках люди с фотостоков, тебе было бы легче? Это же модели, актеры, которые сегодня врачи, завтра домохозяйки, а послезавтра у них съемки белья для Вайлдберриз. Прям так сильно тебе нужны настоящие люди?

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

  • Разрешения

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

    Может быть, не все помнят ранний Андроид, а я помню. В версиях до какой-то там у Андроида не было разрешений по требованию. Это значит, ты ставишь какой-нибудь Фонарик Про, а он говорит: нужен доступ к фоткам, контактам, акселерометру и еще десяти пунктам. А если не согласен, иди лесом. У Эпла разрешения были по запросу с возможностью позже отозвать их. Модель Гугла была ужасна, ее обузили так сильно, что пришлось сделать как у Эпла.

    Так вот, возвращаясь к десктопу. Хотя в нем есть система разрешений, ее возможностей не хватает. Например, я бы хотел регулировать, какой программе можно в интернет, а какой нет. Чтобы снять в настройках чекбокс у программы — и все, шабаш. Не увидишь модалки, что вышло обновление. Не появится прогресс-бар с загрузкой. Не будет сбора метрик и кликов. Сейчас, чтобы победить эту хрень, приходится возиться фаерволом.

    Вряд ли такое произойдет, но помечтать не вредно.

  • Last in Clojure

    Chat GPT: No, the last function is not particularly expensive for vectors in Clojure. It runs in O(1) time because vectors in Clojure support efficient access to their last element.

    Meanwhile, Clojure:

  • Подробней о last

    Поскольку в заметке про last отметились только профильные специалисты (в Телеграме), считаю, нужна его подробная версия. Я и сам понимаю, что для таких огрызков служит Твиттер, но чего нет — того нет.

    Итак, дело вот в чем. Коллега спрашивает у Chat GPT, насколько дорого получить последний элемент вектора функцией last в Кложе. На это чат говорит — все тип-топ, у вектора доступ к последнему элементу работает за O(1).

    Все бы ничего, но в ответе напутаны правда и ложь, и в целом он не верный. Чтобы в этом убедиться, откроем исходный код функции last:

    (dеf
     ^{:doc "Return the last item in coll, in linear time"}
     last (fn ^:static last [s]
            (if (next s)
              (recur (next s))
              (first s))))
    

    Полагаю, даже человек, едва знакомый с Лиспом, увидит паттерн. Функция принимает коллекцию, и если в ней больше одного элемента (есть хвост), то она вызывает себя с хвостом. Когда длина хвоста равна одному, возвращается этот элемент.

    В коде нет проверок на тип коллекции, например, отдельно для вектора или списка. Все очень просто и линейно.

    Наконец, докстринг функции как бы говорит нам: вернуть последний элемент коллекции за линейное время. Линейное, Карл, то есть такое, что растет с числом элементов, то есть O(N).

    Поэтому буквальный ответ на вопрос таков: нет, функция last вернет последний элемент вектора за O(N) шагов. Что не подходит для больших векторов.

    Спрашивается, откуда у чата уверенность в правоте? Дело в том, что вектор действительно хранит ссылку на последний элемент. Для доступа к нему служит функция с забавным названием peek (заглянуть). Она работает только для вектора и для всего остального кинет ошибку.

    Можно предъявить Ричу Хикки: надо было сделать интерфейс Lastable с методом getLast. Обычные коллекции бегут с головы, а вектор берет с конца. Все это упаковано в last, который переключает на нужную логику. Все довольны и смеются. Но имеем то, что имеем, тем более что я согласен с текущим положением дел. Из всех коллекций только вектор может вернуть последний элемент. Остальным коллекциям это не положено по идеологическим причинам.

    Скорее всего, чат прочитал вопросы на StackOverflow и слепил некую компиляцию. Вроде бы да, но вроде бы нет. В этом и состоит вред: чат выдает что-то, очень похожее на истину. Выглядит настолько правдоподобно, что принимаешь за чистую монету. Но если проверить, то окажется, что не в карты, а в нарды, и не корову, а машину, и не выиграл, и проиграл.

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

  • Deed: a fast encoding and decoding library for Clojure

    Table of Content

    About

    Deed is a library to dump any value into a byte array and read it back. It supports plenty of types out from the box: Java primitives, most of the Clojure types, Java collections, date and time, and so on. It supports even such tricky types as atoms, refs, and input streams. The full list of supported types is shown in the “Supported Types” section below.

    Deed can be extended with custom types with ease. There is a contrib package that extends encoding and decoding logic for vectors from the the well-known mikera/vectorz library.

    Deed is written in pure Java and thus is pretty fast (see the “Benchmarks” section). It’s about 30-50% faster than Nippy.

    It doesn’t rely on the built-in Java Serializable interface for security reasons. Every type is processed manually.

    Deep provides convenient API for reading the frozen data lazily one by one.

    Motivation

    Obviously you would ask why doing this if we already have Nippy? This is what I had in mind while working on Deed:

    1. The library must be absolutely free from dependencies. This is true for the deed-core package: it’s written in pure Java with no dependencies at all. By adding it into a project, you won’t blow up you uberjar, nor you will have troubles with building a native image with GraalVM.

    2. Any part of Deed that requires 3rd-party stuff must be a sub-library. So you have precise control of what you use and what you don’t

    3. Unlike Nippy, Deed never falls back to native Java serialization. There is no such an option. Thus, your application cannot be attacked by someone how has forged a binary dump.

    4. Deed is simple: it blindly works with input- and output byte streams having no idea what’s behind them. It doesn’t take compression nor encryption into account – yet there are utilities for streams.

    5. The library provides API which personally I consider more convenient than Nippi’s. Namely, Deed can lazily iterate on a series of encoded data instead of reading the whole dump at once.

    6. Finally, why not using popular and cross-platform formats like JSON, Message Pack, or YAML? Well, because of poor types support. JSON has only primitive types and collections, and nothing else. Extending it with custom types is always a pain. At the same time, I want my decoded data be as close to the origin data as possible, say, LocalDateTime be an instance of LocalDateTime but not a string or java.util.Date. Sometimes, preserving metadata is crucial. To haldle all of these cases, there now a way other than making your own library.

    Read more →

  • World of Goo 2

    Вышла вторая часть World of Goo. Я долго не знал об этом, потому что повестка забита другим, да и на работе зашиваюсь. Однако это самая приятная новость за последние полгода или около.

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

    Шестнадцать лет исполнилось моему старшему ребенку. Помню, как начал играть в Гушек, когда ему было пять месяцев. Гушек прошли вдоль и поперек двое моих детей, третьей еще предстоит.

    Если вы не играли в первую часть, горячо советую это сделать. Игра — не просто залипательная казуалка. В ней много попыток что-то донести между строк. Почти каждый уровень — это метафора на что-то из жизни. Попадаются намеки на отношения, красоту и любовь. Есть серия уровней в компьютере, где моделируется перехват трафика, балансировка нагрузки и другие айтишные штучки. Есть длинный диалог на тему браузерных кук. Ближе к концу — эпичный поворот с удаленными электронными письмами.

    Приятно не только расставлять шарики, но и ловить подсказки, намеки, искать отсылки. Гушки — настоящее произведение искусства. Спасибо двум парням, которые заставили часами залипать в этот мир. Прошло шестнадцать лет — и я снова загляну туда, пусть ненадолго, но с тем же интересом.

Страница 4 из 86