Home

Advertisement

eye
Просто бесподобен. Лучше я ничего не слышал.

Tags:

Building DLL in GHC

  • Nov. 5th, 2009 at 11:17 PM
eye
По умолчанию GHC не умеет собирать нормальный DLL под Windows (как с этим в других системах, я не знаю). Поэтому я написал для себя модуль (код ужасен, я знаю, я взял половину из туториала, половину наваял на скорую руку и вынес, лишь бы оно собирало мой код), для которого достаточно определить дополнительный раздел в CABAL — x-export, внутри которого перечислить модули, содержащие экспортируемые функции. Точнее, там нужен список модулей, которые будут добавляться в рантайм при помощи hs_add_root.
Правильно ли я понимаю, что на других системах с этим проблем нет? Т.е. всей этой свистопляской надо заниматься только под Windows? Если да, то в defaultDll, видимо, не нужно предпринимать каких-либо дополнительных действий, если система отлична от Windows.

Tags:

ID3v2: 2 vs 4

  • Nov. 2nd, 2009 at 10:19 PM
eye
Во время прогона по всей своей библиотеке, примерно в сотне из 4к композиций обнаружил неизвестные теги TT2 и TP1. 4-й байт был нулевым, что, вообще говоря, не по стандарту, т.е. фрейм невалиден. Оказывается, в старом варианте ID тега был всего лишь 3 байта, и данные теги соответствовали имени автора и названию альбома. Удивительны тут две вещи:
  1. Каким боком эти теги попали в новую версию? Видимо, какая-то кривая программа читала эти 3 байта в число (что логично), однако без всякой конвертации записала в 3-ю версию тегов.
  2. Однако explorer спокойно эти теги отображает в свойствах файла.
Т.е. какой-то мудак сломал файл, а какой-то умник решил исправить его ошибку таким способом. Теперь у меня когнитивный диссонанс, как это обрабатывать в библиотеке?
С одной стороны, такой фрейм вообще невалиден, с другой — не читать его некомильфо. Видимо, наваяю отдельную функцию, которая будет «восстанавливать» такие битые фреймы, по дефолту же их читать не надо.

ID3v2

  • Oct. 31st, 2009 at 11:47 PM
eye
Написал я библиотечку для загрузки/сохранения ID3v2 из mp3. Начал писать из интереса, когда добрался до реализации рекурсивного шифрования (теги, регистрирующие шифрования, сами зашифрованы), решил-таки посмотреть, а как сделано у других. Объемы кода меня напугали до ужаса, а нередкое отстутствие вообще какой-либо поддержки шифрования меня убило ещё больше. Для моих небольших задач это было слишком, и я стал допиливать свою.
Так вот, о чём я. Хаос в названиях авторов музыкальных композиция отразился на том, что в библиотеке WMP встречались до 10 различных написаний одного и того же автора, а почему-то выделить 10 папок и сказать "хочу, чтоб здесть был автор А" нельзя. Я знаю, что есть куча программ, которые могли бы мне помочь, но я решил заодно протестировать своё поделие.
До этого запуска я практически не тестировал библиотеку, за исключением открытия и чтения тегов одного единственного mp3.
На более 500 файлах не было ни единого разрыва! Точнее, были планируемые ошибки (с более-менее внятной ошибкой, что не встретился ID3, а вместо него какая-то фигня), например, когда тега в начале нет (или он в конце, или ещё где), я пока просто этого не доделал.
У меня больше не возникает плохого предчувствия, когда я запускаю код после длительного кодирования, я вылечился.
На каком ещё языке такое бывает?

Tags:

fix

  • Oct. 26th, 2009 at 10:47 PM
eye
Надо себе на стену повесить: всегда пиши ленивый паттерн в функции, которую планируется использовать в fix, всегда!
Второй раз уже натыкаюсь, а сейчас вообще пол часа на поиск «ошибки» угрохал.

Tags:

Мои извинения

  • Oct. 21st, 2009 at 1:34 AM
