Вильнюс

Гостевой пост. Текст написан любимым корректором.

В Вильнюсе мы были уже несколько раз, но почему-то до сих пор не написали о нём. А тут такое везение: можно и даже нужно пожить в древней столице почти неделю!

Исторически сложилось, что Вильнюс для наших людей — соседний город, вовсе и не заграница. Много лет мы были единым народом, одной страной. Белорусские дети учат в школе историю возникновения стольного града, годы жизни и правления великих князей, которых литовцы считают своими. Долгое время Вильнюс был центром белорусской мысли и белорусской культуры: здесь жили или просто бывали Адам Мицкевич (который в Польше поляк, а в Беларуси — белорус), Янка Купала, Бронислав Тарашкевич и многие, многие другие светочи, деятели и так далее. Вильня была негласной белорусской столицей, городом вольнодумства и борьбы за независимость…

В общем, смысл этого пассажа — братья мы, у нас одна история и очень схожие традиции. Хотя нельзя отрицать, что всё-таки литовцы — не славяне, и это видно.

Какой-то костёл

Какой-то костёл

Я постараюсь не слишком длинно и занудно описать свои ощущения и впечатления от города и людей.

Читать далее...
Планировщик маршрутов v2.0

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

  1. Самое главное, самое сложное и самое незаметное: поменялся алгоритм поиска. Вместо точного и медленного метода ветвей и границ теперь почти точный и метаэвристичекий метод муравьиной колонии. В основе проекта лежит Travelling Salesman Problem (Задача коммивояжёра), которая является NP-полной. А это означает, что нет другого пути решения её, кроме как перебрать все варианты. Количество вариантов можно посчитать по формуле \(n! \over 2\), где \(n\) — количество промежуточных городов. Если подставить в эту формулу, например, 15 получим 653 837 184 000 вариантов, а это уже очень много. Поэтому пришлось воспользоваться приближенным алгоритмом, который работает быстро, но и решение выдаёт не всегда оптимальное. Кстати, так как внутри алгоритма задействован генератор случайных чисел, решение может меняться от запуска к запуску, достаточно нажать кнопку обновить.

  2. Я убрал из проекта AngularDart, что позволило уменьшить размер JavaScript-файла в 3 раза.

  3. Теперь можно скопировать ссылку с маршрутом и отправить кому-нибудь. Только осторожно, чем больше городов будет в маршруте, тем длиннее будет ссылка и некоторые браузеры могут не справиться с такой длиной. Вот вам, например, ссылка на маршрут по всем европейским столицам.

  4. Упростилось добавление городов в маршрут. Из вариантов объектов теперь убираются лишние, если вариант только один, то он добавляется автоматически. И самое главное: можно добавлять города, даже не прикасаясь к мышке, используя для выбора варианта клавиши «Вверх», «Вниз» и «Enter».

  5. А ещё теперь можно развернуть карту на весь экран, чтобы было удобнее её изучать.

  6. Исправлены ошибки.

Маршрут по Европе

Маршрут по Европе

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

Проблемы с запуском планировщика

Поделюсь некоторыми сложностями, с которыми я столкнулся, запуская планировщик маршрута.

Конечно, во время самой разработки все сложности сводились к алгоритмическим. В Dartium всё работало как положено с нужным количеством ошибок и предупреждений. Проблемы начались сразу после компиляции в JavaScript и запуске в обычном браузере (кто бы сомневался). Проект не запустился, выдав странного вида stack trace. Ну что же, пересобираем проект без минификации. Stack trace стал читаемым, ошибки понятными. Как оказалось, вся проблема была в аннотации @MirrorsUsed. Дело в том, что mirrors (так называется reflection в Dart) занимает непозволительно много места в скомпилированном коде. И для того, чтобы этого избежать, авторы Dart предлагают использовать эту аннотацию для указания библиотек и классов, информацию о которых нужно будет получить во время исполнения (кстати, в будущем обещают доработать компилятор, что избавит от необходимости использовать эту аннотацию). Так вот, мой класс контроллера не был указан в @MirrorsUsed и, соответственно, AngularDart не мог с ним работать.

Исправил, скомпилировал, не запустилось. Правда, в этот раз я увидел несколько больше, чем просто пустую страницу. Немного поисков — и обнаружилась вторая проблема: нельзя в шаблонах AngularDart использовать методы встроенных объектов Dart. То есть в Dart можно, а в JavaScript нельзя. Конкретно в моем случае было написано:

{{segment[0].distanceTo(segment[1]).round()}}

Понятное дело, JavaScript и не догадывается о методе round у типа double. Да, собственно, про существование double он тоже не догадывается. Поэтому нужно писать геттер или вспомогательную функцию. Например так:

{{segment[0].distanceToRound(segment[1])}}

Ура, запустилось! Теперь осталось собрать минифицированную версию и выложить. Но после сборки проект снова упал с непонятным stack trace, как и в самом начале.

Не один час был потрачен на гугление всевозможных причин. Дело почти дошло до того, чтобы написать новый багрепорт о неправильной компиляции. Но я вовремя остановился, и хорошо, потому что ошибка была у меня и, кстати, довольна простая: я пытался проинициализировать Яндекс.Карты, а функция-конструктор не существовала. Вот проблемная строка, она соответствует map = new ymaps.Map(“map”, options); в JavaScript:

map = new JsObject(context['ymaps']['Map'], ["map",  options]);

Но ведь несжатая версия работает! Моё предположение, что сжатая версия приложения инициализируется значительно быстрее и поэтому API Яндекс.Карт ещё не готово к тому времени, оказалось верным, и вызов функции ymaps.ready всё исправил.

Итог. Более-менее сложное Dart-приложение хорошо работает в браузерах без поддержки Dart. Но зато когда сталкиваешься с проблемой, которая возникает только в скомпилированном и минифицированном коде, можно потратить довольно много времени на поиск решения просто потому, что подсказок никто не даст, а код выглядит довольно запутанно. Но в своей практике я сталкивался со случаями, когда и обычное JavaScript-приложение после сжатия начинало вести себя неправильно и возникали те же проблемы поиска решения.

Хочется отметить минификатор JS, встроенный в Dart. Он заменяет все ; на переводы строк. Из-за этого в результате получается файл со множеством строк, а не с одной, как в случае с другими минификаторами. А это, в свою очередь, сильно упрощает ручной анализ кода.

Neunundzwanzig
29

29

Продолжим прошлогодний рассказ про числа. 29 — это нечётное натуральное число между 28 и 30.

Это десятое простое и четвёртое праймориальное простое число. Так же оно является шестым простым числом Софи Жермен. А ещё — простым числом Люка, Пелля, Эйзенштейна, Пиллаи.

29 является суммой трёх последовательных квадратов (\(2^2 + 3^2 + 4^2\)), атомным числом меди, числом дней в лунном месяце и периодом обращения Сатурна в годах. В турецком, финском, шведском, датском и норвежском алфавитах по 29 букв.

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

Чистим Git-репозиторий

Внезапно после множества правок, попавших на сайт после перехода на Hakyll, размер Git-репозитория оказался равным 80 Мб. Конечно, некоторую часть из этого занимают фотографии, но огромное количество занятого пространства приходится на старые удалённые файлы. В общем, идея всё почистить давно поселилась в моей голове. Теперь это время настало.

Для этой операции воспользуемся утилитой BFG Repo Cleaner.

Итак, извлекаем новую копию репозитория.

git clone --mirror git@github.com:dikmax/dikmax.name.git

Удаляем из истории всё, что больше 64 Кб.

java -jar bfg.jar --strip-blobs-bigger-than 64K dikmax.name.git

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

cd dikmax.name.git
git reflog expire --expire=now --all
git gc --prune=now --aggressive

Теперь размер репозитория уменьшился в два раза, до 39 Мб, что чуть больше размера всех имеющихся на сайте фотографий.

Заливаем изменения на GitHub:

git push --force

А теперь то, что оказалось важным, но в инструкции отсутствует. После этой операции HEAD будет указывать на тот же коммит, что и раньше, а это значит, что возможно выполнить git push в любой ранее извлечённой копии. Эта команда выполнится безо всяких предупреждений и зальёт разницу, то есть как раз все те файлы которые мы благополучно из репозитория выпилили. Более того, так как и порезанные коммиты из репозитория никуда не исчезнут, то получится что у нас будет храниться две версии для каждого старого коммита: полная и порезанная. Поэтому, если в репозиторий могут добавлять изменения несколько человек, то все они должны быть предупреждены и действия должны быть согласованными. В моем же случае git push выполнял автоматически скрипт синхронизации комментариев с Disqus, что сводило все усилия сделать всё как надо к нулю.

Предыдущий абзац одним предложением: абсолютно все рабочие копии должны быть извлечены заново.

← СтаршеМоложе →