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

пятница, 29 октября 2010 г.

Ссылка на временный объект в списке инициализации конструктора

Имеем два исходника:

#include <iostream>

struct A {
  A();
  const int& i;
};

A::A() : i(123) {}

int main() {
  A a;
  std::cout << a.i << std::endl;
}
и

#include <iostream>

struct A {
  A() : i(123) {}
  const int& i;
};

int main() {
  A a;
  std::cout << a.i << std::endl;
}

Будучи скомпилированными компилятором от Sun или GCC, эти два примера печатают разные результаты. И первый - неправильный. Студия же 2010, с настройками по умолчанию, дает предупреждение и генерирует код, работающий правильно (точнее, как ожидает программист) в обоих случаях.

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

Ко мне пример попал как результат анализа реального бага.

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

Никому нельзя верить, даже себе

Есть (точнее был) у меня вот такой код:

const std::string& id() const { return id_; }
std::string id() { return id_; }

В нем есть одна досадная опечатка, из которой код:

order.id() = 123;

делал то, что от него не ожидалось, а точнее, ничего не делал. И проблема, как вы наверное уже догадались, в пропущеном значке "&" во второй строке. Должно было быть так:

const std::string& id() const { return id_; }
std::string& id() { return id_; }

Эта опечатка стоила мне часа поиска проблемы через вторичные признаки в виде иногда не обновляемой базы данных.

Причина? А все потому, что я поленился написать тесты изначально, решив, что это уж очень простые методы. Но теперь таки добавил для этого тест:

TEST(Order, GetterSetters) {
  Order order;
  ...
  EXPECT_EQ(0, order.id());   // Must be initialized.
  order.id() = 123;
  EXPECT_EQ(123, order.id());
  ...
}

Решил сэкономить время, а вышло наоборот.

Вывод: тесты, тесты и тесты.

среда, 20 октября 2010 г.

Умножение вручную на бумажке по-китайски

Не берусь утверждать, что этот способ эффективнее традиционного столбика, но все равно впечатляет.

четверг, 7 октября 2010 г.

Книги по обработке строке и производящим функциям

Покупка книг на Амазоне в Европе и Штатах дело крайне простое, удобное и быстрое (особенно для электронных книг). Тут также можно и продавать свои книги. Очень удобно и для владельца, когда надо избавиться от одноразовой книги, и для покупателя, так как можно купить реально дешевле (а потом тоже продать).

Надеюсь, почта России одумается и сделает нормальный сервис доставки.

В Лондоне мой любимый книжный - это Foyles на Charing Cross. Ни в Waterstones, ни в Borders не видел столь огромного отдела технической литературы, которая еще и великолепно отсортирована и разобрана по разделам. Например, отдельный шкаф с книгами чисто по компиляторам, или чисто по языку Хаскель, или по QA тестированию, а вдоль стендов про Java или C++ вообще можно ехать на самокате.

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

Куда я первым делом отправился во время визита в Москву на прошлой неделе? В "Библио-Глобус" на Лубянке. Кто знает в Москве офлайновый магазин с большим выбором и лучшей организацией стендов - поделитесь.

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

Но еще более приятно, что наша литература ничем не хуже.

В разделе компьютерном купил:

С. М. Окулов, Алгоритмы обработки строк



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

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

Вывод: иметь.

Далее, в соседнем разделе математики купил:

С. К. Ландо, Лекции о производящих функциях



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

Эта книга есть в свободном варианте.

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

http://www.mccme.ru/free-books/
http://e-maxx.ru/bookz/

P.S. Кто знает еще хорошие ссылки по электронным книгам - делитесь в комментах.

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

Функции, указатели на них и оператор "?"

Был удивлен, когда компилятор С съел вот такой забавный способ условного вызова функций:

#include <stdio.h>
#include <math.h>
int main() {
  int i;
  for (i = 0; i <= 1; ++i) {
    float a = (i ? floor : ceil) (10.5);
    printf("%d: %f\n", i, a);
  }
  return 0;
}

Для С++ надо написать:

#include <stdio.h>
#include <cmath>
int main() {
  int i;
  typedef float (*f)(float);
  for (i = 0; i <= 1; ++i) {
    float a = (i ? (f)std::floor : (f)std::ceil) (10.5);
    printf("%d: %f\n", i, a);
  }
  return 0;
}

или

#include <stdio.h>
#include <cmath>
int main() {
  int i;
  for (i = 0; i <= 1; ++i) {
    float a = (i ? std::floorl : std::ceill) (10.5);
    printf("%d: %f\n", i, a);
  }
  return 0;
}

Все программы выводят:

0: 11.000000
1: 10.000000

воскресенье, 19 сентября 2010 г.