eye

Господа, я дико извиняюсь, что всё восвсплыло (planet.defun.ru), но почему же у вас на смену тегов так отреагировало? Посты-то не новые.

WTL + VS.Express + DEP

  • Oct. 21st, 2009 at 12:21 AM
eye
Данный пост оставлю себе на будущее, чтобы не дай боже ещё раз с этим сношаться.

Решился я поставить WTL на Express, но как известно, он завязан на ATL, коего в новом Platform SDK и не предвидется (а старый качать лень). Поэтому я отыскал готовый кусок из старого PSDK, внёс необходимые правки (которые можно узреть, например, здесь), и сохранил всё это в архив.

На этом, правда, ничего не кончилось, а только началось, я взял чужой проект и он у меня успешно падал с AV при запуске. Наивно полагая, что дело в разнице студий и, возможно, каких-либо настроек (которые я предварительно сломал, дабы собрать проект у себя), я стал рыться в коде. Но потом и пустой с нуля созданный проект тоже не заработал. Методом «исправляй по кускам, пока не заработает» (а у меня по крайней мере запустился пример из WTL), я таки нашёл злосчастную опцию: необходимо отключить DEP, т.е. выставить флаг /NXCOMPAT:NO в дополнительных настройках компоновщика. Я зол. И тем более я не понимаю, почему на двух других машинах всё работало, а у меня нет.

Tags:

Darcs `on` Windows

  • Oct. 11th, 2009 at 4:34 PM
eye
Вряд ли кто-то пытался поднять сервер darcs под windows, но мало ли, по крайней себе на будущее (если вдруг всё поправят).
На клиенте достаточно установить putty + сопутствующие утилиты.
На сервере: FreeSSHd (мануал), а также указать корневую директорию для sftp, внутри которой лежат репозитории. После этого SSH работает, darcs pull'ит и get'ит, но упорно не хочет push'ить с ошибкой:

D:\projects\some>darcs push voidex@voidex.org:some
Sat Oct 3 10:57:14 [_\cc_][_\ee_][_\f1_][_\ea_][_\ee_][_\e2_][_\f1_][_\ea_][_\e
e_][_\e5_] [_\e2_][_\f0_][_\e5_][_\ec_][_\ff_] ([_\eb_][_\e5_][_\f2_][_\ee_]) 20
09 voidex@voidex.org
* Initial revision
Shall I push this patch? (1/1) [ynWvplxdaqjk], or ? for help: y

darcs failed: can't set directory to 'some'
darcs: fd:3: hPutBuf: resource vanished (Broken pipe)

И чёрт его знает, что ему не нравится.

Tags:

HsColour: rgb colours

  • Oct. 2nd, 2009 at 6:35 PM
eye
Стандартный HsColour меня всем устраивал кроме поддержки цветов. Десяток стандартных цветов — слишком мало, хотя это плата за возможность раскрашивать код где угодно. Раньше я тупо добавлял свою функцию showHTML с заменой стандартных цветов на свои любимые при выводе в HTML. Сегодня решил допилить. Добавил — возможность указания любого цвета (при этом необходимо указать также и стандартный для тех форматов, которые любой цвет вывести не в состоянии), возможность указать foreground, background, размер шрифта (в px) и шрифт (font-family). При наличии последних в HTML вставляется div, дабы задний фон был не только под текстом. It works for me.
Нужно ли это кому-то, кроме меня? Наверняка я сделал что-то неправильно, где-то недосмотрел, возиться просто так не хочется, другое дело, если его потом на hackage заливать.

Ниже кусок кода, раскрашенный новым HsColour.

Tags:

eye

