Категория: Компьютеры

Заметки о программировании и на околокомпьютерные темы

Вложенные категории: Delphi, Игры

Гугл закрывает Google Reader.

У меня только одна реакция:
“What the fuck? Why?”

“Бла-бла им пользуется слишком мало людей. Сложно поддерживать”.

1. Мало? Посмотрите, какой поднялся шум.
2. Офигительно сложно поддерживать продукт, в котором ничего не меняют уже несколько лет и который ни с чем не связан. Работает себе и работает.
3. И как же “свободная и открытая инфраструктура”, а, Гугл? Мало людей пользуются RSS – отлично, так мы его вообще похороним. Пусть через Фейсбук новости смотрят, там всё свободно и открыто.

Ад и отчаяние.

Петиция не закрывать ридер, на которую никто не обратит внимания.
Лучшие альтернативы Google Reader, которые всё равно уже будут не такими удобными.

Про паскаль

Если считать, что паскаль вымирает, то вымирать он умудряется с огоньком. У нас есть Delphi, который при всех проблемах управления постоянно вводит крутые возможности в язык. Он компилируется под win-x32, win-x64 и маки. Есть FreePascal, которые поддерживает большую часть вводимых Дельфи фич и компилируется подо всё на свете, в том числе под linux-x32, linux-x64 и native-android (с помощью этой штуки компилятор FPC можно встроить в интерфейс дельфи, если не нравится Lazarus). И есть Oxygene, который компилируется под .NET, Android и iPhone, и тоже вводит в язык много крутого. (И в нём вместо VCL используются нативные формы каждой платформы, типа Windows Forms).

То есть, в общем-то, покрыта вся мыслимая разработка для локальных компилируемых языков. Кроме интерпретируемых языков, единственный язык с таким покрытием – это C/C++. И кстати, если пользоваться только паскалём уровня Delphi 7, стандартной библиотекой вместо винапи, и ограничиться консольным или сервисным приложением, то программу можно будет скомпилить на любом из этих компиляторов. Интересно, пробовал ли кто-нибудь?

Про регэксы

Регэксы – это офигенное средство, не только потому, что с их помощью легко записывать формат строки. Его легко преобразовывать и быстро парсить!

Может показаться, что парсер, написанный от руки – самый быстрый, но это не так. Вообще, декларативные языки (“кнопка такого-то размера”) всегда лучше инструктивных (“нарисуй прямоугольник, если кликнут – вызови функцию”), поскольку декларацию можно оптимизировать или реализовать частично.

Но регэксы ужасны – стоит попытаться написать что-нибудь достаточно сложное, как выясняется, что:
– повторяющийся набор групп матчится только один раз
– наоборот, два одинаковых набора подряд матчатся как разные группы (следовало бы ожидать…)
– группам можно задать имена, но они должны быть уникальными (нафига тогда имена?)
– указать “если есть блок А, должен быть блок Б” крайне сложно, а уж “должен быть блок Б здесь либо чуть позже” вообще нельзя
И так далее.

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

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

Приватный hg-репозиторий в интернете

Задача: поднять для личного hg-репозитория постоянный (приватный) адрес в интернете.

Для публичных репозиториев это легко сделать на Google Code или github или SourceForge. Как быть, если кодом делиться не хочется?

UPD. Инструкция верна, но есть путь проще – см. в конце.

1. Регистрируемся на BitBucket (можно зайти через гугл-аккаунт). Подтверждаем почту, выбираем пароль для доступа к репозиториям.
BitBucket позволяет сделать сколько угодно частных репозиториев, но к каждому доступ не больше пяти человек. Бесплатно.

2. Запускаем командную строчку. Если меркуриал не в %PATH%, делаем cd C:\Program Files\TortoiseHg (например) и set PATH=%CD%;%PATH%.

3. Переходим в папку с репозиторием cd C:\Path\Repo и запускаем hg serve.

Приложение А. Если у компа нет постоянного адреса в интернете, регистрируйтесь на DynDNS и полученные логин с паролем засуньте в рутер – почти все рутеры сейчас умеют сами обновлять DynDNS.