Электронные книги - iPad, Kindle, Amazon и все все все

Недавно я обзавелся iPad'ом. Это лучший гаджет, который был у меня в жизни. Но сейчас я расскажу только про один аспект его использования - чтение книг.

Я давно коллекционирую интересные мне книги в электронном виде (pdf, djvu, mobi, epub и т.д.), но они просто лежали, так как читать с экрана в удовольствие я не могу, поэтому я обычно либо печатал на бумагу, или таки покупал бумажный экземпляр.

И вот все изменилось. Чтение накопленных запасов книг получило новый виток - iPad.

Форм-фактор, свечение экрана, время работы (2-3 дня при чтении 2-3 часа в день плюс серфинг через Wifi или 3G), и главное - программы для чтения просто приковывают к этому устройству.

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

Да и с художественной у меня нет особых причин не читать на айпаде. Первым делом я заправил туда подборочку любимого Рампо Эдогавы.

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

PDF

Если это нормальный файл-исходник, а не пиксельный скан, то можно спокойно пользоваться стандартным приложением iBook и закачивать туда книги через iTunes. Очень грамотная программа для чтения (i-все-таки). Но если приходится иметь дело с pdf'ками, которые по сути являются постраничными сканами (таких "книг" обычно большинство на просторах торрентов), то тут бесспорный лидер - это Good Reader. Очень быстро работает даже с тяжелыми файлами, умеет сам выкачивать файлы из интернета (HTTP, Dropbox, Google Docs, POP3/IMAP и т.д.), и самое главное - умеет делать конфигурируемую "обрезку" страниц. Это о-о-очень полезно. Часто книги отсканированы через пень-колоду, и например, в порядке вещей наличие гигантских отступов-полей, которые могут отжирать и так ограниченное пространство зоны чтения. А "обрезкой" можно персонально для каждой книги (для четных и нечетных страниц также) задать отображаемую часть листа. Вывод: Good Reader - это номер 1.

DjVu

К сожалению, Good Reader не понимает этот формат, а те программы, которые понимают, кривые. Поэтому я просто печатаю DjVu в PDF через PDFCreator и уже PDF заправляю на айпад.

Где брать книги? Конечно, торренты. Тут не надо ничего объяснять.

Отдельно расскажу по технологию Kindle. Это амазоновская читалка. Но есть еще и приложения Kindle для iPhone/iPad/iPod, Android, Windows Mobile, PC и т.д. В чем тут цимес? Для начала - время покупки электронных Kindle-книг на Амазоне исчисляется минутами: выбрал, оплатил и через пару минут книга сама будет закачена на устройство (можно сначала бесплатно скачать preview в виде десятка первых страниц).

Сами книги сделаны очень добротно, с хорошей и удобной версткой. Можно делать закладки, пометки в тексте, а при клике на слово вызывается оксфордовский толковый словарь (жаль, конечно, что не русский подстрочник). Также можно искать по тексту. А теперь ключевая фича - это синхронизация между различными устройствами. Допустим, у меня зарегистрированы читалки: программа-Kindle на PC, на айпаде и на Nexus'е. Все, что делаю с книгой на любой из читалок (ставлю закладки, пометки, позиция чтения) автоматически синхронизируется на другими устройствами.

Теперь я всегда на Амазоне сначала смотрю наличие электронной книги и покупаю ее, если есть (кстати, при покупке электронной версии не должно более быть проблем с доставкой, например, в Россию). Жалко, что пока ассортимент книг несравнимо мал, по сравнению с полным каталогом.

Есть с амазоновскими книгами одно "но". Файлы книг как таковые как бы пользователю не даются. Они видны во всех зарегистрированных читалках, и их можно скачивать повторно, но вытащить напрямую "официально" не получается.

Я написал письмо в Амазон:

Dear Sirs,

I've purchased a few books in Kindle format via Kindle application on iPad and Nexus. I would like to have those books as files (PDF, mobi, ebook etc). How can I get my purchased items this way?

Thanks,
Alexander


Получил ответ:

Hello Alexander,

Content you purchase from the Kindle Store (such as books, newspapers, magazines and blogs) can be read on most Kindle devices or devices running a Kindle application registered to your Amazon.co.uk account. Information about devices that can read Kindle content can be found in our Help pages here:

http://www.amazon.co.uk/kindlesupport

Kindle Store content is not currently viewable on any other electronic reading devices. We don't offer Kindle content in any other formats so you would have to see if they are available elsewhere in different formats to read outside Kindle applications.

I hope this helps.


Значит типа "хрен тебе".