Парадокс двух конвертов формулируется так: вам даны два конверта, в одном из них денег в два раза меньше, чем в другом; вы выбираете один, смотрите содержимое, а потом решаете, взять ли другой конверт (не глядя) или оставить этот.
Интуитивно кажется, что разницы никакой (по крайней мере мне), с другой стороны, на мембране написано иначе, мол, если вы видите 10 рублей, то в другом конверте или 5, или 20, а следовательно при смене вы будете получать, в среднем 12.5. Шоке-шоке.
Далее пишут, что если вообще задаться любой величиной (например, 100 рублей), и менять конверт, если в открытом конверте сумма меньше, то вы также будете в выигрыше. Ну и третий способ: менять, если в открытом конверте сумма меньше, чем вы получили в прошлом розыгрыше.

Итого, три метода:

  1. Менять конверт всегда
     
  2. Менять, если сумма в конверте меньше N
     
  3. Менять, если сумма в конверте меньше, чем в предыдущем раунде
     

1-й вариант не работает, интуиция тут не подводит. Хотя исхода правда два (во втором или 5, или 20), но вероятность этого нам неизвестна. Если конверт меняется всегда, то очевидно, что это всё равно, что сразу выбрать другой конверт (ведь сама сумма нас не интересует). Симметричость этих вариантов очевидна.

Однако 2-й вариант может сработать! Предположим, что максимальная сумма в конвертах — M, тогда пары конвертов можно разбить на три группы:

  1. В обоих конвертах сумма меньше N — выигрыша не будет, так как всегда меняем, равноценно 1-му варианту
     
  2. В обоих конвертах сумма больше N — аналогично нет выигрыша
     
  3. В одном больше, в другом меньше. В этом случае, мы всегда будем брать конверт с большей суммой. В среднем без смены конверта мы бы получали 1.125N (минимальный конверт — 0.5 и 1, среднее 0.75, максимальный конверт — 1 и 2, среднее 1.5, так как сами конверты тоже равномерно распределены, то общее среднее — 1.125), а со сменой — 1.5N, выигрыш в 0.375N. Самих таких конвертов будет (2N-N)/M = N/M. Общий выигрыш за K игр = 0.375N * N/M * K.
    У меня в тесте получилось 3799273, почти 3750000.
     

Итак, в этом случае у нас неиллюзирный выигрыш, однако я немного соврал насчёт количества конвертов, если 2N меньше M, тогда их будет уже (M-N)/M=1-N/M, и выигрыш опять будет падать. Оптимально — N = M/2.

Если M заведомо большое (на например хоть раз нам выпало 500 рублей), можно взять небольшое N и получать стабильный профит.

Рассуждения проводились для равномерного распределения, для неравномерного будет уже всё иначе, но по крайней мере видно, что здесь нет никакого обмана.

3-й способ ещё лучше, так как он выбирает N на ходу, таким образом нас не волнует M, главное, чтоб оно было вообще.

Однако в исходной постановке задачи, когда распределение не оговаривается никак, ответа дать невозможно.
Имелось в виду, что в исходной постановке вроде ничего не говорится о вероятностях вообще, проводящий лотерею может класть суммы в определённом порядке. В этом случае я не знаю, есть ли тактика.
eye
По мотивам поста на Хабрахабре от скуки написал такую же штуку на Haskell'е. Просто, чтобы сравнить.
Я, правда, самой главной функции не написал, но подозреваю, что она на .Net завязана (если даже не на код автора), а разбираться мне было лень, да и суть статьи была в DSL.

Полотно кода )

Tags:

CPS

  • Aug. 21st, 2009 at 11:35 PM
eye

По мотивам поста[info]mr_aleph(reinventing CPS — functional pearl from my own soul) решил попробовать написать это на Haskell, если я, конечно, правильно понял задание :)

Для начала определим два типа:
data Continuation r = Continue (IO (Continuation r)) | Fail String | Done r
type Continuable a = forall r . ContT (Continuation r) IO a


Вычисление может либо вернуть ошибку, либо остановиться на «контрольной точке» (например, файл скачан). Соответственно если дальнейшие действия провалятся, вычисление продолжит работу с этой контрольной точки. Done здесь нужен для возврата окончательного значения.