4. Пробросьте порт 8000 на рутере на свой компьютер. Убедитесь, что http://your-pc-name:8000 открывается.

5. В BitBucket сделайте “Импорт репозитория” и укажите этот адрес. Подождите немного. Ура, репозиторий со всеми правками сдублирован на битбакет.

UPD. Как показал в комментариях korrshun, гораздо проще создать пустой репозиторий и сделать из домашнего туда push. Оказывается, в репозиторий меркуриала можно пушить сколько угодно независимых веток 0__0, то есть, можно в один репозиторий слить несколько.

Tell me what I’m going to use it for

For those who didn’t know, there’s a new pascal-based compiler on a market for a while, and a pretty cool one at that. Enter RemObjects Oxygene.

It’s Visual Studio-based, compiles to .NET, Android Java and iPhone Cocoa, resembles Pascal and implements the majority of its cool features like generics. Parts of language are redesigned, some for better, some for worse.

Cool feature. Even the main unit now has the interface/implementation sections.

namespace Application1;
interface
implementation
begin
  Console.WriteLine("The magic happens here.");
end.

Uncool feature. initialization/finalization sections are no more. I guess you can kinda replace them with class constructors, but they were so much better.

Anyway.

The language is indeed pretty fresh, with support even for WinRT while Delphi has yet to convince Microsoft to let everyone else have a part of the cake. Turns out, the only way to do native WinRT applications is through using Microsoft Visual C++ Runtime. Ha-ha, funny joke Microsoft, you.

So I thought about playing with it for a change.
No, I’m not betraying Delphi just yet. It’s still pretty cool, compiling to 64 bit and not being afraid of anything.

But sitting before the empty Oxygene project, I have found myself at loss at what to do.
Okay, it runs. It compiles Hello World, alright.
What next?

Turns out, when you encounter a new language, you have to have a few use cases for it. And since you usually don’t know what this langage can do, it’s better if someone suggests those for you.

Inc(i)

Все знают, что когда перебираешь null-terminated строки, то нужно останавливаться по нулю:

while pc^<>#00 do Inc(pc); //ищем конец строки

Все знают, что когда перебираешь дельфийские строки, нужно останавливаться, когда индекс превысит длину строки:

while (i<Length(s)) and (s[i]=' ') do Inc(i); //пропускаем пробелы

Все знают, что у дельфийских строк в конце всё равно ноль.

 74 00 65 00 73 00 74 00 00 00

Но не всем и не сразу приходит в голову, что длину дельфийской строки тоже часто можно не проверять!
Второй пример можно записать так:

while s[i]=' ' do Inc(i); //пропускаем пробелы

В конце строки ноль, а ноль – это не пробел, поэтому цикл сам собой прервётся.

Где надо быть осторожным – так это при промотке строки назад. В начале строки нуля нет:

while (i>0) and (s[i]=' ') do Dec(i); //пропускаем пробелы в обратную сторону

type и class

В Delphi можно написать:

type HexString = string;

Так мы отметим специальный тип строк, который хранит в себе hex. Но для компилятора они ничем не отличаются от обычных. Вот это скомпилируется нормально:

var a: HexString;
  b: string;
begin
  a := b;
end;

Что, если мы не хотим разрешать такое копирование? (А обычно мы должны не хотеть! Разные по смыслу вещи нельзя присваивать, это опасно). Компилятор можно попросить создать “независимый тип”:

type HexString = type string;

Теперь строку типа HexString нельзя присвоить строке типа string, и наоборот.

Похожий приём работает с классами, только чуть иначе.

type
  HexStringList = TStringList; //можно присваивать HexStringList -> StringList и обратно!
  HexStringList = class(TStringList); //можно присваивать только HexStringList -> StringList, но не обратно!

Классы, в отличие от простых типов, поддерживают наследование. Более “частный” класс можно положить в переменную более общего, но не наоборот. Если мы объявляем тип без “class“, то мы просто создаём для него другое имя: оба типа на самом деле одно и то же. А с помощью “class(TStringList)” мы говорим компилятору “HexStringList – это частный случай StringList, он от него наследует”.

Но что, если мы напишем так?