Ясное дело, проще всего покопаться с программой на PC. Файлы с книгами я нашел быстро. Это обычный mobi-формат, но, конечно, зашифрованный. Но если книга читается, когда интернет отключен, значит ключ где-то на самом компьютере, а значит - его можно найти. На просторах интернета нашлась волшебная программа skindle, которая в момент из зашифрованной DRM-ленной книги делает просто mobi-книгу.

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

Конечно, айпад - это весьма дорогая читалка книг, есть альтернативы, но сам подход электронных книг - это какой-то нереальный скачок вперед.

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

UPDATE: Совсем забыл. Для чтения книг в CHM-формате тоже есть хорошее приложение для айпада.

воскресенье, 12 сентября 2010 г.

Проба на Эрланге

Решал я тут одну красивую задачу. Решение - рекурсивная функция, динамическое программирование.

Для эксперимента написал свою первую программу на Эрланге как решение этой задачи.

Вот оригинал на C++ (рекурсия с кешированием):

bignum f(int n, int k, int t, int& K, vector<vector<vector<bignum> > >& dp) {
  if (n == 0 && k == 0 && t == 0) return 1;
  if (n == 0 || k < 0) return 0;

  if (!(dp[t][n][k] == -1)) return dp[t][n][k];

  bignum ans = 0;

  if (k < K) {
    ans = ans + f(n - 1, k - 1, t, K, dp);
    if (t == 1 || (t == 0 && k < K - 1))
      ans = ans + f(n - 1, k + 1, t, K, dp);
  }

  if (t == 1 && k == K) {
    ans = ans + f(n - 1, k - 1, 0, K, dp);
    ans = ans + f(n - 1, k - 1, 1, K, dp);
  }

  dp[t][n][k] = ans;
  return ans;
}

А теперь Эрланг:

main(_) ->
  N = 50, K = 25,
  io:format("~w\n", [rbs(2*N, 0, 1, K)]).

rbs(0, 0, 0, _) -> 1;
rbs(0, _, _, _) -> 0;
rbs(_, K, _, _) when K < 0 -> 0;

rbs(N, K, 0, MAX_K) ->
  if 
    K < MAX_K-1 -> rbs(N-1, K-1, 0, MAX_K) + rbs(N-1, K+1, 0, MAX_K);
    K < MAX_K   -> rbs(N-1, K-1, 0, MAX_K);
    K =:= MAX_K -> 0
  end;

rbs(N, K, 1, MAX_K) ->
  if
    K < MAX_K   -> rbs(N-1, K-1, 1, MAX_K) + rbs(N-1, K+1, 1, MAX_K);
    K =:= MAX_K -> rbs(N-1, K-1, 0, MAX_K) + rbs(N-1, K-1, 1, MAX_K)
  end.

Красиво, а?

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

#!/usr/bin/env escript

main(_) ->
  N = 50, K = 25,
  io:format("~w\n", [rbs(2*N, 0, 1, K)]).

rbs(0, 0, 0, _) -> 1;
rbs(0, _, _, _) -> 0;
rbs(_, K, _, _) when K < 0 -> 0;

rbs(N, K, 0, MAX_K) ->
  if 
    K < MAX_K-1 -> rbs_memo(N-1, K-1, 0, MAX_K) + rbs_memo(N-1, K+1, 0, MAX_K);
    K < MAX_K   -> rbs_memo(N-1, K-1, 0, MAX_K);
    K =:= MAX_K -> 0
  end;

rbs(N, K, 1, MAX_K) ->
  if
    K < MAX_K   -> rbs_memo(N-1, K-1, 1, MAX_K) + rbs_memo(N-1, K+1, 1, MAX_K);
    K =:= MAX_K -> rbs_memo(N-1, K-1, 0, MAX_K) + rbs_memo(N-1, K-1, 1, MAX_K)
  end.

rbs_memo(N, K, T, MAX_K) ->
  Name = rbs,
  case ets:info(Name) of
    undefined ->
      ets:new(Name, [public, named_table]);
    _ -> true
  end,
  Key = {N, K, T, MAX_K},
  case ets:lookup(Name, Key) of
    [] ->
      Val = rbs(N, K, T, MAX_K),
      ets:insert(Name, {Key, Val}),
      Val;
    [{_, Val}] -> Val;
    Else -> Else
  end.

Странно, ведь можно было бы заложить кеширование прямо в языке. Ведь когда функция вызывается повторно с теми же самыми аргументами, то очевидно, что результат будет такой же (ибо Эрланг - это "чистый" функциональный язык, и side effects тут исключены). Но в целом функция кеширования весьма универсальна и ее можно быстро адаптировать для других ситуаций.

По времени выполнения эрланговый вариант не уступает C++. При это надо учесть, что Эгланге по умочанию арифметика "длинная", а в С++ мне пришлось использовать доморощенный класс bignum.