Теперь две функции, одна прекращает вычисление с ошибкой, а вторая соответственно «коммитит» значение и продолжение:
done v = ContT $ \k -> return (Continue (k v))
stop s = ContT $ \_ -> return (Fail s)


Остаётся написать ещё две примитивные функции — одна для оборачивания ContT в наш тип, а вторая для запуска вычисления, они тоже простые:
routine :: ContT (Continuation r) IO r -> IO (Continuation r)
routine m = runContT m (return . Done)
type ContinuationState r = Either r (String, IO (Continuation r))
run :: IO (Continuation r) -> IO (ContinuationState r)
run k = do
  k' <- k
  case k' of
    (Done v) -> return $ Left v
    (Fail s) -> return $ Right (s, k)
    (Continue k) -> run k


Ну и последнее, перезапуск вычисления. По сути тот же run, но принимающий промежуточный результат:
retry :: (String -> IO ()) -> ContinuationState r -> IO (ContinuationState r)
retry _ (Left v) = return $ Left v
retry out (Right (s, k)) = out s >> run k


Тест:
getInput prompt = do
  liftIO $ putStrLn prompt
  s <- liftIO $ getLine
  if null s
    then stop "Empty input"
    else done s

bar = routine $ do
  x <- getInput "Input value 1:"
  y <- getInput "Input value 2:"
  z <- getInput "Input value 3:"
  return (x ++ " - " ++ y ++ " - " ++ z)


Теперь если на второй запрос ввести пустую строку, вычисление остановится, после retry продолжит сразу со второго запроса.
Ну и forkIO туда впихнуть по желанию.

Tags:

Let's Yota!

  • Aug. 2nd, 2009 at 6:40 PM
eye
С подключением были очень неприятные проблемы, но они разрешились. А теперь хвалить буду:
Сегодня, придя домой, обнаружил, что сеть не работает (местный провайдер в Красногорске). Не меньшим было удивление, что теперь Yota ловит и внутри квартиры, хотя буквально неделю назад поймать подключение можно было только возле окна (модем, правда, ловил внутри, но HTC — нет).
Наличие хотя бы не очень качественного интернета куда лучше его отсутствия, так что проклинать местного провайдера я буду явно реже.

Пост, разумеется, написан с Йотовским подключением.

Data.Binary monad transformers

  • Jul. 29th, 2009 at 11:06 PM
eye

В классе Data.Binary есть две функции — put и get. Что делать, если в процессе чтения я захочу иметь возможность вернуть ошибку (Exceptional) и/или собрать какую-то информацию (Writer)? На помощь придут monad transformers, но тип полученного get'а будет ExceptionalT ex (WriterT info Get) value, который уже нельзя использовать в классе. Если я вообще захочу IO, то мне нужен будет не Get (который instance Monad), а GetT (instance MonadTrans). Тогда класс можно будет поменять на такой:
class Binary m a where
    put :: a -> PutT m ()
    get :: GetT m a

И тогда можно будет
instance Binary (ExceptionalT String (Writer [String])) MyData where
и все будут счастливы, кроме того, что будет лишний геморрой при стыковке разнотипных get/put'ов, но это можно решить.

Лирическое отступление
Однако таким образом мы можем написать десяток instance'ов для разных монад и тут меня гложет ощущение излишнего копипаста. По сути код сериализации будет тот же, отличие будет только в том, что ещё побочного мы делаем по пути. Как вообще это решают гуру? Т.е. есть у меня, к примеру, код загрузки данных из файла строки, и вдруг я захотел по пути собирать инфу о каждом прочитанном числе, неужто только копипаст и допиливание?
Конец лирического отступления

Дабы задача не была абстрактной, приведу пример, на котором вопрос поднялся. Я разбираю бинарный пакет, в котором есть список данных с разными тагами. Некоторые таги я знаю, а некоторые могу и не знать. Те, которые я не знаю, я хочу каким-то образом пропихнуть наверх, чтобы потом залоггировать. Варианта я вижу два:
1. возвращать тупл из прочитанных данных и списка кривых тагов — собственно, стейт ручками, который потом ещё и протаскивать придётся, но реализуется на текущем бинари и без ебли мозга
2. monad transformers

