*** ВНИМАНИЕ: Блог переехал на другой адрес - demin.ws ***

среда, 17 ноября 2010 г.

Активный язык

В интервью проекту "OpenQuality" в сказал, что не имею особого пристрастия к конкретному языку. Важен проект, а не язык, на котором он реализуется.

А как насчет вопроса «а на каком языке вы наиболее продуктивны»?

Например, спортивное программирование, где нужно как можно быстрее выдать работающий код, нагруженный часто сложными алгоритмами.

Кстати, есть распространенное заблуждение, что спортивное программирование – это обычно куча корявого и запутанного, хоть и работающего кода, который совершенно непригоден для «обычного» программирования. Это не так, и зависит от конкретного человека. Можно, например, посмотреть код двух ТопКодеровских лидеров – ACRush и Petr. Код второго очень чистый, начиная от нормального форматирования и заканчивая общей понятной структурой решения. Код же первого же – обычно по внешнему виду оставляет желать лучшего (хотя это нисколько не умаляет его бесспорного лидерства), и читать его гораздо сложнее.

Так вот, я задал себе вопрос – а на каком языке я бы мог участвовать в контестах, где идет учет времени, и нет возможности копаться в документации по языку или библиотеке? И честно говоря, С++ находится далеко впереди Java и C# (Си не берем из-за бедности стандартной библиотеки). Мой уровень «погружения» в С++ (моменты языка, бесчисленные шаблоны уже виденного кода, знание библиотеки и т.д.) не сравним с той же Java.

А сколько языков у вас «живут в пальцах»?

Неконстантные ссылки

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

Проблема: использовать ли не константные ссылки в аргументах функций?

Мой подход: нет.

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

Лично нарвался недавно на собственную мину (конечно, упрощенный вариант):

T a = 10;
...
f(a);
...
assert(a == 10); // BOOM!!! WTF?! Who changed this?

А причина в «void f(T& a);», а должно быть «void f(const T& a);» или «void f(T* a);». Функция «f()» почему-то изменила значение «а», а писал я ее давно и успел забыть такую ее «особенность». Но из кода «f(a)» сходу не видно – может эта функция изменить «а» или нет.

А как могло произойти, что мне вообще пришло в голову сделать параметр «a» неконстантной ссылкой? Лично у меня это случается, когда переменная изначально была внутри функции, и в какой-то момент я решил сделать ее параметром, а менять в коде везде «a.» на «a->» было просто лень, вот и сделал ссылку, вместо указателя. За что и поплатился, позже.

Кстати, один из аргументов, приводимый людьми, выступающими за неконстантные ссылки –это «писать "a." приятнее и понятнее, чем "a->" или "*a"». Также ссылка более надежна с точки зрения NULL (сделать ссылку, указывающую на NULL конечно можно, но тут уже надо постараться). Тут можно выйти положения так:

void f(T* ptr_a) {
  assert(ptr_a != NULL);
  T& a = *ptr_a;
  ...
  if (a.foo()) ...
}

Небольшой лишний код, но решены обе проблемы: проверка на NULL и необходимость разыменовывать указатель каждый раз. А главное, в вызывающем коде придется писать так: “f(&a)”, что явно укажет на факт возможной модификации аргумента внутри функции.

Например, в C# есть специальной ключевое слово «ref», которое надо ставить перед аргументами в вызывающем коде, если хочется передать что-то по ссылке. По мне,это очень хорошее свойство языка.

Исключения из правила

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

Например:

void print(std::ostream& os, const T& a) {
   os << a;
}

или

void save_to_db(Database& db, ...) {
   db.connect();
   db.save(...)
   ...
}

А когда вы используете неконстантные ссылки?

Ну и чтобы два раза не вставать, пара личных маленьких радостей:

  • Мое интервью проекту "OpenQuality".
  • Уверенно решил две задачи в недавней SRM 487. А достижение в том, что во второй задаче даже применил DP, хоть и тривиальное. В процессе контеста был последним в комнате, так как долго возился с первой задачей, но потом почти все упали на фазе challenge'а и на тестах, и я оказался вторым в комнате. Кстати, настоятельно рекоммендую сайт CodeForces. Наши ребята сделали отличный сайт для контестов и регулярно их там проводят. В отличие от ТопКодера русский язык там в почете, задания предоставляются на обоих языках, и выбор язык программирования гораздо шире. Также можно там спросить совета по задачам и получить квалифицированный ответ от бойцов.

суббота, 13 ноября 2010 г.

Screencast’ы

А вы смотрите скринкасты?

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

Например, посмотрев три видео Coding in Objective-C 2.0 (в принципе, можно скачать бесплатно), я могу написать небольшую программу на этом языке с использованием классов и имею представление, как работает управление памятью в Objective-C (кстати, весьма интересная концепция, я бы ее назвал – ручной сборщик мусора, когда память освобождается вроде как автоматически, но ответственность за правильность подсчета активных ссылок на объект возлагается на программиста).

Сейчас в процессе просмотра Writing Your First iPhone Application и на очереди Erlang in Practice.

Но есть со скринкастами одна засада – они должны быть качественно сделаны, иначе будет только сожаление о потерянном времени. Скринкасты от The pragmatic Bookshelf сделаны, на мой взгляд, очень хорошо.

А какие скринкасты смотрите вы? Какие можете посоветовать?

P.S. Кстати, онлайн-магазин «The pragmatic Bookshelf» нравится мне все больше и больше. Все книги продаются в электронном свободном варианте, причем в нескольких форматах (pdf, mobi, epub).

вторник, 9 ноября 2010 г.

Как называть getter'ы и setter'ы

Для именования функций записи и чтения членов класса (getter/setter) в стандартном C++ есть три часто используемых приема.

1. Чисто плюсовый вариант, основанный на семантике ссылок.

class Foo {
  Value field_;
public:
  Value& field() { return field_; }
  const Value& field() const { return field_; }
};

Использование:

Foo foo;
foo.field() = field_instance;
field_instance = foo.field();

Плюсы: краткость текста, близость к нотации "свойств", и возможность использования присвоения в каскаде (foo1.field() = foo2.field() = 2;).

Минусы: использование синтаксиса вызова функции слева от знака присваивания выглядит непривычно.

2. Способ в стиле Java.

class Foo {
  Value field_;
public:
  void setField(const Value& value) { field_ = value; }
  const Value& getField() const { return field_; }
};

Использование:

Foo foo;
foo.setField(field_instance);
field_instance = foo.getField();

Плюсы: ясность и очевидность синтаксиса.

Минусы: многословность из приставок "get" и "set".

3. Cтиль Objective-C

class Foo {
  Value field_;
public:
  void setField(const Value& value) { field_ = value; }
  const Value& field() const { return field_; }
};

Использование:

Foo foo;
foo.setField(field_instance);
field_instance = foo.field();

Плюсы: краткость текста при чтении (нет почти бессмысленной приставки "get") и очевидность при записи.

Минусы: я пока не нашел таковых.

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

Лично я почти всегда предпочитал способ #1, но после своего последнего поста я перешел на третий.