2012-01-10

status $ haskell + projectEuler

Продолжаю на досуге мучить haskell и решать задачки с "Проекта Эйлер".
Решил over 90 задач, уже в топ-50 по Украине
Среди знакомых ников в первой сотне есть adept и jdevelop (оба тоже с haskell)
Задачки разные -- в основном всякий матан: простые числа, числа фибоначчи, "фигурные" числа, функция Эйлера, Диофантовы уравнения, цепные дроби, комбинаторика... посчитать сумму, посчитать разность... посчитать кол-во, найти минимум ....
Есть немного динпрога, часто в в виде задач на поиск пути.
Пару задач уже встречал то-ли на ACM, то ли на тренировках к нему.
Из более "прикладных" задач понравилось "54: даны карты двух игроков в покер. определить, сколько раз выиграл первый", "59: дан неизвестный текстовый файл, зашифрованный xor-ом c 3-хсимвольным неизвестным паролем, расшифровать брутфорсом" и "96: написать решалку судоку",

"Pure C" Ненависти Псто.

Пишу обвертку вокруг одной библиотеки, написанной на "сишечке".
  • Пяток структур и полсотни функций в каждом хидере, документирована от силы четверть. Итого -- тысячи функций, которые вроде бы умеют все, но чтобы сделать что-то, на пол-шага отличающееся от примеров, приходится достаточно долго курить, а клиентский код получается весьма обширным. В результате -- за деревьями не видно леса.   
  • Отсутствие модификаторов доступа в С -- мне как клиенту библиотеки непонятно, какие данные я могу менять напрямую, а какие -- через функции библиотеки. Можно было бы спрятать приватные данные в pimpl, но автор до этого не додумался.
  • Pure C реализации вектора/списка/хешмапы. Через void*, по другому ведь никак. Типобезопасность? Нет, не слышали. Хорошо, если комментарий есть -- "в этом списке char*, в этом -- struct foo*. В динамических языках хоть ассерт можно поставить, в С -- либо все ок, либо расстрел памяти.
  • Вау, автор додумался до идеи конструктора/деструктора. Поэтому для каждой струкутры есть
    struct lib_foo * lib_foo_new(/*параметры*/); 
    void lib_foo_free(struct lib_foo*);
    
    даже если это
    struct lib_foobar {
      int fb_foo;
      int fb_bar;
    };
    
    Соответственно, все обьекты выделяются через malloc в хипе, в том числе и члены членов членов членов главной структуры. 
  • А до виртуального деструктора автор не додумался (Или решил, что это оверхед). Поэтому при оборачивании обьекта в умный указатель приходится постоянно передавать ему делитер.
  • Привычка добавлять сокращенное имя структуры как префикс поля (те самые fb_) в примере выше. В результате -- в клиентском коде будет куча копи-пасты вида
    lib_foo_list * fill_foo(...) {
      lib_foo_list * foos = lib_foo_list_new();
      fill(foos->foo_list, ...)
      return foos;
    }
    
    lib_bar_list * fill_bar(...) {
      libbar_list * bars = lib_bar_list_new();
      fill(bars->bar_list, ...)
      return bars;
    }
    
    lib_baz_list * fill_baz(...) {
      libbaz_list * bazs = lib_baz_list_new();
      fill(bazs->baz_list, ...);
      return bazs;
    }
    
    которую не засунуть в шаблон.
  • Попытка сэкономить на копировании приводит к странному контракту функций - если функция завершилась успешно -- она владеет переданными обьектами. Если с ошибкой - то обьектами владеет клиентский код. Отсюда: 
    1. В клиенской функции нужна секция обработки ошибок. 
    2. Это не типичная для "сишечки"
      clear_foo:
        lib_foo_free(foo);
      clear_bar:
        lib_bar_free(bar);
        
      return err; 
      
      а нечто позапутанней с дополнительными return-ами или goto-ми
  • Еще запутанней с функциями, принимающими char*. Одна функция копирует переданную строку, соседняя -- ее "овладевает". Комментариев обычно нет, смотри примеры или исходный код. И да, строки мы копируем через strdup, а длину меряем через strlen.