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

суббота, 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.