type HexStringList = type TStringList;

Или так?

type HexStringList = type class(TStringList);

Ответы на это в следующий раз!

Don’t write what you don’t need

Опять программирование. Когда пишешь одну функцию, часто подмывает заодно написать с десяток – и обратное преобразование, и с параметром, и Анси-версию, чтоб уж сразу была библиотечка всего такого на потом. Программисты обожают библиотечки. У каждого программиста своя работа со строками и своё буферизованное чтение в запасах.

Но понемногу я пришёл к выводу, что это вредно.

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

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

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

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

Но мало того, когда вы захотите исправить ошибку, вам вдруг станет страшно. Вы подумаете: “А что, если?..”

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

Нет такого программиста, которому нравилось бы наугад портить свои проекты. И вы откатываете правки и оставляете функцию сломанной, а рядом пишете новую, functionName2(). На этот раз правильную.
И заодно ещё что-нибудь.

Wakan

Wakan 1.80.8 переводит 1 мегабайт текста за 4 секунды!
Стабильная версия, 1.67, на него же тратила 26.5 минут.

Ещё он понимает Аозора-Руби. Можно вставлять текст в таком: 大人《おとな》びた雰囲気 – формате, и вот эти скобочки превратятся в подпись над 大人. А ещё Вакан может сохранять свой собственный автоматический перевод в виде руби! Это значит, что можно загрузить книжку, нажать “Auto-Translate”, и Вакан расставит чтение всем тем словам, которые вы ещё не выучили. И эту книжку потом можно читать в любой из тысяч поддерживающих руби читалок, и слова будут подписаны!

Консты нереально круты

Да, я понимаю, что тут никто не пишет на дельфи, но раз уж я иногда что-то пишу о ней, то позвольте мне.

В дельфи есть элемент языка, которым все пренебрегают. Очень крутой. Это атрибут входного параметра const.
Вместо:
function IsStringAbrakadabra(s:string): boolean
Получится:
function IsStringAbrakadabra(const s:string): boolean

Зачем?
Строки в Дельфи ведут учёт ссылок. Каждое присваивание увеличивает счётчик на 1. Каждое зануление – уменьшает его. Поэтому любая функция, которая получает строки, преобразуется компилятором в следующую:

UStrLAsg(s); //увеличить счётчик ссылок
try
  //сама функция
finally
  UStrLClr(s); //уменьшить счётчик ссылок
end;

Два лишних вызова! И фрейм try..finally (это очень дорогая конструкция). Эта обёртка легко может тратить больше времени, чем сама ваша функция! Скомпилите и посмотрите в ассемблер – инлайнить такую дуру тоже пропадает всякая выгода.

На помощь спешит модификатор const! Он говорит компилятору, что вы клянётесь героиней любимого мультика не трогать полученной строки. Тогда можно учёт ссылок не вести, и фрейм try..finally тоже не нужен. Вместо 60 ассемблерных инструкций ваша функция внезапно компилируется в шесть!

Но это ещё не всё.
Мало добавлять const к строковым параметрам. Строки могут передаваться неявно. Функция, которая получает структуру со строкой внутри, тоже требует учёта ссылок и фрейма try..finally. Даже хуже: вместо прямолинейного UStrLAsg будет вызван AddRefRecord, который с помощью некоего подобия рефлекшна изучает вашу структуру и решает, каким полям нужен какой учёт ссылок. И так в каждой функции!
Дельфи не глупая, и если структуре совсем не нужен учёт ссылок, она поймёт это при компиляции, и фрейм не вставит. Но когда хоть одно поле требует учёта, вы получите пенальти в размере полного разбора всей структуры дважды.

Поэтому ставьте const везде, где можно. Ставьте const всему, что передаёте на копирование во всевозможные “SetSomething” или “InitSomething”. В крайнем случае он будет просто подсказкой читающему код.

Ещё очень важная информация: отключайте “String format checking” в настройках компиляции. Всегда. Сразу же. Эту опцию следовало бы назвать “замедлить в три раза все операции со строками, для того, чтобы спрятать от вас чудовищные баги в вашем коде”.