Есть ли ещё варианты?

Tags:

Let's Yota!

  • Jul. 25th, 2009 at 5:14 PM
eye
Три простых шага:
1. Войдите в личный кабинет — 6 часов ожидания, когда это станет возможным
2. Положите на счёт деньги — 3 часа ожидания получения денег, два звонка в поддержку QIWI
3. Активируйте устройство — подождите, пока система сама поймёт, что у вас и правда 1000р. на счету и перестанет говорить "пополните счёт", время тикает, уже 4.5 часа.

Это инфернальный пиздец, на каждом, на каждом, блять, маленьком этапе поджидает какая-нибудь еботня. Старались, наверное, сделать как можно ебливее.

fix

  • Jul. 7th, 2009 at 1:50 AM
eye
Ковырялся тут с fix, написал такую штуку:
fib self 0 = 1
fib self 1 = 1
fib self n = self (n - 2) + self (n - 1)

fixMemo f = let x = f (map x [0..] !!) in x

fixMemo fib 10000 отрабатывает почти мгновенно, в отличие от.

Других открытий, правда, не сделал, а они есть?

Tags:

ICFPC 2009

  • Jun. 30th, 2009 at 3:47 AM
eye

Закончился ICFPC, отчётов я писать не умею, поэтому лишь отмечу некоторые моменты, какие ошибки были сделаны и какой из всего этого я получил опыт.

Суть заданий описана у [info]_adept_(Таких не берут в космонавты!), так что повторяться не вижу смысла.

О контесте я узнал за два дня до его начала, так что о команде речи не шло. И хотя удалось собраться втроём ( [info]mr_aleph, [info]nealar), задание им не понравилось, так что команда распалась, не собравшись. Позже [info]nealar  приходил и хотел помочь, но я к тому времени уже наваял код, который не соберётся без соответствующих библиотек.

После чтения спецификации я занялся реализацией VM копанием в своём стороннем коде и вообще посторонними вещами. Сыграло мнение других участников, что задание не ахти. Однако через 5-6 часов, доделав дела, я начал писать VM. Считаю, что правильно сделал, выбрав C++ для этой задачи, так как кода было мало (284 строки на VM и "визуализатор"), а работало всё сразу и достаточно быстро, так что я к нему больше и не возвращался.
Протестировав работу, написал биндинги к Haskell, дабы основное решение писать уже там.
Почитав википедию на предмет предложенного варианта решения, я написал "в лоб" решения первых 4-х задач, а затем вторых 3-х. Вторая задача (из второго блока) не поддавалась — результирующая орбита была очень далеко, так что я попадал в цель, но недостаточно близко.
Было утро субботы, а я не спал уже с 20 часов четверга, но полученные быстрые очки дали уверенность, что "вот ещё чуть-чуть" и сделаю почти всё. Хрен! Третьи задачи были сложнее, и реализовывать их тем же методом, что и первые, было уже нереально. Очевидно, что управление спутником в данных задачах — state-машина. И вот тут я сделал большую ошибку. Вместо того, чтобы использовать yield (продолжения, или монаду Cont), меня чёрт дёрнул использовать игрушку, с которой я игрался на днях — акторы а-ля Erlang c selective receive и прочими свистоперделками. И беда тут была вовсе не в том, что это сделанная на коленке реализация (как выяснилось, как раз в ней багов не было), а в том, что опыта её использования у меня нет никакого. С другой стороны, этот опыт я получил :)
Переписанная на акторы реализация давала возможность в обработчике получать сообщения от VM и переключать состояния направо и налево, что, собственно, умеют и продолжения. Написав решение для первой задачи в новом стиле, я вдруг обнаружил, что иногда спутник не реагирует на мои команды. Точнее, он либо реагирует раньше, либо позже, либо завершает всю обработку в самом начале полёта. Повтыкав liftIO $ putStrLn "I am here" и прочего, я вдруг заметил, что спутник стал слушаться. И тут я сделал вторую фатальную ошибку, которая отняла у меня всю субботу и часть ночи воскресенья. Я грешил на ленивость, так как уже сталкивался с чем-то подобным (разумеется, виноват был я, а не компилятор). Если бы я был выспавшимся и с незамутнённым сознанием, то сел бы и подумал над своим решением, а тут я стал вставлять разные логирующие выводы и искал баг совершенно не там — в библиотеке акторов, которая как раз работала прекрасно.
Как выяснилось к утру воскресенья, все проблемы возникали от того, что я никак не синхронизировал прогон VM и обработку каждого шага своей функцией. В итоге в виртуальной машине уже могло быть сделано 10000 шагов, тогда как обработчик работает над сообщением из 1000-го шага, но при этом располагает актуальными данными! Т.е. в обработчике шаг был тысячный, а данные он получал уже с десятитысячного. После этого, исправив код вставкой везде, где можно, syncHost, посыпав голову пеплом, я таки отправился спать в районе 4 часов утра. Можете посчитать, сколько я не спал в сумме.
Полноценно вернуться я смог уже только к утру понедельника, проспав более суток. Здесь мне надо было бы сходить на разведку и узнать, что есть книга Orbital Mechanics: For Engineering Students, где подробно расписаны все варианты манёвров на любой вкус, чего было бы достаточно с головой. Но я сел и стал изобретать собственные методы, в последствии узнавая их реальные названия :) Плюс зачем-то стал писать всякие "подруливатели", которые могли держаться на орбите в пределах 1-2 метров, или догонять впереди летящий спутник с высокой точностью. Толку от них почти никакого, а времени я опять же потратил.
Дописав различные манёвры, посжирав топливо в первом задании, я опять ушёл в отключку.
Проснулся за 2 часа до конца и реализовал наконец-таки вторую задачу из второго бинарника. Принялся за третью, и к самому окончанию успел попасть в пределах двух км. Казалось бы, ещё чуть-чуть, и сделал бы одну-две (а то и все) задачи из третьего файла, но ещё чуть-чуть уже не было.

В общем и целом, мне понравилось, но я, можно сказать, слил. Жаль, по своей же глупости.

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

Суммарный объём кода:
Haskell: 1347 строк
C++: 284 строки

Код: http://voidex.org/icfpc/svn/trunk/
В архиве: http://voidex.org/icfpc/2009/voidex_icfpc_2009.zip

ping pong

  • Jun. 22nd, 2009 at 2:46 AM
eye
Это, конечно, не Эрланг, но меня устроит.

ping 0 _ = return ()
ping n pongID = do
s <- self
send pongID "ping"
receive (liftIO . putStrLn)
ping (n - 1) pongID

pong = do
receive $ \(pingID, s) -> do
liftIO $ putStrLn s
send pingID "pong"
pong

pingPong = runProcess $ do
ponger <- spawn pong
pinger <- spawn $ ping 10 ponger
return ()

Tags:

A Gentle Introduction to Category Theory

  • May. 20th, 2009 at 5:43 AM
eye

Читаю subj и наткнулся на упражнение:
"Exercise: define a category where the morphisms are numbers, and the composition is addition."

Числа можно складывать любые, т.е. f : A  B и g : C  D всегда можно сложить, причём как g.f так и f.g, из чего заключаю, что A == D и B == C, т.е. f : A  B и g : B  A, а любая их композиция — id. Ах да, id тоже можно со всем сложить, а потому и A == B.
Что я делаю не так?

Zipper

  • May. 14th, 2009 at 7:04 AM
eye

Под впечатлением от прочитанного написал на хабре статью.
Нутром чую, что всё это всем общеизвестная тривиальщина (т.е. это явно самые основы теории), но меня сама возможность брать производные от типов потрясла. А посему прошу совета, что и где можно/нужно почитать по подобной тематике, чтоб впечатлиться не меньше?