tag:blogger.com,1999:blog-36280512701858017522024-03-12T19:46:33.683-07:00Случайные заметкиБлог продолжающего linux-оида.Portnovhttp://www.blogger.com/profile/07229550690332111129noreply@blogger.comBlogger82125tag:blogger.com,1999:blog-3628051270185801752.post-2019112052471330542018-07-28T12:32:00.002-07:002018-07-28T22:57:22.054-07:00ICFPC 2018<div dir="ltr" style="text-align: left;" trbidi="on">
В этот раз мы писали программы для какого-то фантастического
3D-принтера, в котором печать производят летающие наноботы. Боты имеют
какую-то систему команд, могут перемещаться по полю и превращать материю
в энергию и обратно. Кроме того, присутствует некое
"подпространственное поле", за счёт которого напечатанные куски модели
могут висеть в воздухе, пока поле включено. Боты могут размножаться
делением и потом опять соединяться. Каждая команда ботам стоит
сколько-то энергии, и включение "антигравитации" тоже сколько-то стоит.
Дан набор моделей, которые надо напечатать, надо написать
соответствующие программы для ботов, тратящие как можно меньше энергии.<br />
<br />
Задачи для ICFPC придумывают вроде бы люди из университетов, но при
этом, похоже, эти люди имеют какой-то опыт промышленной разработки, так
что процесс решения задач похож на "настоящее" программирование гораздо
больше, чем, например, те же ACP ICPC. Начать с того, что сам процесс
получения входных данных и формирования выходных каждый раз представляет
собой целую небольшую задачу - то по REST надо разговаривать, то
длинные рациональные числа парсить. В этот раз протокол был полностью
бинарным: в командах координаты кодировались даже не байтами, а
отдельными битами. Модели тоже были закодированы не совсем тривиально:
по одному биту на воксель. Вероятно, как раз бинарный протокол стал
причиной того, что в этот раз в контесте принимало участие гораздо
меньше команд, чем в прошлые разы (всего 95 команд; в прошлом году, для
сравнения, мы были 102-ми из 120).<br />
<br />
Другой момент, делающий ICFPC похожим на "реальную жизнь" - не
ограниченный размер команды. При ограниченном сроке (трое суток),
очевидно, организовать эффективную работу большой команды над сложной
задачей - целая большая управленческая проблема. Если у вас есть где-то
чудо-ПМ, вы, теоретически, можете взять команду из 100 условных индусов и
всех победить. Или вы можете взять двух-трёх хороших разработчиков без
всякого ПМ-а и тоже всех победить. То есть, как и в реальных задачах,
это соревнование не только по программированию, но и по управлению
проектами.<br />
<br />
И ещё один момент: условия задачи меняются и усложняются прямо во
время разработки. Как знакомо. В этот раз, правда, усложнение было всего
одно, зато какое... Так часто бывает на ICFPC: когда впервые читаешь
задачу, появляется ощущение, что эту задачу решить нельзя никак,
никакими силами и ни за какое время. Потом ты начинаешь что-то пилить,
разбираться, у тебя начинает что-то получаться... А потом приходит
усложнение задачи раз в пять.<br />
Писали в этот раз опять на Haskell.<br />
<br />
<h2 id="пятница">
Пятница</h2>
Эту часть контеста Minoru охарактеризовал так:<br />
<blockquote class="tr_bq">
<pre><code>1. Никто ничо не понял, но Портнов уже пилит базовые типы и тулинг.</code></pre>
</blockquote>
С прошлых лет у меня так и осталось ощущение, что очень важно
побыстрее выдать хоть что-то. Не для того, чтобы набрать очки (правила
подсчёта очков в ICFPC обычно такие, что одним только кодингом на
скорость ничего не добьёшься). А для того, чтобы у команды появилось
ощущение "ну, что-то вроде можем, может быть эту задачу даже можно
решить". Пока такого ощущения не появится, все находятся в некоторой
фрустрации: "руки опускаются". А это просто потеря времени.<br />
А для того, чтобы выдать хоть какой-то самый тривиальный алгоритм,
надо написать некоторое количество обвязки: разбор файлов итд. Поэтому я
нагуглил пакет <a href="http://hackage.haskell.org/package/bits">bits</a>
и стал реализовывать кодирование команд ботам в заданный бинарный
протокол. В чате в это время обсуждали общие подходы и усолвие задачи. А
потом я пошёл спать.<br />
<br />
<h2 id="суббота">
Суббота</h2>
Краткое содержание от Minoru:<br />
<blockquote class="tr_bq">
<pre><code>2. Появились какие-то базовые идеи, народ осваивает запиленный Портновым
тулинг.
3. Идеи рождаются быстрее, чем пишется код, это всё выпрыскивается в чат (в
этом году ещё и в трекер).</code></pre>
</blockquote>
На самом деле, я ещё продолжал пилить обвязку. Дело в том, что в
данной задаче, когда ботов больше одного, команды для разных ботов
должны идти вперемешку. Точнее, исполнитель на каждом шаге смотрит,
сколько сейчас живых ботов, читает из программы N команд и раздаёт их
ботам. С учётом того, что алгоритм должен выдавать команды ботам
согласованно, целую небольшую задачку составляет формирование общей
программы из программ для отдельных ботов, с учётом их переменного
количества. А ещё я сделал чтение файлов моделей при помощи библиотеки <a href="https://hackage.haskell.org/package/bitwise">bitwise</a>.<br />
Потом я взялся писать "простой алгоритм", который включает
антигравитацию и начинает печатать модель слой за слоем одним ботом. При
этом мне приходилось по дороге допиливать обвязку.<br />
Изначально у меня была мысль, что будет две отдельных сущности:
Generator - набор API для формирования последовательности команд, и
какой-нибудь Emulator - реализация описанной в спецификации модели, с
подсчётом энергии, учётом закрашенных вокселей и т.д. И потом мы бы
как-нибудь с использованием генератора создавали какие-нибудь программы,
и сравнивали их при помощи эмулятора. Но потом постепенно как-то так
получилось, что для написания хоть сколько-то полезных алгоритмов
генератору на каждом шаге нужно (по крайней мере частично) знать
состояние мира, в которое приводит сгенерированная последовательность
команд. А эмулятор у нас так и не родился...<br />
К вечеру самый простой алгоритм был готов, и мы даже надеялись что-то выдать в раммках lightning division, но не успели.<br />
<br />
Потом я стал оптимизировать исходный тупой алгоритм. Идея была в том,
чтобы выключать "антигравитацию", когда она не нужна. Только вот для
этого в момент построения программы надо понимать, какие из закрашенных
на данный момент вокселей "заземлены" (т.е. опираются на что-нибудь), а
какие "висят в воздухе". Пришлось добавлять отслеживание этого в
генератор, да и после мы с этой частью логики ещё долго возились.<br />
<blockquote class="tr_bq">
<pre><code>4. Выходит апдейт условий, все получают скачок адреналина, возможен регресс
до второй фазы с целью перепилить тулинг. (Эта фаза может наступать
несколько раз в зависимости от апдейта).</code></pre>
</blockquote>
Теперь:<br />
<ul>
<li>можно не только превращать энергию в материю, но и наоборот</li>
<li>несколько ботов могут взаимодействовать и закрашивать за один ход
большие области: линию, прямоугольник или параллелипипед из вокселей.</li>
<li>появились новые задачи, в которых надо не собрать модель, а разобрать</li>
<li>появились ещё задачи, в которых надо разобрать одну модель и собрать другую.</li>
</ul>
Для задач по разборке сразу появилась идея, что можно составить
программу на сборку, а потом по определённым правилам "инвертировать"
её. Да вот только до реализации этой идеи мы так и не дошли. Разборку мы делали так же, как сборку, просто с другими командами. А пересборку реализовали в лоб: сначала разобрать одно до нуля, а потом собрать другое.<br />
<br />
Уже почти ночью мы всё-таки напоролись на "мифические" проблмы с
производительностью из-за иммутабельности и ленивости (мифические,
потому что по моему опыту на них наталкиваешься довольно редко). Решили
кардинально, избавившись в этих местах и от ленивости, и от
иммутабельности.<br />
Ещё мы успели что-то залить, и оказались аж на 12м месте в
лидерборде. Ну, просто потому, что только 15 команд к данному моменту
успели хоть что-то выдать.<br />
<br />
<h2 id="воскресенье">
Воскресенье</h2>
Никакого особо умного алгоритма мы так и не придумали, поэтому
продолжали оптимизировать тупой. В частности, я научился пользоваться
двумя ботами и закрашивать весь слой серией команд "закрасить линию", в
тех случаях, когда это достаточно просто, а именно - когда слой состоит
из серии достаточно коротких линий. И ещё при закрашивании каждого
следующего слоя научился менять направление, чтобы ботами туда-сюда
меньше впустую ездить.<br />
Для возможности хоть как-то сравнивать разные решения задач я написал
простой Evaluator, который оценивает количество энергии, потребляемое
программой. При этом этот evaluator не отслеживает состояние модели, не
отслеживает положение ботов, не выполняет никаких проверок...<br />
Полноценного эмулятора у нас так и не было, поэтому оценить, насколько правильно решаются <i>все</i>
задачи, мы не могли - только глазами проверить некоторые выборочно.
Поэтому перед сном мы залили версию, которая на некоторых задачах нам
дала существенный прирост, зато на всех остальных - нули, т.к. в
программах были ошибки. Но узнали об этом мы только в понедельник, т.к.
лидерборд обновлялся редко.<br />
<br />
<h2 id="понедельник">
Понедельник</h2>
В понедельник остались только мы с Minoru. Товарищи Akon32 и ForNeVer
оставили нам по длинному описанию начатых, но не законченных идей в
трекере.<br />
С утра, увидев что мы сползли на 67е место из 78, я стал разбираться с ошибками, и к обеду залил исправленную версию.<br />
Minoru начал писать скрипт, который бы с использованием evaluator
выбрал лучший вариант решения для каждой задачи, из тех что мы
отправляли за весь контест.<br />
С какими-то ещё небольшими оптимизациями, мы поднялись на 48е место из 95. Не очень плохо, но бывало лучше...<br />
А вскоре после обеда, лайвборд заморозили, и до вечера мы что-то ещё
понемногу оптимизировали уже "вслепую": эмулятора-то не было, и мы до
сих пор не знаем, чего в этих сабмишшенах было больше: оптимизаций или
ошибок, и куда мы в итоге попадём. Примерно с равной вероятностью мы
можем оказаться на 20м и на 80м месте.<br />
<blockquote class="tr_bq">
<pre><code>5. В смысле конец контеста, у меня же ещё вот это, это и это не реализовано!</code></pre>
</blockquote>
<h2 id="в-общем">
В общем</h2>
Некоторое количество замечаний в духе Капитана Очевидность:<br />
<ul>
<li>Надо лучше организовывать общение в команде. В частности, лучше
документировать код, наверное. А то я несколько раз объянял товарищам,
как работает мой Generator. И пару раз выяснялось, что два человека
параллельно делают одно и то же.</li>
<li>Не надо экономить на инфраструктуре. Если понятно, что вот это можно
вынести в утилитную функцию, вот здесь для ясности ввести type synonym,
то это надо делать, хотя это занимает время. При работе в одиночку, или
даже в команде в том случае, когда ты один пишешь большой кусок кода,
иногда имеет смысл на первом этапе сэконоить: написать побыстрее
некоторое количество тупого кода, с копипастой и прочим, убедиться в его
работоспособности, а потом отрефакторить и убрать копипасту. В случае
тесной командной работы так делать не надо: инфраструктурный код должен
сразу иметь простое, "красивое" api.</li>
<li>Если какая-нибудь часть инфраструктурного кода очевидно нужна
(например, эмулятор, или pathfinding, или визуализатор), то её очевидно
нужно писать, а не "да ладно, давайте как-нибудь так".</li>
<li>Вся команда должна хорошо владеть используемым языком и его
библиотеками. У нас же чрезвычайно разношёрстная команда: когда мы после
контеста решили выяснить пересечение множеств умеемых каждым из нас
языков, оказалось, что это пересечение пусто.</li>
</ul>
</div>
Portnovhttp://www.blogger.com/profile/07229550690332111129noreply@blogger.com1tag:blogger.com,1999:blog-3628051270185801752.post-81703029956921226572016-08-08T12:49:00.000-07:002016-08-08T12:50:59.377-07:00Как можно участвовать в ICFPC (2016)<div dir="ltr" style="text-align: left;" trbidi="on">
<div style="text-align: right;">
<i>«Рядом с этой сокровищницей мысли, — неторопливо думал Васисуалий, — делаешься чище, как-то духовно растешь»</i></div>
<br />
В этом году мы с товарищами (ForNever, Minoru, Hagane, grouzen) опять
взялись участвовать в ICFPC. Если кто не в курсе, что это такое — см <a href="http://iportnov.blogspot.ru/2015/08/icfpc-icfpc-2015.html">мой предыдущий пост</a> или <a href="http://users.livejournal.com/-adept-/tag/icfpc">серию постов товарища adept</a>.<br />
В этот раз для разнообразия мы не писали AI для игрушки. Организаторы
были из Японии, и поэтому мы весь уик-енд... складывали оригами. Кто-то
из кода, а кто-то нет.<br />
Задача состояла в том, чтобы из квадратного «листа бумаги» складывать
разные (плоские) фигуры. В качестве задачи даётся описание нужной
фигуры, а решение — набор складок на квадрате и описание, какие вершины в
какие переходят при складывании. Первая сотня фигур была предложена
организаторами, а дальше задачи могли отправлять участники, и за
отправку задач тоже давали очки.<br />
При этом, на самом деле, это было «не настоящее оригами». Были
допустимы решения, невозможные с бумагой — части листа могли проходить
друг через друга при складывании. При отправке решений чужих задач можно
было даже «рвать бумагу» (при отправке своих задач — нельзя). Момент
про разрешено ли разрезание мы уточняли долго и сложно.<br />
Контест в этот раз проходил с утра пятницы по вечер воскресенья в моём часовом поясе.<br />
<h2 id="в-пятницу">
В пятницу</h2>
с утра я «по-быстрому» написал скачивалку задач с сервера и
закачивалку решений (на python, просто потому что там было REST API, а у
меня уже был опыт использования для этого питоньей библиотеки
requests). Ещё я написал парсер предложенного формата задач и
рендерилку, которая рисует фигуры из задач в виде svg (на Haskell).
Сразу пригодился хаскельный модуль Data.Ratio. Дело в том, что в задачах
использовались координаты в виде рациональных чисел, причём размеры
числителя и знаменателя не были ограничены. Так что быстро образовалась
строчка type Number = Ratio Integer.<br />
Потом я посмотрел на результаты рендера и увидел, что первые семь
задач тривиальные (там просто квадрат, сдвинутый или повёрнутый, но не
сложенный), решения для них легко написать вручную. Решил и залил.<br />
Потом мы начали думать над задачей (я уже писал, у меня всегда так — сначала код, потом думать).<br />
Когда нет ограничения на разрезание бумаги, задача сводится к тому,
чтобы разрезать квадрат на эн многоугольников и сложить из них нужную
фигуру. Насколько я знаю, подобные темы широко и глубоко изучены, но я
не знаком с конкретными работами и алгоритмами. (в университете был даже
какой-то спецкурс по оригами, как разделу геометрии, но я на него не
попал). Поэтому я долго и упорно думал в эту сторону, но ничего не
придумал.<br />
В чате довольно быстро образовалось несколько идей:<br />
<ul>
<li>Разрезать требуемую фигуру на треугольники и попытаться из таких треугольников сложить квадрат.</li>
<li>Какой-нибудь вариант генетических алгоритмов: разрезаем квадрат как
попало и складываем как попало, оцениваем скор в соответствии с
правилами организаторов.</li>
<li>Быстро придумался простой алгоритм для складывания любого достаточно
маленького выпуклого многоугольника: пройти по всем его рёбрам и
сложить бумагу вдоль них. Если останутся торчащие «хвосты», то опять
пройти по рёбрам и подвернуть вдоль них.</li>
<li>Кроме силуэта фигуры, в задачах приводился её «скелет» — набор всех
рёбер в положении после складывания. При этом, правда, было указание,
что это только подсказка, складывать в соответствии с этим скелетом не
обязательно. Так вот, появилась идея брать этот скелет и пытаться
разворачивать его до квадрата.</li>
</ul>
Товарищ grouzen произвёл обширные изыскания существующих научных
работ на тему оригами и около, и обеспечил нас пейперами для чтения на
всю неделю. К сожалению, применить их мы не смогли.<br />
Потом начался как всегда разброд и шатание, каждый стал заниматься чем-то своим.<br />
Я решил начать с написания какого-никакого «фреймворка» для
дальнейшей работы — набора функций для разрезания многоугольников,
объединения и т.п. Некоторые из геометрических алгоритмов тривиальны
(отражение многоугольника относительно прямой), зато другие
зубодробительны (объединение произвольных многоугольников). Для таких
алгоритмов я стал гуглить существующие реализации, по возможности на
Haskell (т.к. парсер задач уже был на Haskell). Нашёл только <a href="https://hackage.haskell.org/package/clipper">clipper</a>.
Это биндинги к одноимённой библиотеке на C++. Сразу обнаружилась
особенность: Clipper работает с целочисленными координатами (видимо, в
основном заточен на экранную графику) размером Int64. А у нас
рациональные и просто целые очень большие числа. Так что при попытке
засунуть в clipper одну из первых тривиальных задач сразу получили:
Literal 1267650600228229401496703205376 is out of the Int64 range
-9223372036854775808..9223372036854775807. Так что реализацию сложных
алгоритмов отложили на потом (по факту объединение многоугольников у нас
в итоге и не использовалось). Minoru взялся писать разрезание
многоугольника прямой на два (это в любом случае бы понадобилось для
складывания, а в clipper этого нет).<br />
Дальше Minoru, Fornever и hagane начали обсуждать вариант с
разворачиванием скелета. Я в эту идею особо не вникал, т.к. думал над
представлением «фигуры» в процессе её сворачивания и реализацией
«сворачивалки» в виде State ЧтоТо ().<br />
К вечеру пятницы у меня был «солвер», который брал многоугольник и
один раз сворачивал квадрат вдоль каждой грани этого многоугольника. И
рисовал в svg что получилось. То есть он теоретически мог уже решать
часть задач. Но при этом он принимал данные не в том виде, и выводил не в
том. При этом там было ещё куча неуловленных багов.<br />
Fornever к этому времени перегенерировал все иллюстрации к задачам, устранив баг в рендерилке.<br />
<h2 id="в-субботу">
В субботу</h2>
с утра я стал было дописывать алгоритм сворачивания выпуклых
полигонов, но наткнулся на баги в методе разрезания многоугольника
(где-то какое-то деление на ноль). Попытался разобраться в коде этого
метода и не смог. Т.к. Minoru сильно в другом часовом поясе, я решился
переписать этот код. Стал гуглить как это делается правильно, и нашёл
зубодробительные алгоритмы разрезания произвольных многоугольников.
После долгих попыток в них разобраться я внезапно понял, что нам нужно
разрезать только выпуклые многоугольники (т.к. многоугольники, на
которые бумагу могут разделить складки оригами, могут быть только
выпуклыми), и быстро написал сравнительно простой метод разрезания
выпуклых многоугольников.<br />
Потом я стал писать форматирование решения в соответствии с форматом,
прниимаемым сервером. В это время Fornever с Hagene что-то писали на
тему «разворачивания скелетов». Hagane ещё и постил задачи для других
участников, придуманные «вручную». Потом, чтобы не тратить клетчатую
бумагу, Hagane c Fornever стали писать генератор случайных задач.<br />
grouzen предложил написать графическое интерактивное приложение для
разворачивания фигур вручную, но до реализации этой идеи как-то ни у
кого не дошли руки.<br />
К вечеру субботы у меня был солвер, который решал некоторую часть
задач, и я его пачками натравливал на избранные простые задачи. На
многих задачах при этом проявлялся баг в геометрических алгоритмах. Тут
же написалась решалка тривиальных задач (которые просто квадрат без
складываний). Ещё я написал вариант солвера, который складывает вместо
требуемой фигуры её выпуклую оболочку — это, очевидно, даёт приближённые
решения, но лучше чем ничего.<br />
Потом оказалось, что у нас имеется три реализации геометрии — Minoru и
моя на Haskell, Hagane на Racket и Fornever на F#; и две реализации
форматирования вывода, обе на Haskell — моя и Hagane, отличающиеся
по-моему только названиями функций. На этом месте я взвыл в чатик — мол,
нет на нас ПМ-а или хотя бы тимлида.<br />
До позднего вечера я искал баги в реализации геометрии, Fornever с
Hagane писали генератор задач, а Minoru писал разворачивание скелетов.<br />
Потом Hagane решил взять на себя функции лида и отправил меня спать.
Правда, совсем перед тем как лечь, я нашёл баг в одной строчке (опечатка
при копипасте) в реализации разрезания многоугольника, и поправил его,
отчего все «волшебные» баги исчезли.<br />
<h2 id="в-воскресенье">
В воскресенье</h2>
с утра у меня образовалось несколько идей:<br />
<ul>
<li>сделать параллельный перенос исходного квадрата к месту, где
находится искомая фигура — до этого момента все складывания выполнялись
«на месте», на единичном квадрате, так что случаи, когда фигура
находится где-то сбоку, не обрабатывались.</li>
<li>придумать какие-нибудь эвристики, как определить, на какой угол
нужно повернуть квадрат перед складыванием — т.к. фигуру «единичный
квадрат повёрнутый на 45 градусов», очевидно, нельзя получить из
единичного квадрата одними складываниями без поворотов.</li>
<li>доделать складывание выпуклых фигур в солвере. К этому моменту у
меня делалось всегда 2 итерации обхода многоугольника со складыванием
вдоль рёбер, что было избыточно для одних задач и недостаточно для
других. Надо было написать проверку — надо ли делать ещё обход.</li>
<li>появилась идея по складыванию невыпуклых фигур. Невыпуклый
многоугольник с одной невыпуклой вершиной часто можно получить
перегибанием выпуклого многоугольника. Поэтому можно поискать способы
«развернуть» такие многоугольники в выпуклые, а выпуклые мы складывать
уже умеем. Для более хитрых многоугольников можно процедуру
разворачивания повторить несколько раз.</li>
</ul>
Я нарешал скриптом с исправленным солвером пару сотен простых задач и
стал реализовывать перечисленные идеи. В итоге где-то к середине дня я
реализовал перенос и для самых простых случаев поворот, доделал
сворачивание выпуклых фигур, и оставалось только время от времени
запускать скачивалку и решалку (ту, которая складывает выпуклую оболочку
для всего подряд). Ещё я оставшееся время пробовал всякие улучшения и
оптимизации.<br />
В частности, в какой-то момент я вдруг решил попробовать сделать хоть
какой-нибудь вариант генетического алгоритма для решения сложных задач.
Для этого нужно иметь функцию оценки, а она в данном случае
определяется через площади многоугольников. Какое-то время я боролся с
функцией нахождения площади из clipper, но то ли я его не так готовил,
то ли есть баги в биндингах, то ли в самом clipper — результаты оценки
получались настолько неадекватными, что я на это плюнул. Писать
собственный расчёт площади времени не осталось.<br />
У моего солвера оставалась существенная проблема: он часто
генерировал решения размером в десятки или сотни килобайт, при
ограничении в 5000 байт. Вечером стали думать, как уменьшить размер
решений. В итоге сделал:<br />
<ul>
<li>если многоугольник не выпуклый и мы всё равно будем делать
приблизительное решение — округлить входные координаты до 10-12 знаков
после запятой (уменьшает размер дробей на выходе);</li>
<li>складывать не по всем подряд рёбрам многоугольника, а сначала только
по чётным, потом только по нечётным. Не очень понимаю почему, но на
некоторых задачах это помогло существенно.</li>
</ul>
Вечером Minoru стал пытаться решать сложную задачу номер 101. Там
нужно было сложить классического журавлика, и он его стал складывать. Из
бумаги. С целью потом перевести эти складки в цифры. Правда, в итоге
безуспешно. Но эту задачу вообще решила только одна команда.<br />
Ночью я передал Minoru скрипты для скачивания задач, решения и
заливки решений, оставил его их запускать в оставшееся время контеста, и
ушёл спать. Leaderboard к этому времени как раз заморозили.<br />
<h2 id="в-итоге">
В итоге</h2>
к моменту заморозки leaderboard мы оказались на 44м месте (из 293
команд всего, получивших ненулевое количество очков 201). Что есть
необычно высокий результат по сравнению с нашими прошлыми попытками.<br />
По моему личному впечатлению, в этот раз задача была заметно проще,
чем в предыдущие (я не удивлюсь, если увижу разочарованные отзывы об
этом icfpc от «аксакалов» — мол, слишком легко). Но, возможно, это такой
perception bias из-за того, что в классической аналитической геометрии с
линейной алгеброй худо-бедно разбираюсь, а во всяких AI почти никак не
разбираюсь.<br />
У нас, как всегда, были проблемы с организацией и коммуникацией
(каждый писал что-то своё, иногда дублируя работу). По-моему, если бы мы
физически находились в одном месте, результаты могли бы быть сильно
лучше, хотя бы за счёт взаимной мотивации (говорят, психологически
сложнее отлынивать, когда видишь как человек за соседним компом
сосредоточенно пишет код) :) Как и в прошлые разы, были проблемы из-за
того, что члены команды имели сильно разный опыт работы с выбранным
языком.<br />
Надеюсь, в следующем году мы как-нибудь сможем смягчить эти проблемы, и результат будет ещё лучше.</div>
Portnovhttp://www.blogger.com/profile/07229550690332111129noreply@blogger.com0tag:blogger.com,1999:blog-3628051270185801752.post-80709137498959323172015-08-12T12:36:00.001-07:002015-08-12T12:36:55.105-07:00Как не надо участвовать в ICFPC (типа отчёт об ICFPC-2015)<div dir="ltr" style="text-align: left;" trbidi="on">
Мы с несколькими товарищами (широко известными в узких кругах -
ForNeVer, rexim, Minoru) уже который год подряд пытаемся участвовать в
ICFPC. Если кто не в курсе, это такой своеобразный ежегодный контест по
программированию, приуроченный к international conference for funcional
programming (ICFP). Несмотря на название, программировать можно не
только на функциональных языках, а на чём бог на душу положит. Более
того, во многих случаях код организаторам передавать необязательно -
контест организован так, что нужно отослать ответы на какие-то задачи.
Т.е. теоретически можно вообще не программировать, а решить задачи на
бумажке и отослать ответы. Просто очень сложно. Контест длится трое
суток — в моём часовом поясе это от вечера пятницы до вечера
понедельника. Темы заданий выбираются в целом довольно разнообразные, но
последние годы заметен уклон в сторону всяческих AI. Например, вот
задачи тех контестов, в которых я участвовал:<br />
<ul>
<li>2014 - надо было написать AI для игры a la Pacman. Даны карты, а
надо прислать программы на ассемблере выдуманных процессоров, играющие
за основного персонажа и за призраков, набирая побольше очков.</li>
<li>2013 - угадывание формул. Даны значения переменных и результат
вычисления, а также некоторая общая информация о выражении. Надо
прислать угаданные выражения.</li>
<li>2012 - AI для игры наподобие Supalex.</li>
</ul>
Результаты у нашей команды обычно... э... ну, лучше чем последнее место, чо. С одной стороны, главное не победа. C другой...<br />
Типичный «путь к успеху» таков. Команда географически сильно
распределённая, покрывая несколько часовых поясов. Теоретически это
могло бы даже быть преимуществом (непрерывный 24-часовой кодинг, как
24-часовая поддержка, а?). Но как-то не в нашем случае. О контесте
вспоминаем за пару дней. Обсуждаем выбор языка программирования, особо
не зацикливаясь на фиксации этого выбора. Типичное решение: «давайте
попробуем haskell, а там если увидим что чото не то, возьмём C++». При
том, что часть команды имеет очень мало опыта с Haskell, а другая часть —
с C++. Во время контеста (он длится трое суток) у каждого что-нибудь
случается, из разряда «зимой совершенно неожиданно выпал снег» —
приезжают родственники, нужно на работу или ещё что-нибудь. Т.е. вещи,
которые в принципе можно было предотвратить (отработать в другой день
итд), если позаботиться об этом достаточно заранее. В этот раз
соригинальничали. О контесте вспомнили аж за неделю. В качестве языка
программирования выбрали Scala. При том, что двое из команды (я и
Minoru) к этому моменту о ней только по наслышке знали. Я был в принципе
не против, т.к. наслышан о языке был давно — почему бы и не изучить. За
неделю скачал компилятор, среду разработки (hint: Idea community
edition с плагинами для Scala и vim mode) и уехал на дачу. Где через
напоминающий старые-добрые времена мобильный GPRS (ни дать ни взять
диалап - не верещит на всю комнату, и только) стал изучать гугл на тему
туториалов. Нашёл scala by example. Написав hello world, решил
потренироваться на кошках и пошёл куда очень давно не ходил — на
acm.timus.ru. Кстати, с удивлением обнаружил, что они теперь
поддерживают и Scala, и Haskell. Тот факт, что я с институтских времён
не занимался спортивным программированием, очень даже сказался — все мои
программы упёрлись в time limit :) Не из-за «тормозной явы», конечно, а
просто из-за плохих алгоритмов. В данном случае меня это не очень
расстроило — в рамках ICFPC производительность нашего кода обычно мало
важна. Общее впечатление от Scala, кстати — это скорее «пере-ява», чем
«недо-хаскель» :)<br />
У меня есть «особенность стиля разработки», по-моему, не очень
удивительная. Я плохо умею придумывать алгоритмы и организацию кода
отдельно от самого кода. Поэтому, приступая к чему-то новому, я обычно
начинаю с написания сколь угодно плохого алгоритма на том, что под руку
подвернётся (прототип в 15 строк, он может даже и вовсе не работать), а
дальше уже думаю над алгоритмами и дизайном параллельно с изменением
кода. Начало предыдущих ICFPC выглядело поэтому так. Пока народ в чате
думает над задачей, над алгоритмами, обсуждает, лучше ли здесь Haskell
или C++, я беру и начинаю что-то писать на haskell. К моменту, когда в
чате появляются сообщения вида «ну ладно, давайте писать», оказывается,
что в репозитории уже лежит мой код на haskell :) (хороший код или
плохой — отдельный вопрос). И в итоге остальные, из соображения
«экономии кода», чтобы не переписывать то же самое на другом языке,
продолжают тоже на haskell. Когда опыта с ним мало, это иногда
выливается в многочасовую борьбу с компилятором. Видимо как раз из-за
того, что прошлые разы я таким образом «навязывал» остальным Haskell, в
этот раз ForNeVer и предложил взять Scala :) Поэтому в этот раз в начале
контеста я ждал, пока ForNeVer cоздаст заготовку проекта, а в это время
пытался думать над задачей. Без кода думать получалось плохо.<br />
Кстати, о задаче.<br />
В этот раз опять нужно было написать AI для игры. Игра — нечто
похожее на тетрис на гексагональной решётке. Основные отличия: вся
последовательность фигур известна заранее; фигуры сами по себе вниз не
падают, их надо падать отдельными командами; и их можно закреплять не
только внизу, но и где-нибудь посередине, если упереть в стенку или в
другую фигуру. И ещё: каждая команда может быть закодирована одной из
нескольких букв. Поэтому из последовательностей команд можно составлять
слова. Имеется некоторое количество «заклинаний», «phrases of power»,
или попросту чит-кодов. За использование их в последовательности команд
дают отдельные очки. Одно заклинание (Ei!) сказали сразу, другие
предложили найти «в задании, в твитах, в background leaterature и game
artifacts». Бэкграундом были, видимо, произведения Лавкрафта.
Организация получения задач и отправки решений такая: дано фиксированное
количество «карт» — описаний игровых полей (они могут быть пустыми или
на них изначально могут быть какие-то занятые клетки) и фигур, которые
там могут выпадать. Надо для каждой карты прислать последовательность
команд, которая наберёт по специфическим правилам этого «тетриса» как
можно больше очков.<br />
Я нарисовал одну из первых карт (они выдаются в виде json) на
бумажке, и обнаружил, что там заполненными клетками написано «Ei!». Ага,
значит на других картах могут быть другие заклинания. Только рисовать
гексагональную решётку даже на клетчатой бумаге немного утомительно, так
что явно нужна какая-то рисовалка карт, чтобы хотя бы на них
посмотреть. Примерно к этому времени появилась заготовка проекта, и
ForNeVer уже сделал загрузку карт из json. Тогда я написал рисовалку
карт в виде ascii-art. Запустив её на имеющихся картах, узнали ещё
парочку заклинаний.<br />
В предыдущих ICFPC мы как-то умудрялись обходиться без визуализации
действий наших ботов. В этот раз rexim для разнообразия взялся таки
сделать визуализацию. Я в AI и родственных алгоритмах мало сведущ — в
прошлом году апофигеем стало использование волнового алгоритма. Поэтому я
взялся писать то, что особой изобретательности не требует, но при этом
явно понадобится — эмулятор этого самого тетриса. Отдельный анекдот
вышел вокруг представления игрового поля. Очевидно, нужно что-то
наподобие двухмерного массива, с возможностью как-нибудь делать операции
«вычёркивание строки» и «перемещение занятых клеток». При этом у меня
был, с одной стороны, опыт ещё институтских игрушек, где это был сишный
char[][]; с другой — опыт хаскеля предлагал использовать что-нибудь типа
[[CellState]]. Опыт со scala не подсказывал пока ничего. Я спросил в
чате, и мне предложили не заморачиваясь использовать
Array[Array[CellState]] (если кто не в курсе — это мутабельный массив с
произвольным доступом, в принципе аналогично сишному char[][]). Ну и
ладно. Обернул его в класс, дополнив методами типа «проверить валидность
координат», и вперёд. Таким образом, эмулятор вышел «мутабельным»: при
обработке команды он изменяет своё состояние. А когда позже ForNeVer
стал писать какой-никакой AI, ему понадобились методы типа «посмотреть
что получится если выдать такую-то команду», не изменяющие состояния
игрового поля. В результате пришлось переделывать эмулятор в частично
иммутабельный. Народ потом возмущался — зачем у нас эмулятор
мутабельный! в следующий раз никакой мутабельности! Я отвечал, что тогда
непонятно, зачем взяли Scala — код без мутабельности естественнее
писать на Haskell.<br />
Ещё через некоторое время после начала контеста к нам присоединились
grouzen и ulidtko. Ulidtko примерно одновременно с Minoru взялись решать
специфическую геометрическую задачку: поворот фигур на гексагональной
решётке. Причём каждый из них, кажется, пошёл своим путём. Я решил, что
трёх человек на такую простую на первый взгляд задачу будет многовато, и
решил этим не заниматься. В итоге большую часть времени я допиливал
помаленьку эмулятор и... играл в тетрис. В наш эмулятор с
визуализатором. Т.к. первый работоспособный AI у нас появился только
где-то к вечеру воскресенья, до этого мы могли набирать очки только
играя в наш «тетрис», набирая очки и записывая последовательности
команд. А т.к. AI у нас получился бесхитростный, на некоторых задачах
мои результаты так и остались лучшими. В воскресенье вечером мы залили
всё что этот наш AI нарешал. В понедельник все кроме Minoru ушли на
работу. Поэтому в понедельник Minoru ещё что-то допилил в AI и отправил
решения по тем задачам, по которым их удалось улучшить.<br />
Собственно, на этом и всё.<br />
«А мораль сей басни... а не будет сегодня никакой морали, дети, идите сами думайте» (с) не помню откуда. </div>
Portnovhttp://www.blogger.com/profile/07229550690332111129noreply@blogger.com5tag:blogger.com,1999:blog-3628051270185801752.post-66555186422221923822015-06-15T06:01:00.004-07:002015-06-15T06:01:58.987-07:00Настройка планшета Wacom Intuos Pro под Debian/Ubuntu и KDE 4/5<div dir="ltr" style="text-align: left;" trbidi="on">
Чтобы не забыть, запишу куда-нибудь.<br />
У wacom intuos pro есть четыре лампочки вокруг express ring. Подвиндами они показывают активность одного из четырёх режимов, в которых может работать express ring — это просто четыре варианта настроек. Переключаются режимы кнопкой в центре кольца.<br />
Под линуксами планшет работает без проблем сразу после включения. Только express keys по дефолту ни на что не настроены, а express ring работает просто скроллом. Из четырёх лампочек горит всегда первая.<br />
Express keys и express ring легко настраиваются из systemsettings в кедах (для гнома тоже есть конфигурялка, но специально на неё не смотрел). Но про переключение режимов и лампочек эта конфигурялка не знает. Точнее, у неё есть понятие профиля настроек, и даже можно настроить хоткей на их переключение, но с лампочками на планшете они не связаны.<br />
Ядерный драйвер wacom умеет управлять этими лампочками. Для этого надо записать число от 0 до 3 в файлик <code>/sys/bus/usb/devices/*/*/wacom_led/status_led0_select</code>. Только вот этот файлик по дефолту доступен на запись только руту.<br />
Отсюда возник вот такой набор костылей: <a href="https://github.com/portnov/wacom-intuos-pro-scripts">https://github.com/portnov/wacom-intuos-pro-scripts</a>.<br />
Правила udev 99-local.rules запускают скрипт wacom-setup.sh, который запускает chmod, чтобы разрешить простым юзерам запись в тот файлик в /sys.<br />
К кнопке посередине кольца средствами кед (или другими) привязывается какое-нибудь редкоиспользуемое сочетание клавиш. На это же сочетание средствами кед (или другими) вешается скрипт wacom-switch-mode.sh, который прибавляет единичку к значению в файлике (переключает лампочку) и вызывает скрипт wacom-ring-mode.sh, который вызывает xsetwacom, чтобы переназначить функции express ring.<br />
В принципе, переключать настройки можно не через xsetwacom, а дёргая кде-шную конфигурялку через dbus, чтобы она переключала профили. Всё это красиво работает под 4ми кедами.<br />
Ещё одна засада: в текущем состоянии в KDE5 конфигурялка вакомов не работает от слова совсем. Она вроде как портирована на KF5, но этот порт в дистрибутивы не включён. И в дебианах/убунтах он просто так не соберётся, потому что требует libxcb-xinput, которого под дебианами/убунтами нет, т.к. он самими разработчиками libxcb считается unstable и по дефолту с xcb не собирается. Если кому нечего делать, может попробовать собрать libxcb-xinput под дебиан или убунту. <br />
Ситуация несколько облегчается тем фактом, что в kubuntu 15.04 похоже поставляется не чистокровные KDE5, а помесь из компонентов KDE 4 и 5. В частности, можно запустить конфигурялку вакомов от четвёртых кед: <code>kcmshell4 kcm_wacomtablet</code>. Но, модуль кедов, который собственно управляет планшетами, он тоже от четвёртой версии, и потому в пятой по дефолту не запускается. Чтобы настройки работали и применялись, надо в автозапуск кедов добавить запуск этого модуля командой <code>qdbus org.kde.Wacom /kded loadModule wacomtablet</code>. Только из systemsettings в пятых кедах сейчас есть баг, из-за которого скрипт в автозапуск добавить можно, но работать он не будет. Так что надо написать *.desktop-файл, который будет запускать скрипт, который будет запускать qdbus, и положить этот desktop-файл в <code>~/.config/autostart</code>.</div>
Portnovhttp://www.blogger.com/profile/07229550690332111129noreply@blogger.com1tag:blogger.com,1999:blog-3628051270185801752.post-56740291609586523712012-11-20T09:45:00.000-08:002012-11-20T21:33:45.328-08:00LiveMath IV<div dir="ltr" style="text-align: left;" trbidi="on">
<br />
<br />
Дошли руки собрать очередную версию LiveMath.<br />
<br />
LiveMath — это LiveDVD, содержащий большой набор свободного
математического ПО. Предполагается для использования в основном в
демонстрационных целях, но может также использоваться для постоянной
работы.<br />
<br />
Картинка для привлечения внимания:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-ht_wfNu0CQw/UKvEThLrRgI/AAAAAAAAAEY/KGSriXm2QME/s1600/screen1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="http://2.bp.blogspot.com/-ht_wfNu0CQw/UKvEThLrRgI/AAAAAAAAAEY/KGSriXm2QME/s320/screen1.png" width="320" /></a></div>
<br />
(на картинке слева вверху FriCAS считает интегралы в специальных функциях, а справа R выводит графики по данным, включенным в поставку для примера).<br />
<br />
В этот раз LiveMath основан на
Ubuntu 12.10 (Quantal), плюс некоторое количество дополнительного софта. LiveMath IV содержит (среди прочего):<br />
<h3 style="text-align: left;">
<span style="font-weight: bold;">Системы компьютерной алгебры:</span></h3>
<ul style="text-align: left;">
<li><b>Maxima</b> 5.27 (<a href="http://maxima.sourceforge.net/">http://maxima.sourceforge.net</a>) - полнофункциональная система аналитических вычислений.</li>
<li><b>Fricas </b>1.1.8 (<a href="http://fricas.sourceforge.net/">http://fricas.sourceforge.net</a>) и <b>OpenAxiom</b> 1.4.1 (<a href="http://open-axiom.org/">http://open-axiom.org</a>) - обе актуальные версии мощной системы компьютерной алгебры Axiom.</li>
<li><b>YaCas </b>1.3.2 (<a href="http://yacas.sourceforge.net/">http://yacas.sourceforge.net</a>) - еще одна система компьютерной алгебры.</li>
<li><b>PARI/GP</b> 2.5.1
(<a href="http://pari.math.u-bordeaux.fr/">http://pari.math.u-bordeaux.fr/</a>) - широко используемая
компьютерно-алгебраическая система, разработанная для быстрых вычислений
в теории чисел (факторизации, алгебраическая теория чисел,
эллиптические кривые...).</li>
<li><b>GAP</b> 4r4p12 (<a href="http://www.gap-system.org/">http://www.gap-system.org/</a>) - свободно
распространяемый, открытый и расширяемый программный комплекс для
применения в области вычислительной дискретной математики, в частности,
теории групп.</li>
<li><b>Mathomatic</b> 15.8.2 (<a href="http://www.mathomatic.org/">http://www.mathomatic.org/</a>) - переносимая,
универсальная программа, которая может решать, упрощать, группировать,
дифференцировать, интегрировать и сравнивать алгебраические выражения.</li>
</ul>
<h3 style="text-align: left;">
<span style="font-weight: bold;">Системы автоматизации доказательств:</span></h3>
<ul style="text-align: left;">
<li><b>ACL2</b> 4.3
(<a href="http://www.cs.utexas.edu/users/moore/acl2/">http://www.cs.utexas.edu/users/moore/acl2/</a>) - язык программирования
для моделирования компьютерных систем и средство, помогающее доказывать
свойства этих моделей.</li>
<li><b>Coq</b> 8.3.pl4 (<a href="http://coq.inria.fr/">http://coq.inria.fr/</a>) - система
автоматизированного построения доказательств, с помощью которой, кроме
всего прочего, была решена проблема четырех красок.</li>
<li><b>Agda2</b> 2.3.0 (<a href="http://wiki.portal.chalmers.se/agda/pmwiki.php">http://wiki.portal.chalmers.se/agda/pmwiki.php</a>) - язык программирования с зависимыми типами и система автоматизации доказательств.</li>
<li> <b>Prover9/Mace4</b>, <b>Otter</b> и пр.</li>
</ul>
<h3 style="text-align: left;">
<span style="font-weight: bold;">Системы численных вычислений:</span></h3>
<ul style="text-align: left;">
<li><b>SciLab </b>5.3.3
(<a href="http://www.scilab.org/">http://www.scilab.org/</a>) - пакет научных программ для численных
вычислений, предоставляющий мощное открытое окружение для инженерных и
научных расчетов.</li>
<li><b>GNU Octave</b> 3.6.2 (<a href="http://www.octave.org/">http://www.octave.org/</a>) - язык высокого уровня, предназначенный для выполнения математических вычислений;</li>
<li><b>FreeMat</b>
4.0 (<a href="http://freemat.sourceforge.net/">http://freemat.sourceforge.net/</a>) - свободная среда для быстрой
разработки, научного прототипирования и обработки данных, имеет
интерфейс и синтаксис языка, подобные MatLab.</li>
<li><b>Yorick</b> 2.2.02
(<a href="http://yorick.sourceforge.net/">http://yorick.sourceforge.net/</a>) -специализированный С-подобный язык для создания симуляторов с упором на скорость вычислений.</li>
<li> <b>Dynare </b>4.3.0<b> </b>(<a href="http://www.dynare.org/">http://www.dynare.org/</a>).<b><br /></b></li>
</ul>
<h3 style="text-align: left;">
<span style="font-weight: bold;">Образовательные программы:</span></h3>
<ul>
<li><b>Kig </b>4.9.2 (<a href="http://edu.kde.org/kig/">http://edu.kde.org/kig/</a>), <b>Geogebra</b> 4.0.34.0 (<a href="http://geogebra.org/">http://geogebra.org</a>), <b>DrGeo</b> 1.1.0 — интерактивная геометрия.</li>
<li><b>KAlgebra </b>4.9.2<b><br /></b></li>
<li><b>KMPlot</b> 4.9.2 — средство для построения графиков.</li>
</ul>
<h3 style="text-align: left;">
<span style="font-weight: bold;">Обработка и визуализация данных:</span></h3>
<ul style="text-align: left;">
<li><b>Gnuplot</b> 4.6.0</li>
<li><b>Mayavi2</b> 4.1.0 (<a href="http://code.enthought.com/projects/mayavi/#Mayavi2">http://code.enthought.com/projects/mayavi/#Mayavi2</a>) - открытый пакет научной 2D и 3D визуализации данных.</li>
<li><b>OpenDX</b> 4.4.4 (<a href="http://www.opendx.org/">http://www.opendx.org/</a>) - программное средство для анализа данных в графическом виде, визуализации научных данных.</li>
<li><b>GGobi</b> 2.1.10 (<a href="http://www.ggobi.org/">http://www.ggobi.org/</a>) - среда визуализации многомерных данных;</li>
<li><b>QtiPlot</b> 0.9.8.8
- позиционируется как замена для Microcal Origin - программа для
несложной статистической обработки данных, построения всяческих
графиков.</li>
<li><b>Grace </b>5.1.22 (<a href="http://plasma-gate.weizmann.ac.il/Grace/">http://plasma-gate.weizmann.ac.il/Grace/</a>) - программа для подготовки двумерных графиков по численным данным.</li>
<li><b>PAW</b> 2.14.04
(<a href="http://cern.ch/paw/">http://cern.ch/paw/</a>) - интерактивная программа анализа и графического
представления результатов. Может применяться для анализа большого и
очень большого объёма данных.</li>
<li><b>ROOT</b> 5.34.00 (<a href="http://cern.ch/root/">http://cern.ch/root/</a>) - наследник
PAW, интерактивная система обработки и визуализации очень больших
объёмов научных данных.</li>
<li><b>GNU R</b> 2.15.1 (<a href="http://r-project.org/">http://r-project.org/</a>) - мощный язык статистических вычислений, используемый профессиональными статистиками.</li>
<li><b>GRETL</b> 1.9.9 (<a href="http://gretl.sourceforge.net/">http://gretl.sourceforge.net/</a>) - система эконометрического анализа.</li>
<li><b>Udav</b> 0.7.1.2 (<a href="http://udav.sourceforge.net/">http://udav.sourceforge.net/</a>) - инструмент визуализации данных.</li>
</ul>
<div style="text-align: left;">
</div>
<div style="text-align: left;">
</div>
<h3 style="text-align: left;">
Работа с графами</h3>
<ul>
<li><b>Tulip</b> 0.5.11</li>
<li><b>GraphThing</b> 1.3.2</li>
<li><b>Cytoscape</b> 2.8.3</li>
<li><b>Rocs</b> 1.7.2</li>
</ul>
<h3>
<span style="font-weight: bold;">Научные редакторы:</span></h3>
<h3 style="text-align: left;">
<span style="font-weight: bold;"></span></h3>
<ul style="text-align: left;">
<li><b>TeXLive </b>2012.20120611 - полноценный дистрибутив TeX.</li>
<li><b>TeXmacs</b> 1.0.7.15
(<a href="http://texmacs.org/">http://texmacs.org</a>) - текстовый редактор для набора математических и
прочих научных текстов, также позволяет включать в документ сессии <b>FriCAS</b>, <b>Maxima,</b> <b>Octave</b>, <b>SciLab</b> и других систем компьютерной математики. Данная версия использует Qt, так что выглядит заметно приятнее старых, и работает несколько шустрее.</li>
<li><b>Kile</b> 2.1.2 (<a href="http://kile.sourceforge.net/">http://kile.sourceforge.net/</a>) - интегрированная среда подготовки документов с помощью TeX.</li>
<li><b>Texmaker</b><b> </b>3.4 (<a href="http://www.xm1math.net/texmaker/">http://www.xm1math.net/texmaker/</a>) - интегрированная оболочка для LaTeX.</li>
<li><b>TeXworks</b> 0.5- лёгкая оболочка для LaTeX. </li>
<li><b>LyX</b> 2.0.3.</li>
</ul>
<div style="text-align: left;">
<br />
Также
LiveMath IV содержит среду XFCE 4.10, LibreOffice 3.6.2.
Для "больших" систем (ROOT, PAW, R, Octave) включена значительная часть
имеющихся в репозиториях Ubuntu пакетов. Для многих изначально
"консольных" систем включены GUI-обёртки, для некоторых по несколько, на
выбор. К большинству программ есть документация. Возможна установка
системы на жёсткий диск с помощью стандартного установщика Ubuntu.<br />
<br />
<a href="http://redmine.iportnov.ru/attachments/download/8/LiveMath.IV.packages.txt">Полный список установленных пакетов</a>.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
<a href="http://home.iportnov.ru/files/livemath/LiveMath-IV.iso" rel="nofollow">Загрузить образ ISO</a>. (2 GB). Образ гибридный: можно записать на DVD или на флешку. Выложен образ на моём домашнем сервере, суперскоростей не обещаю.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
<a href="http://redmine.iportnov.ru/projects/livemath">Страница проекта</a>.</div>
<div style="text-align: left;">
<br />
К
сожалению, у меня нет времени, чтобы тестировать все эти программы. То,
что я протестировал - работает. Багрепорты принимаются в комментариях
или на e-mail portnov at bk dot ru, но мгновенного исправления не
обещаю.<br />
<br />
LiveMath сделан с помощью Ubuntu Construction Kit
(<a href="http://uck.sourceforge.net/">http://uck.sourceforge.net/</a>), так что каждый, в принципе, может сделать
себе нечто подобное. Вероятно, это окажется проще, чем качать моё
изделие.</div>
</div>
Portnovhttp://www.blogger.com/profile/07229550690332111129noreply@blogger.com3tag:blogger.com,1999:blog-3628051270185801752.post-36057825290102617152012-10-11T07:24:00.001-07:002012-10-11T20:55:08.739-07:00Домашняя бухгалтерия в командной строке, yet another<div dir="ltr" style="text-align: left;" trbidi="on">
Сделал вот ещё одно приложение для «домашней бухгалтерии в командной строке», в стиле ledger или hledger, но лучше :)<br />
Называется YaLedger (yet another ledger).<br />
Код: <a href="https://gitorious.org/yaledger/yaledger/blobs/master/README.ru">https://gitorious.org/yaledger</a><br />
README: <a href="https://gitorious.org/yaledger/yaledger/blobs/master/README.ru">https://gitorious.org/yaledger/yaledger/blobs/master/README.ru</a><br />
Умеет:<br />
<ul style="text-align: left;">
<li>Автоматический выбор корреспондирующих счетов по настраиваемым правилам, так что в большинстве случаев достаточно записывать только одну половину проводки;</li>
<li>Сверку балансов счетов — можно указать, что в данный момент на счёте такая-то сумма, и yaledger автоматически сделает проводку, чтобы его данные сходились с указанными;</li>
<li>Шаблоны проводок; периодические проводки; автоматическое выполнение проводок при определённых условиях;</li>
<li>Чтение проводок из нескольких файлов;</li>
<li>Чтение проводок из форматов CSV и HTML (выписки из телебанков);</li>
<li>Само собой, работу с разными валютами;</li>
<li>Учёт курсовой разницы (из-за разницы между курсами купли/продажи)</li>
<li>Загрузку курсов валют ЦБ РФ;</li>
<li>Умную обработку дублирующихся записей;</li>
<li>Несколько отчётов: балансы счетов, обороты по счетам итп.</li>
</ul>
Сделано на haskell, с учётом основных (моих) требований:<br />
<ul style="text-align: left;">
<li>Это приложение именно для домашней бухгалтерии, так что вести журнал проводок должно быть максимально просто (например, мне лень для каждой проводки указывать два счёта).</li>
<li>Я хочу, чтобы приложение оперировало более-менее стандартными объектами бухгалтерского учёта. Ну, например, hledger не использует таких сущностей, как "дебет" или "кредит", он просто прибавляет к балансу число, которое может быть отрицательным и положительным; yaledger ведёт себя более похоже на "взрослые" системы, учитывая отдельно кредитовые и дебетовые полупроводки.</li>
<li>Мне лень, да и некогда особенно, заниматься отладкой; поэтому максимально возможное количество проверок я переложил на систему типов, а там, где в компайл-тайме особенно ничего не проверишь — сделал так, чтобы система типов заставляла меня проверять всё что можно (например, невозможно кредитовать дебетовый счёт, не сойдутся типы; а если в данном случае неизвестно, кредитовый это счёт или дебетовый, компилятор заставит явно написать проверку).</li>
</ul>
Что-то вроде документации (неполной) тут: <a href="http://redmine.iportnov.ru/projects/yaledger/wiki">http://redmine.iportnov.ru/projects/yaledger/wiki</a>.<br />
<br />
Эту штуку я использую в течение всего периода её разработки (чуть больше месяца :)). У меня работает, но баги, конечно, возможны. <br />
<br />
Это пока что-то типа пре-релиза. На днях допишу документацию и выложу на hackage.</div>
Portnovhttp://www.blogger.com/profile/07229550690332111129noreply@blogger.com6tag:blogger.com,1999:blog-3628051270185801752.post-26686544366674553392011-10-28T08:59:00.000-07:002011-10-28T09:47:02.031-07:00Введение в прикладное программирование под GNU/Linux<span style="font-style: italic;">Это конспект, который я готовил для доклада на <a href="http://school2011.masu-inform.ru/">конференции</a>, проводившейся местным университетом совместно с нашей <a href="http://lug-mgn.ru/">LUG</a>. Доклад «для самых маленьких», так что профессионалам просьба не жаловаться на поверхностность и обзорность.</span><br /><br /><div id="content"> <div class="sect1"> <h2 id="_аудитория">Аудитория</h2> <div class="sectionbody"> <div class="paragraph"><p>Эта статья расчитана на два вида читателей. Во-первых, это люди, имеющие опыт программирования под MS Windows, но не имеющие такого опыта под GNU/Linux. Во-вторых, это люди, не имеющие опыта программирования вовсе. Однако, я предполагаю, что читатель в общем знаком с общепринятой в программировании терминологией, и ему не нужно объяснять, например, что такое «программа», «функция», «компилятор» или «отладка».</p></div> </div> </div> <div class="sect1"> <h2 id="_средства_разработки">Средства разработки</h2> <div class="sectionbody"> <div class="paragraph"><p>Я буду рассматривать разработку с использованием тех средств, которые являются наиболее «родными» для GNU/Linux. К ним относятся:</p></div> <div class="ulist"><ul><li> <p> Язык программирования C </p> </li><li> <p> Командная оболочка bash </p> </li><li> <p> Текстовые редакторы Vim и Emacs </p> </li><li> <p> Компилятор GCC </p> </li><li> <p> Отладчик GDB </p> </li><li> <p> Утилита для сборки проекта GNU make </p> </li><li> <p> Система управления версиями Git </p> </li><li> <p> Оконная система X11 </p> </li></ul></div> <div class="paragraph"><p>Выбор именно этих средств не является догмой. Каждое из выше перечисленных средств может быть при желании заменено на другое. Однако, обычно под фразами наподобие «среда разработки Linux» понимается именно этот набор инструментов.</p></div> </div> </div> <div class="sect1"> <h2 id="_языки_программирования">Языки программирования</h2> <div class="sectionbody"> <div class="paragraph"><p>Наиболее «родным» языком программирования для GNU/Linux является C. Это обусловлено следующими факторами:</p></div> <div class="ulist"><ul><li> <p> GNU/Linux заимствует многие идеи (практически, идеологию) операционной системы UNIX; </p> </li><li> <p> Операционная система UNIX была написана на языке C (собственно, этот язык создавался именно для написания этой ОС); </p> </li><li> <p> Соответственно, ядро Linux и системное окружение GNU написаны тоже на C. </p> </li></ul></div> <div class="paragraph"><p>Ниже я буду рассматривать разработку с использованием языка C. Однако, этот выбор не является догмой. Другими популярными при разработке под GNU/Linux языками являются C++, Python, Perl. Конечно, могут использоваться и любые другие языки.</p></div> </div> </div> <div class="sect1"> <h2 id="_среда_разработки">Среда разработки</h2> <div class="sectionbody"> <div class="paragraph"><p>В течение последних двух десятилетий очень широкое распространение получили т.н. IDE — интегрированные среды разработки. Такая среда включает в себя текстовый редактор, компилятор, отладчик, средства сборки проекта и мн.др. Такие среды есть и под GNU/Linux (наиболее популярны Eclipse, NetBeans, IDEA, KDevelop, Anjuta). Однако, история разработки под UNIX-подобные системы показывает, что IDE не являются не только единственным, но и наиболее эффективным средством разработки. Практически, правильный ответ на вопрос «какая самая лучшая IDE под GNU/Linux» — это «GNU/Linux это и есть IDE».</p></div> <div class="paragraph"><p>Часто можно встретить мнение, что большой проект без IDE разрабатывать невозможно. Это мнение легко опровергается. Первые версии UNIX писались даже не в Vim (его тогда ещё не было), а в Ed. Это так называемый «построчный» текстовый редактор, в котором вы можете редактировать за раз только одну строку текста. Весь файл на экране не отображается. В случае с UNIX по-другому и быть не могло — у разработчиков не было никаких экранов, а общение с системой осуществлялось при помощи телетайпов. Современное ядро Linux пишется в основном в редакторах Emacs и Vim.</p></div> <div class="paragraph"><p>Многие утилиты UNIX вызывают «текстовый редактор по умолчанию». Команда, запускающая текстовый редактор по умолчанию, берётся из переменной окружения <tt>$EDITOR</tt>. Некоторые утилиты смотрят сначала в переменную <tt>$VISUAL</tt>, и, лишь если она не установлена, в переменную <tt>$EDITOR</tt>. Это исторически сложившееся поведение: к старым компьютерам зачастую не было подключено никакого дисплея, а только телетайп, поэтому запускать экранный (визуальный) редактор смысла не было. В современных дистрибутивах обычно по умолчанию оказывается <tt>EDITOR=vi</tt> или <tt>EDITOR=nano</tt>. Указать использование другого редактора для одной команды можно так:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>EDITOR=emacs some-command</tt></pre> </div></div> <div class="paragraph"><p>Чтобы использовать нужный редактор по умолчанию всегда, нужно добавить в файл <tt>~/.profile</tt> строчку типа</p></div> <div class="literalblock"> <div class="content"> <pre><tt>export EDITOR=emacs</tt></pre> </div></div> <div class="paragraph"><p>Исторически сложилось так, что «настоящими» текстовыми редакторами для программистов являются только Vim и Emacs (просто из-за того, что у них самая долгая история развития именно в качестве текстовых редакторов для программистов). Остальные редакторы находятся в положении догоняющих.</p></div> </div> </div> <div class="sect1"> <h2 id="_командная_оболочка">Командная оболочка</h2> <div class="sectionbody"> <div class="paragraph"><p>Командная оболочка (или командный интерпретатор) — это программа, принимающая команды от пользователя на некотором достаточно простом языке программирования и выполняющая их. Большинство команд запускают одноимённые программы. Отдельные команды представляют собой конструкции языка программирования оболочки.</p></div> <div class="paragraph"><p>Стандарт POSIX включает описание минимального набора возможностей, предоставляемых командной оболочкой. Реально используемые оболочки предоставляют, как правило, больше возможностей.</p></div> <div class="paragraph"><p>ОС семейств DOS и Windows заимствовали некоторые функции командной оболочки из UNIX, однако их авторы пошли на существенные упрощения, из-за чего функционал <tt>COMMAND.COM</tt> и <tt>cmd.exe</tt> получился сильно урезанным. PowerShell вполне на уровне, но работает существенно по-другому.</p></div> <div class="paragraph"><p>В рамках этой статьи я ограничусь использованием командной оболочки bash (как наиболее распространённой и используемой по умолчанию в большинстве дистрибутивов) для запуска компилятора и других средств разработки. Хороший обзор использования командной оболочки можно найти, например, в известной книге <a>[kernigan_pike]</a>.</p></div> </div> </div> <div class="sect1"> <h2 id="_документация">Документация</h2> <div class="sectionbody"> <div class="paragraph"><p>Все средства разработки и библиотеки в GNU/Linux обычно довольно хорошо документированы. Традиционно для документации используется специальный формат и утилита для его просмотра — <tt>man</tt>. Документация в системе делится на несколько разделов:</p></div> <div class="olist arabic"><ol class="arabic"><li> <p> Команды пользователя (например, ls, gcc или man) </p> </li><li> <p> Системные вызовы — API ядра ОС </p> </li><li> <p> Библиотечные функции </p> </li><li> <p> Драйвера и т.п </p> </li><li> <p> Форматы файлов </p> </li><li> <p> Игры и т.п </p> </li><li> <p> Различные обзоры подсистем </p> </li><li> <p> Команды, используемые для системного администрирования </p> </li></ol></div> <div class="paragraph"><p>Для вызова раздела документации по имени нужно указать это имя при вызове команды man (например, <tt>man ls</tt>). Иногда разделы с одинаковым названием есть сразу в нескольких разделах документации документации. Указать конкретный раздел можно при вызове man (например, <tt>man 3 printf</tt>).</p></div> <div class="paragraph"><p>Более подробную информацию о справочной системе <tt>man</tt> см. в <tt>man man</tt>.</p></div> <div class="paragraph"><p>Утилиты системного окружения GNU часто используют для документации формат info. См., например, <tt>info Coreutils</tt>.</p></div> </div> </div> <div class="sect1"> <h2 id="_компилятор">Компилятор</h2> <div class="sectionbody"> <div class="paragraph"><p>Сейчас существует много компиляторов языка C, более-менее совместимых с различными стандартами. Тем не менее, пока что в среде GNU/Linux наиболее применимым остаётся компилятор C, входящий в комплект GNU Compilers Collection (GCC). Этот компилятор, кроме стандарта C, поддерживает некоторое количество расширений стандарта. Эти расширения, в частности, широко используются в исходных текстах ядра Linux. В последнее время появляются компиляторы, способные скомпилировать ядро Linux (например, llvm-clang, или EKO).</p></div> <div class="paragraph"><p>Компилятор GCC запускается из командной оболочки командой вида</p></div> <div class="literalblock"> <div class="content"> <pre><tt>gcc [OPTIONS] program.c</tt></pre> </div></div> <div class="paragraph"><p>где <tt>program.c</tt> — имя входного файла. Кроме того, по стандарту POSIX, компилятор может быть запущен командой <tt>cc program.c</tt> (cc — от "C compiler").</p></div> <div class="paragraph"><p>При обычном запуске компилятор пытается создать исполняемый файл. По умолчанию, выходной файл называется <tt>a.out</tt> (такое название осталось от древних версий UNIX). Другое название можно задать с помощью опции компилятора <tt>-o</tt>, например,</p></div> <div class="literalblock"> <div class="content"> <pre><tt>gcc -o program program.c</tt></pre> </div></div> <div class="paragraph"><p>При сборке программы из нескольких модулей компилятору можно подавать на вход несколько исходных файлов или файлов объектного кода, например,</p></div> <div class="literalblock"> <div class="content"> <pre><tt>gcc -o program main.c module1.o module2.o …</tt></pre> </div></div> <div class="paragraph"><p>Чтобы только скомпилировать один исходный файл в объектный код (не пытаясь собрать исполняемый файл), нужно дать команду вида</p></div> <div class="literalblock"> <div class="content"> <pre><tt>gcc -c module.c</tt></pre> </div></div> <div class="paragraph"><p>(имя выходного файла по умолчанию будет module.o).</p></div> <div class="paragraph"><p>Для сборки программы часто бывают нужны библиотеки. В Linux используются два типа библиотек: библиотеки для статической и динамической компоновки. При статической компоновке библиотека при сборке программы целиком включается в состав исполняемого файла. При динамической компоновке в исполняемый файл вписывается только название динамической библиотеки, а поиск этого файла и компоновка происходят при запуске программы.</p></div> <div class="paragraph"><p>Статическая библиотека в UNIX-подобных системах представляет собой архив (старинного формата <tt>ar</tt>), включающий набор объектных файлов. Такой архив создаётся командой вида</p></div> <div class="literalblock"> <div class="content"> <pre><tt>ar r libsomething.a module1.o module2.o …</tt></pre> </div></div> <div class="paragraph"><p>Имена файлов библиотек традиционно начинаются с префикса <tt>lib</tt>.</p></div> <div class="paragraph"><p>Динамически загружаемая библиотека представляет собой объектный файл специального формата (расчитанного на динамическую загрузку). Такая библиотека создаётся командой вида</p></div> <div class="literalblock"> <div class="content"> <pre><tt>gcc -shared -o libsomething.so module1.c module2.c …</tt></pre> </div></div> <div class="paragraph"><p>Для использования библиотеки при сборке программы её нужно указать компилятору при помощи опции <tt>-l</tt>, например</p></div> <div class="literalblock"> <div class="content"> <pre><tt>gcc -o program -lm program.c</tt></pre> </div></div> <div class="paragraph"><p>(здесь будет использоваться файл библиотеки <tt>libm.so</tt>, префикс <tt>lib</tt> компилятор подставляет по умолчанию). По умолчанию компилятор собирает программу, использующую динамические библиотеки. Если нужно использовать статические версии библиотек, компилятору нужно указать опцию <tt>-static</tt>.</p></div> <div class="paragraph"><p>Подробную информацию об опциях gcc см. в <tt>man gcc</tt>.</p></div> </div> </div> <div class="sect1"> <h2 id="_hello_world">Hello, world!</h2> <div class="sectionbody"> <div class="paragraph"><p>Считается, что традиция начинать изучение языка программирования с написания программы, выводящей строку "Hello, world!", пошла с книги Кернигана и Ричи "Язык C" <a>[kernigan_richie]</a>. В случае с языком C эта программа выглядит следующим образом:</p></div> <div class="listingblock"> <div class="content"> <pre><tt><span style="font-weight: bold"><span style="color:#000080;">#include</span></span> <stdio.h><stdio.h><br /><br /><span style="color:#009900;">int</span> <span style="font-weight: bold"><span style="color:#000000;">main</span></span><span style="color:#990000;">(</span><span style="color:#009900;">int</span> argc<span style="color:#990000;">,</span> <span style="color:#009900;">char</span><span style="color:#990000;">*</span> argv<span style="color:#990000;">[])</span> <span style="color:#FF0000;">{<br /></span><span style="font-weight: bold"><span style="color:#000000;">printf</span></span><span style="color:#990000;">(</span><span style="color:#FF0000;">"Hello world!</span><span style="color:#CC33CC;">\n</span><span style="color:#FF0000;">"</span><span style="color:#990000;">);</span><br /><span style="font-weight: bold"><span style="color:#0000FF;">return</span></span> <span style="color:#993399;">0</span><span style="color:#990000;">;</span> <span style="color:#FF0000;"><br />}</span></stdio.h></tt></pre></div></div> <div class="paragraph"><p>Чтобы запустить эту программу, этот текст нужно записать в файл с именем, скажем, <tt>hello.c</tt>, и из директории, в которой расположен этот файл, дать команду вида</p></div> <div class="literalblock"> <div class="content"> <pre><tt>gcc -o hello hello.c</tt></pre> </div></div> <div class="paragraph"><p>Впрочем, в случае такой простой программы достаточно дать команду</p></div> <div class="literalblock"> <div class="content"> <pre><tt>make hello</tt></pre> </div></div> <div class="paragraph"><p>(я поясню ниже, почему эти две команды работают одинаково). В результате в той же директории появится исполняемый файл с именем <tt>hello</tt>. Запустить его можно командой</p></div> <div class="literalblock"> <div class="content"> <pre><tt>./hello</tt></pre> </div></div> </div> </div> <div class="sect1"> <h2 id="_порядок_сборки">Порядок сборки</h2> <div class="sectionbody"> <div class="paragraph"><p>Остановимся несколько подробнее на том, что именно делает компилятор. Порядок действий компилятора C традиционен, и применяется компиляторами некоторых других языков.</p></div><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/-pQomkwucRHk/TqrTl_qx8CI/AAAAAAAAAC8/EXOMc5JQWKs/s1600/compiling.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 215px;" src="http://1.bp.blogspot.com/-pQomkwucRHk/TqrTl_qx8CI/AAAAAAAAAC8/EXOMc5JQWKs/s320/compiling.png" alt="" id="BLOGGER_PHOTO_ID_5668575730698678306" border="0" /></a><br /><br /><div class="paragraph"><p>На входе компилятор имеет в общем случае набор файлов с исходными текстами. Перед началом собственно компиляции эти файлы обрабатываются т.н. препроцессором (программа <tt>cpp</tt>). Главная функция этой программы — выполнение директив вида <tt>#include <stdio.h></stdio.h></tt>. Встретив такую директиву, препроцессор вставляет содержимое указанного файла (в данном случае, <tt>stdio.h</tt>) на место этой директивы. Препроцессор понимает ещё некоторые директивы, но сейчас на них останавливаться я не буду.</p></div> <div class="paragraph"><p>После препроцессора выполняется собственно компиляция. Из исходных файлов на этом этапе получаются т.н. объектные файлы. Это файлы, содержащие исполняемый машинный код, но ещё не готовые для запуска. Главное, чего в них недостаёт — это адреса вызываемых библиотечных функций. Например, код функции <tt>printf()</tt> содержится в библиотеке <tt>libc</tt>. А в объектном файле содержится только имя этой функции. Кроме того, объектный файл содержит имена всех объявленных в нём функций.</p></div> <div class="paragraph"><p>Объектные файлы, а также используемые библиотеки подаются на вход компоновщику (программа <tt>ld</tt>). Компоновщик ищет все вызываемые из различных объектных файлов функции (по именам) в объектных файлах и в библиотеках. Если все функции найдены, то компоновщик собирает собственно исполняемый файл. При этом имена вызываемых функций заменяются на конкретные адреса памяти. В случае использования динамической библиотеки имя используемой функции остаётся, и к нему добавляется имя файла динамической библиотеки, в которой при запуске программы нужно будет искать эту функцию.</p></div> <div class="paragraph"><p>Собственно программа <tt>gcc</tt> представляет собой так называемый драйвер (driver). Она запускает упомянутые выше программы (или только некоторые из них, в зависимости от опций), чтобы получить исполняемый файл.</p></div> </div> </div> <div class="sect1"> <h2 id="_второй_пример_решение_квадратных_уравнений">Второй пример: решение квадратных уравнений</h2> <div class="sectionbody"> <div class="paragraph"><p>В качестве несколько более сложного примера рассмотрим программу, которая должна решать квадратные уравнения. Пользователь вводит коэффициенты квадратного трёхчлена, а программа выдаёт его действительные корни. Вот полный текст такой программы:</p></div> <div class="listingblock"> <div class="content"> <pre><tt><tt><span style="font-weight: bold"><span style="color:#000080;">#include <stdio.h><stdio.h><br />#include <math.h><math.h><br /><br />/* solve: calculate roots of square equation.<br />* a, b, c are coefficients in equation.<br />* Roots would be stored at x1, x2.<br />* Return value: count of real roots.<br />*/<br />int solve(double a, double b, double c,<br /> double* x1, double* x2) {<br />double D = b*b - 4*a*c;<br />double sqrtD;<br /><br />if (D > 0) {<br /> sqrtD = sqrt(D);<br /> *x1 = (-b - sqrtD)/(2.0 * a);<br /> *x2 = (-b + sqrtD)/(2.0 * a);<br /> return 2;<br />} else if (D < 0)<br /> return 0;<br /> else {<br /> *x1 = -b/(2.0*a);<br /> return 1;<br /> }<br />}<br /><br />int main (int argc, char* argv[]) {<br />double a,b,c;<br />double x1, x2;<br />int roots_count;<br /><br />// Input coefficients<br />printf("A: ");<br />scanf("%lf", &a);<br />printf("B: ");<br />scanf("%lf", &b);<br />printf("C: ");<br />scanf("%lf", &c);<br /><br />// Solve the equation<br />roots_count = solve(a,b,c, &x1, &x2);<br /><br />// Output results<br />switch (roots_count) {<br /> case 0:<br /> printf("No (real) roots.\n");<br /> break;<br /> case 1:<br /> printf("One root: %0.4lf\n", x1);<br /> break;<br /> case 2:<br /> printf("Two roots: %0.4lf and %0.4lf\n",<br /> x1, x2);<br /> break;<br />}<br /><br />return 0;<br />}<br /><br /></math.h></stdio.h></span></span></tt></tt></pre></div></div> <div class="paragraph"><p>По аналогии с предыдущим примером, запишем этот текст в файл <tt>square.c</tt> и попытаемся скомпилировать его командой</p></div> <div class="literalblock"> <div class="content"> <pre><tt>gcc -o square square.c</tt></pre> </div></div> <div class="paragraph"><p>Но на этот раз мы получим ошибку примерно такого вида:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>/tmp/cc6RNFIi.o: In function `solve': square.c:(.text+0x6d): undefined reference to `sqrt' collect2: ld returned 1 exit status</tt></pre> </div></div> <div class="paragraph"><p>В чём здесь дело? Ясно, что компилятору почему-то не понравился вызов функции <tt>sqrt()</tt>. Причём, он жалуется уже не на файл исходного кода, а на объектный файл (вот этот <tt>cc6RNFIi.o</tt>). Это означает, что исходный файл благополучно скомпилировался, а проблемы возникли на стадии компоновки (что можно видеть и по упоминанию в тексте ошибки программы <tt>ld</tt> — это стандартный в GNU/Linux компоновщик). Компоновщик не смог найти функцию <tt>sqrt()</tt>. В данном случае, это произошло из-за того, что эта функция содержится в библиотеке <tt>libm</tt>, а мы не просили компилятор использовать её. Чтобы избавиться от этой ошибки, нам нужно изменить команду компиляции на следующую:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>gcc -o square -lm square.c</tt></pre> </div></div> <div class="paragraph"><p>Такая команда должна отработать без ошибок и создать исполняемый файл <tt>square</tt>.</p></div> <div class="paragraph"><p>При сборке любой достаточно сложной программы нам придётся использовать несколько библиотек, и, возможно, понадобится указывать ещё какие-то опции компилятору. Команда может получиться довольно длинная. Что же, каждый раз набирать её вручную? Нет. Один из принципов философии UNIX гласит: «Всё, что может быть автоматизировано, должно быть автоматизировано». Здесь нам пригодится одна из древнейших UNIX-утилит — программа <tt>make</tt>. Чтобы воспользоваться ею, нужно написать файл с именем <tt>Makefile</tt> (в той же директории, что и наш исходный файл) со следующим содержимым:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>square: square.c $(CC) -o $@ -lm $<</tt></pre> </div></div> <div class="paragraph"><p>Теперь собрать исполняемый файл можно просто дав команду <tt>make</tt>. Как это работает?</p></div> </div> </div> <div class="sect1"> <h2 id="_make">Make</h2> <div class="sectionbody"> <div class="paragraph"><p>Утилита <tt>make</tt> предназначена для сборки программ (хотя может использоваться для автоматизации многих других похожих задач). Она читает файл с именем <tt>Makefile</tt> и видит в нём набор правил. Каждое правило определяет три вещи: цель (goal, т.е. то, что нужно собрать), список исходных файлов и набор команд, которые нужно выполнить, чтобы собрать цель из исходных файлов. В примере выше, <tt>square</tt> — это имя цели, <tt>square.c</tt> — единственный в данном случае исходный файл (если их несколько, они перечисляются через пробел), а вторая строчка — команда. В команде могут использоваться переменные. Некоторые из переменных имеют специальное значение. В частности, в любом правиле <tt>$@</tt> обозначает имя цели, а <tt>$<</tt> — первый исходный файл. Переменная <tt>$(CC)</tt> указывает на компилятор C, используемый в системе по умолчанию (в большинстве случаев это <tt>gcc</tt>, но бывает и что-нибудь другое).</p></div> <div class="paragraph"><p>В имени цели и списке исходных файлов может использоваться подстановочный символ <tt>%</tt>. Например, такое правило:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>%.o: %.c $(CC) -c $<</tt></pre> </div></div> <div class="paragraph"><p>обозначает, что файлы с именем, заканчивающимся на <tt>.o</tt>, нужно собирать из соответствующих файлов с суффиксом <tt>.c</tt>.</p></div> <div class="paragraph"><p>Кроме того, <tt>make</tt> заранее знает некоторое количество правил по умолчанию. Среди них есть упомянутое в последнем примере, а также правило</p></div> <div class="literalblock"> <div class="content"> <pre><tt>%: %.c $(CC) -o $@ $<</tt></pre> </div></div> <div class="paragraph"><p>Благодаря этому правилу, в примере с «Hello, world!» просто команда <tt>make hello</tt> запускала <tt>cc -o hello hello.c</tt>.</p></div> <div class="paragraph"><p>По набору правил <tt>make</tt> составляет граф зависимостей целей друг от друга и от исходных файлов, и выполняет только те команды, которые нужны для сборки цели, указанной в командной строке. Если не указано никаких целей, то собирается первая цель, описанная в <tt>Makefile</tt>.</p></div> <div class="paragraph"><p>Более подробную информацию об этой утилите см., например, в <tt>man make</tt>.</p></div> </div> </div> <div class="sect1"> <h2 id="_управление_версиями">Управление версиями</h2> <div class="sectionbody"> <div class="paragraph"><p>Для управления версиями исходного кода может использоваться любая VCS. Однако, раз уж мы говорим о GNU/Linux, рассмотрим вкратце систему, используемую для разработки ядра Linux: git. По git существует довольно обширная документация, в т.ч. и на русском языке. См. например, мою статью <a>[vcs_git]</a> или известную серию статей <a>[los-t_git]</a>.</p></div> <div class="paragraph"><p>Для начала использования git нужно создать репозиторий — хранилище для версий файлов. Это делается командой</p></div> <div class="literalblock"> <div class="content"> <pre><tt>git init</tt></pre> </div></div> <div class="paragraph"><p>Теперь можно добавлять файлы в репозиторий. Но нам не нужно отслеживать версии некоторых файлов, а именно: объектных файлов и исполняемых файлов. Чтобы сразу исключить их из рассмотрения git, напишем файл <tt>.gitignore</tt> следующего содержания:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>*.o square hello</tt></pre> </div></div> <div class="paragraph"><p>Теперь команда</p></div> <div class="literalblock"> <div class="content"> <pre><tt>git add .</tt></pre> </div></div> <div class="paragraph"><p>добавит в репозиторий все файлы в текущей директории, кроме упомянутых в файле <tt>.gitignore</tt>. После этого можно делать коммит командой</p></div> <div class="literalblock"> <div class="content"> <pre><tt>git commit</tt></pre> </div></div> <div class="paragraph"><p>По этой команде откроется текстовый редактор по умолчанию. Тут нужно будет написать комментарий к коммиту. В данном случае достаточно строчки типа «Initial commit».</p></div> </div> </div> <div class="sect1"> <h2 id="_отладка">Отладка</h2> <div class="sectionbody"> <div class="paragraph"><p>Для отладки в Linux используется отладчик <tt>gdb</tt>. Но сначала, для того, чтобы программу было удобно отлаживать, её нужно скомпилировать с опцией <tt>-g</tt>. Сейчас нам достаточно изменить <tt>Makefile</tt>, приведя его к виду</p></div> <div class="literalblock"> <div class="content"> <pre><tt>square: square.c $(CC) -o $@ -lm -g $<</tt></pre> </div></div> <div class="paragraph"><p>и пересобрать программу.</p></div> <div class="paragraph"><p>При обычной компиляции в исполняемый файл не попадают имена функций, переменных и т.п. Опция <tt>-g</tt> указывает компилятору, что эту информацию нужно записать в соответствующую секцию исполняемого файла. Кроме того, с этой опцией в исполняемый файл записывается информация о соответствии смещений и номеров строк в исходном файле.</p></div> <div class="paragraph"><p>Отладка запускается командой вида</p></div> <div class="literalblock"> <div class="content"> <pre><tt>gdb ./square</tt></pre> </div></div> <div class="paragraph"><p>Если отлаживаемой программе нужно передать какие-то опции командной строки, их можно указать здесь же:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>gdb ./some-program -a -b</tt></pre> </div></div> <div class="paragraph"><p>При запуске отладчика появляется приглашение командной строки вида:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>GNU gdb (GDB) 7.2-ubuntu Copyright (C) 2010 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http: org="" licenses="" html=""> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "i686-linux-gnu". For bug reporting instructions, please see: <http: org="" software="" gdb="" bugs="">... Reading symbols from /home/portnov/LUG/src/square...done. (gdb)</http:></http:></tt></pre> </div></div> <div class="paragraph"><p>Работа с отладчиком, в общих чертах, напоминает работу с командной оболочкой. Вы вводите команды, отладчик их исполняет. Как и в командной оболочке, работает автодополнение команд по клавише Tab. Кроме того, для краткости можно сокращать команды до первых нескольких букв — лишь бы избежать неоднозначности.</p></div> <div class="paragraph"><p>К наиболее часто используемым командам относятся:</p></div> <div class="dlist"><dl><dt class="hdlist1"> list </dt><dd> <p> Напечатать очередной кусок исходника (печатается 10 строк). Можно указать конкретные номера строк после имени команды, например <tt>l 10,15</tt>. </p> </dd><dt class="hdlist1"> run </dt><dd> <p> Запустить программу на выполнение под отладчиком. Программа будет выполняться до ближайшей точки останова, или до конца. </p> </dd><dt class="hdlist1"> break </dt><dd> <p> Установить точку останова. Номер строки, на которой нужно установить точку останова, указывается после имени команды. </p> </dd><dt class="hdlist1"> next </dt><dd> <p> Выполнить одну строку программы. </p> </dd><dt class="hdlist1"> print </dt><dd> <p> Вычислить и напечатать выражение. Выражение указывается после команды. Таким образом можно, например, однократно посмотреть значение какой-нибудь переменной. </p> </dd><dt class="hdlist1"> display </dt><dd> <p> Добавить выражение к списку постоянно отображаемых. Значения этих выражений будут показываться после исполнения каждой команды. Рядом с каждым выражением печатается его номер в списке. Удалить выражение из списка можно командой <tt>undisplay</tt> с номером выражения. </p> </dd><dt class="hdlist1"> quit </dt><dd> <p> Выход из отладчика. </p> </dd></dl></div> <div class="paragraph"><p>Более подробную информацию по GDB см. в <tt>man gdb</tt>.</p></div> </div> </div> <div class="sect1"> <h2 id="_оконная_система_x11">Оконная система X11</h2> <div class="sectionbody"> <div class="paragraph"><p>Исторически в UNIX не было и не могло быть никакой графической среды, потому что не было графических дисплеев. Графическая среда для UNIX появилась примерно тогда, когда появились распространённые графические дисплеи: в 1984. Сначала она называлась W (от Window), затем её усовершенствовали и назвали следующей буквой алфавита — X, следующая версия называлась X2… Сейчас имеем X11.</p></div> <div class="paragraph"><p>X11 представляет собой, прежде всего, сетевой протокол поверх TCP/IP и UDP/IP. У протокола есть клиент и есть сервер. Клиент посылает последовательность запросов вида «нарисуй мне окошко», «нарисуй на нём кнопочку», а сервер их исполняет. Один из главных принципов X11 — «определять механизмы, а не политики». Протокол предоставляет возможность, скажем, рисовать окошки, а как именно они будут отображаться — не определяет.</p></div> <div class="paragraph"><p>Наиболее распространённым X-сервером сейчас является Xorg (<a href="http://x.org/">http://x.org</a>); всё ещё жив XFree86; под Windows актуален Xming; выпускаются аппаратные X-серверы — комплекты «монитор + клавиатура + мышка», в которых поддержка серверной функциональности X11 реализована аппаратно — такие комплекты используются в качестве графических терминалов.</p></div> <div class="paragraph"><p>Протокол X11, в отличие от, скажем, HTTP, является бинарным, а не текстовым — это сделано из соображений экономии пропускной способности сетевого соединения и простоты разбора запросов сервером. Но это усложняет создание клиентов этого протокола: собирать замысловатые бинарные X11-запросы заведомо сложнее, чем, например, текстовые HTTP-запросы. Поэтому для написания X-клиентов используются специальные библиотеки функций, формирующих и отправляющих серверу X-запросы. Наиболее распространена библиотека libX11. Более современным вариантом является libxcb.</p></div> <div class="paragraph"><p>Запросы X11 весьма низкоуровневые. Например, чтобы реализовать функциональность кнопки, нужно нарисовать в окне прямоугольник, написать в нём текст, ждать в цикле нажатия кнопки мыши, и при каждом нажатии проверять, где щёлкнули — внутри прямоугольника или вне него. Поэтому стали появляться так называемые тулкиты — библиотеки, являющиеся высокоуровневыми обёртками над libX11.</p></div> <div class="paragraph"><p>Исторически первым тулкитом был Athena3D. Потом были Motif и Tk. Сейчас наиболее распространены GTK+ и Qt (Qt, строго говоря, представляет собой не X11-тулкит, а многоцелевой кроссплатформенный набор библиотек, который может использоваться в качестве X11-тулкита).</p></div> </div> </div> <div class="sect1"> <h2 id="_hello_world_на_gtk">Hello, world на GTK+</h2> <div class="sectionbody"> <div class="paragraph"><p>В качестве примера рассмотрим следующую программу. Она показывает окно с одной кнопкой. При нажатии на эту кнопку появляется сообщение «Hello, world».</p></div> <div class="listingblock"> <div class="content"> <pre><tt><span style="font-weight: bold"><span style="color:#000080;">#include <gtk/gtk.h><br /><br />// This function displays message dialog.<br />// main_window parameter should be set to parent window of the dialog.<br />void message_box (GtkWindow* main_window, gchar *message) {<br /> GtkWidget *dialog, *label, *content_area;<br /> <br /> // Create a dialog<br /> dialog = gtk_dialog_new_with_buttons ("Message",<br /> main_window,<br /> GTK_DIALOG_DESTROY_WITH_PARENT,<br /> GTK_STOCK_OK,<br /> GTK_RESPONSE_NONE,<br /> NULL);<br /> // Create a label<br /> content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));<br /> label = gtk_label_new (message);<br /> <br /> // On "response" signal (it's called when user clicks a button in<br /> // the dialog), destroy the dialog.<br /> g_signal_connect_swapped (dialog,<br /> "response",<br /> G_CALLBACK (gtk_widget_destroy),<br /> dialog);<br /> <br /> // Add a label<br /> gtk_container_add (GTK_CONTAINER (content_area), label);<br /> // Show the dialog<br /> gtk_widget_show_all (dialog);<br />}<br /><br />// Callback for delete-event signal<br />static gboolean delete_event( GtkWidget *widget,<br /> GdkEvent *event,<br /> gpointer data )<br />{<br /> // If return TRUE, window will not be closed.<br /> // This may be used to preven closing window in some situations.<br /> return FALSE;<br />}<br /><br />// Callback for destroy signal<br />static void destroy( GtkWidget *widget,<br /> gpointer data )<br />{<br /> // End main GTK+ event loop<br /> gtk_main_quit ();<br />}<br /><br />// Callback for button click<br />static void hello ( GtkWidget *widget,<br /> gpointer data )<br />{<br /> // "data" parameter represents main window here<br /> message_box(GTK_WINDOW(data), "Hello, world!");<br />}<br /><br />int main( int argc,<br /> char *argv[] )<br />{<br /> GtkWidget *window;<br /> GtkWidget *button;<br /> <br /> // Init GTK+<br /> gtk_init (&argc, &argv);<br /> <br /> // Create main window<br /> window = gtk_window_new (GTK_WINDOW_TOPLEVEL);<br /> <br /> // Set up callbacks for some signals<br /> g_signal_connect (window, "delete-event",<br /> G_CALLBACK (delete_event), NULL);<br /> <br /> g_signal_connect (window, "destroy",<br /> G_CALLBACK (destroy), NULL);<br /> <br /> // Set window borders width<br /> gtk_container_set_border_width (GTK_CONTAINER (window), 10);<br /> <br /> // Create labeled button<br /> button = gtk_button_new_with_label ("Hello World");<br /> <br /> // Set up callback for "clicked" signal of the button.<br /> // Pass main window as second parameter.<br /> g_signal_connect (button, "clicked", G_CALLBACK (hello), (gpointer)window);<br /> <br /> // Pack the button into window<br /> gtk_container_add (GTK_CONTAINER (window), button);<br /> <br /> // Show the button<br /> gtk_widget_show (button);<br /> <br /> // Show the window<br /> gtk_widget_show (window);<br /> <br /> // Run main GTK+ event loop.<br /> gtk_main ();<br /> <br /> return 0;<br />}<br /><br /></span></span></tt></pre></div></div> <div class="paragraph"><p>Собирается эта программа командой вида</p></div> <div class="literalblock"> <div class="content"> <pre><tt>gcc -o gtk-hello $(pkg-config --cflags gtk+-2.0) $(pkg-config --libs gtk+-2.0) gtk-hello.c</tt></pre> </div></div> <div class="paragraph"><p>GTK+ — довольно сложно устроенный набор библиотек, поэтому, чтобы не указывать все нужные библиотеки и опции компилятора вручную, мы используем здесь программу <tt>pkg-config</tt>, которая печатает опции компилятора, нужные для использования GTK+.</p></div> </div> </div> <div class="sect1"> <h2 id="_дополнительная_литература">Дополнительная литература</h2> <div class="sectionbody"> <div class="paragraph"><p>[raymond] Реймонд, Эрик С. Искусство программирования для UNIX. — Пер. с англ. — М.: Издательский дом «Вильямс», 2005. — 544с., ил.</p></div> <div class="paragraph"><p>[kernigan_pike] Керниган Б., Пайк Р. UNIX. Программное окружене. — Пер с англ. — СПб: Символ-Плюс, 2003. — 416с., ил.</p></div> <div class="paragraph"><p>[kernigan_richie] Керниган Б., Ритчи Д. Язык программирования C. — Пер. с англ. — Москва: Вильямс, 2006. — 304 с.</p></div> <div class="paragraph"><p>[vcs_git] Портнов И. Системы контроля версий. Git. URL: <a href="http://iportnov.blogspot.com/2008/06/git.html">http://iportnov.blogspot.com/2008/06/git.html</a></p></div> <div class="paragraph"><p>[los-t_git] Los-t. Git guts (серия статей в ЖЖ). URL: <a href="http://los-t.livejournal.com/tags/git+guts">http://los-t.livejournal.com/tags/git+guts</a></p><p><br /></p></div> </div> </div> </div>Portnovhttp://www.blogger.com/profile/07229550690332111129noreply@blogger.com2tag:blogger.com,1999:blog-3628051270185801752.post-144265723876366262011-03-29T06:57:00.000-07:002011-03-29T07:00:14.597-07:00Изготовление презентаций из Asciidoc с помощью Beamer<p>В одном из недавних постов я говорил, что из Asciidoc-разметки можно сделать много всяких разных выходных форматов. Среди всего прочего, можно делать и презентации. Не слишком «навороченные», правда, но для многих случаев этого хватает.</p><div id="content"><div id="preamble"><div class="sectionbody"> <div class="paragraph"><p>Схема такая:</p></div><a href="http://3.bp.blogspot.com/-iHdrTT_Wf24/TZHlrxLg_NI/AAAAAAAAACM/ao0GTDl7a7E/s1600/presentations.png"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 182px; height: 320px;" src="http://3.bp.blogspot.com/-iHdrTT_Wf24/TZHlrxLg_NI/AAAAAAAAACM/ao0GTDl7a7E/s320/presentations.png" alt="" id="BLOGGER_PHOTO_ID_5589501152642268370" border="0" /></a><br /><br /><div class="paragraph"><p>Сначала из Asciidoc делаем DocBook. Это можно сделать командой вида <tt>asciidoc -b docbook input.txt</tt>. Только вот по такой команде получится файл в формате DocBook 4.5, а моя XSL-таблица понимает только DocBook 5.0+. Поэтому берём конфигурационные файлы asciidoc для изготовления DocBook 5.0, например, <a href="http://gitorious.org/doc-building-system/doc-building-system/trees/master/asciidoc-conf">отсюда</a>, и даём команду вида</p></div> <div class="literalblock"> <div class="content"> <pre><tt>asciidoc -b topic input.txt</tt></pre> </div></div> <div class="paragraph"><p>Получается файл <tt>input.xml</tt>. Теперь берём XSL-таблицу, преобразующую DocBook 5.0+ в TeX-овский исходник, например, <a href="http://gitorious.org/doc-building-system/doc-building-system/blobs/master/xsl/beamer.xsl">здесь</a> (эта таблица заведомо не полная, но для моих задач хватает; кому не хватит — добавляйте туда свои шаблоны :)), и даём команду вида</p></div> <div class="literalblock"> <div class="content"> <pre><tt>xsltproc beamer.xsl input.xml > presentation.tex</tt></pre> </div></div> <div class="paragraph"><p>Ну а теперь компилируем этот исходник командой вида</p></div> <div class="literalblock"> <div class="content"> <pre><tt>xelatex presentation.tex</tt></pre> </div></div> <div class="paragraph"><p>(может понадобиться запустить эту команду несколько раз, чтобы проставились все ссылки и т.п).</p></div> <div class="paragraph"><p>Таким образом сделана, в частности, упомянутая в одном из предыдущих постов <a href="http://lug-mgn.ru/files/march_2011/Presentations/asciidoc_docbook.pdf">презентация</a> (из <a href="http://lug-mgn.ru/files/march_2011/presentation.page">вот такого исходника</a>).</p></div> </div> </div> </div>Portnovhttp://www.blogger.com/profile/07229550690332111129noreply@blogger.com8tag:blogger.com,1999:blog-3628051270185801752.post-68221847880783624902011-03-27T11:15:00.000-07:002011-03-28T08:35:31.908-07:00Об изготовлении EPUB из DocBook<div id="content"> <div id="preamble"> <div class="sectionbody"> <div class="paragraph"><p>Тут не так давно я уже упоминал, что из DocBook можно делать, в том числе, и EPUB. Однако процесс не вполне тривиальный; мне кажется, его стоит расписать несколько подробнее.</p></div> <div class="paragraph"><p><a href="http://www.ibm.com/developerworks/xml/tutorials/x-epubtut/section5.html">Вот тут</a> в IBM DeveloperWorks описан такой процесс. Однако изготовленный по этому рецепту файл будет обладать парой недостатков:</p></div> <div class="ulist"><ul><li> <p> Это будет файл устаревшего формата «Open Epub»; </p> </li><li> <p> В нём не будет внедрённых шрифтов. </p> </li></ul></div> <div class="paragraph"><p>Такой файл благополучно открывается чем-нибудь типа Okular. Однако AdobeViewer на читалках типа PocketBook 301+ в таких файлах показывает русские буквы вопросиками. Это происходит из-за того, что встроенного шрифта в файле нет, AdobeViewer пытается использовать шрифт по умолчанию, а в нём нет русских букв.</p></div> <div class="paragraph"><p>Так что будем делать «более правильный» EPUB. «Нормальный» EPUB-файл — это zip-архив, в котором содержатся следующие вещи:</p></div> <div class="ulist"><ul><li> <p> Файл с именем <tt>«mimetype»</tt>; этот файл обязательно должен идти первым в архиве, не должен быть сжат, и его содержимым должна быть строка <tt>«application/epub+zip»</tt> (без символа новой строки в конце). </p> </li><li> <p> Директория с именем <tt>«META-INF»</tt>; в ней — файл <tt>container.xml</tt>. Примеры содержимого этого файла можно нагуглить, главное что в нём должно быть прописано — ссылка на файл <tt>contents.opf</tt> (<tt>OPS/contents.opf</tt>). </p> </li><li> <p> Директория с именем <tt>«OPS»</tt>; В ней — HTML-файлы с собственно содержимым книги, картинки к ним, шрифты (файлы <tt>*.ttf</tt>), и файл contents.opf и таблица стилей CSS. </p> </li></ul></div> <div class="paragraph"><p>Для того, чтобы сделать такой файл из имеющегося DocBook-файла <tt>book.xml</tt>, нужно проделать следующее:</p></div> <div class="olist arabic"><ol class="arabic"><li> <p> Выполнить команду вида: </p> <div class="literalblock"> <div class="content"> <pre><tt>xsltproc --stringparam epub.oebps.dir OPS/ \<br /> --stringparam epub.embedded.font Ubuntu-R.ttf \<br /> --stringparam html.stylesheet style.css /usr/share/xml/docbook/stylesheet/docbook-xsl/epub/docbook.xsl \<br /> book.xml</tt></pre> </div></div> <div class="openblock"> <div class="content"> <div class="paragraph"><p>Здесь вместо «Ubuntu-R.ttf» можете указать какое-нибудь другое имя шрифтового файла. Путь к <tt>epub/docbook.xsl</tt> на вашей системе может быть и другим, подставьте ваш. Эта команда создаст директори <tt>META-INF</tt> и <tt>OPS</tt> в текущей директории.</p></div> </div></div> </li><li> <p> Скопировать откуда-нибудь или написать таблицу стилей <tt>OPS/style.css</tt>. Стили можно настраивать на ваш вкус, но надо учитывать следующее: </p> <div class="ulist"><ul><li> <p> Если вы предполагаете, что документ будут читать на E-Ink устройстве, то не делайте слишком сложную цветовую гамму; экран мало того что чёрно-белый, так ещё не очень хорошо показывает тонкие оттенки. Идеально было бы обойтись 4мя или 8ю оттенками серого (включая чёрный и белый). </p> </li><li> <p> В таблице должны быть правила, указывающие конкретные шрифты, наподобие следующих: </p> <div class="literalblock"> <div class="content"> <pre><tt>@font-face {<br /> font-family: "Ubuntu";<br /> font-style: normal;<br /> font-weight: normal;<br /> src: url(Ubuntu-R.ttf)<br />}<br /><br />@font-face {<br /> font-family: "Ubuntu";<br /> font-style: normal;<br /> font-weight: bold;<br /> src: url(Ubuntu-B.ttf)<br />}<br /><br />@font-face {<br /> font-family: "Ubuntu";<br /> font-style: italic;<br /> font-weight: normal;<br /> src: url(Ubuntu-I.ttf)<br />}<br /><br />@font-face {<br /> font-family: "Ubuntu";<br /> font-style: italic;<br /> font-weight: bold;<br /> src: url(Ubuntu-BI.ttf)<br />}<br /><br />@font-face {<br /> font-family: "DroidSansMono", monospace;<br /> font-style: normal;<br /> font-weight: normal;<br /> src: url(DroidSansMono.ttf)<br />}<br /><br />pre {<br />white-space: pre-wrap;<br />font-family: "DroidSansMono", monospace;<br />font-size: 85%;<br />}</tt></pre> </div></div> <div class="openblock"> <div class="content"> <div class="paragraph"><p>(имена шрифтов, конечно, укажите ваши).</p></div> </div></div> </li></ul></div> </li><li> <p> Скопировать откуда-нибудь файлы шрифтов (<tt>*.ttf</tt>) в директорию <tt>OPS/</tt>. </p> </li><li> <p> Исправить файл <tt>OPS/content.opf</tt>, добавив туда ссылки на шрифты. Это делается строчками вида: </p> <br /><div class="literalblock"> <div class="content"> <pre><tt><br /><item id="font2" href="Ubuntu-I.ttf" type="application/x-font-ttf"/><br /></tt></pre> </div></div> <div class="openblock"> <div class="content"> <div class="paragraph"><p>в разделе <tt><manifest></manifest></tt> для каждого файла шрифта. Значения параметра <tt>id</tt> ни на что не влияют и могут быть любыми, лишь бы не повторялись.</p></div> </div></div> </li><li> <p> Собрать собственно EPUB-файл командами вида </p> <div class="literalblock"> <div class="content"> <pre><tt>$ echo "application/epub+zip" > mimetype<br />$ zip -0Xq my-book.epub mimetype<br />$ zip -Xr9D my-book.epub META-INF OPS</tt></pre> </div></div> </li><li> <p> (Опционально) Проверить получившийся файл при помощи программы <a href="http://code.google.com/p/epubcheck/">epubcheck</a> командой вида </p> <div class="literalblock"> <div class="content"> <pre><tt>java -jar epubcheck.jar my-book.epub</tt></pre> </div></div> </li></ol></div> <div class="paragraph"><p>Получившийся файл <tt>my-book.epub</tt> должен нормально читаться как на компьютере, так и на книгочиталках.</p></div> </div> </div> </div>Portnovhttp://www.blogger.com/profile/07229550690332111129noreply@blogger.com0tag:blogger.com,1999:blog-3628051270185801752.post-12459442482577844092011-03-19T10:20:00.000-07:002011-03-19T10:21:40.047-07:00Небольшое разъяснение по комментариямВ разных постах в комментариях товарищи меня корректируют — где-то я ошибся, где-то что-то перепутал. Я специально не вношу изменения в пост — чтобы было видно, что вот тут я ошибался, а вот тут меня исправили.Portnovhttp://www.blogger.com/profile/07229550690332111129noreply@blogger.com2tag:blogger.com,1999:blog-3628051270185801752.post-58031252122696139702011-03-19T08:30:00.000-07:002011-07-28T21:02:59.252-07:00Подготовка технической документации с использованием asciidoc и DocBook<p><em>Это мой конспект для выступления на <a href="http://lug-mgn.ru/articles/materialy-seminara-1903">семинаре</a>, проводившемся нашей LUG. См. также <a href="http://lug-mgn.ru/files/march_2011/Presentations/asciidoc_docbook.pdf">презентацию</a> к докладу.</em></p><br /><br /><div class="chapter"><div class="titlepage"><div><div><h2 class="title">Глава 1. Актуальность, цели и задачи</h2></div></div></div><p>Вобще говоря, разработка технической документации — это достаточно обширная область. Техническая документация, по идее, прилагается чуть ли не ко всему подряд — инструкция к чайнику, проектная документация к мосту, мануал к программе. Конечно, в разных случаях аудитория у документов разная, разные и цели подготовки документации, а потому используются разные подходы. Я буду говорить в основном о подготовке документации к программным продуктам, хотя практически всё это применимо и к разработке других видов документации (хотя, вероятно, предлагаемой методики недостаточно).</p><p>Целью разработки документации к программному продукту является предоставление пользователю достаточно полной информации о продукте в том виде, в котором это удобно пользователю. Отсюда вытекают следующие задачи:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"> Полная документация к большому продукту будет большой. Поэтому необходимо обеспечить автору документации простые и удобные средства для её подготовки, стараться не тратить время автора на посторонние вещи типа оформления этой документации или тонкостей xml-разметки; </li><li class="listitem">Пользователи у продукта, возможно, будут разные, а потому документацию необходимо предоставлять в разных форматах; самые популярные форматы — PDF, HTML и CHM; </li><li class="listitem">Так как пользователи разные, разной должна быть комплектация документации. В одних случаях нужно только руководство пользователя, в других — только руководство программиста, в третьих — и то, и другое. </li></ul></div><p>Ниже приводится обзор различных технологий, используемых для подготовки технической документации, с оценкой их соответствия поставленным задачам, а затем предлагается собственная методика использования существующих технологий (с использованием свободного ПО).</p></div><div class="chapter" title="Глава 2. Обзор технологий"><div class="titlepage"><div><div><h2 class="title">Глава 2. Обзор технологий</h2></div></div></div><p>Я начну с краткого обзора технологий, которые применяются для подготовки технической документации. Наиболее известны следующие технологии:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"> MS Word и подобные системы; </li><li class="listitem"><p class="simpara"> Системы «единого источника»: </p><div class="itemizedlist"><ul class="itemizedlist" type="circle"><li class="listitem"> Help&Manual и аналоги; </li><li class="listitem"> DocBook; </li><li class="listitem"> DITA; </li><li class="listitem"> и другие. </li></ul></div></li></ul></div><p>Рассмотрим некоторые из этих технологий несколько подробнее.</p></div><div class="chapter" title="Глава 3. Help&Manual"><div class="titlepage"><div><div><h2 class="title">Глава 3. Help&Manual</h2></div></div></div><p>Главным плюсом этой системы часто является то, что она уже внедрена и работает. Также несомненный плюс — это система единого источника, т.е. из одного текста можно получить документ в разных форматах. Особенность этой программы и аналогов: это WISIWYG-системы. Насчёт того, является ли эта особенность плюсом или минусом, по сей день не утихают споры. Плюс WISIWYG-технологий очевиден, он заложен в названии: можно сразу видеть, как будет выглядеть текст в результате. К минусам WISIWYG относят следующие пункты:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"> Есть такое высказывание: если система предоставляет возможность что-нибудь настраивать, она, фактически, заставляет вас это настраивать. В данном случае это относится к тому, что автор тратит время не только на написание текста, но и на его оформление, хотя это, по идее, не его задача. </li><li class="listitem">Такие системы склонны порождать совершенно дикую и нечитаемую разметку при конвертировании в HTML, LaTeX и другие подобные форматы. Например, легко можно получить разметку наподобие: <code class="literal"><b>разм</b><b>етка</b></code>. Это сказывается, как только появляется необходимость поправить такую разметку «руками». </li><li class="listitem"> При работе в таких системах люди склонны использовать не логическую, а физическую разметку (например, помечать кусок текста жирным шрифтом, вместо того чтобы отмечать этот текст как заголовок). Что интересно, большинство таких систем предоставляют возможность использования логической разметки, иногда даже удобную, но по каким-то психологическим соображениям люди не склонны её использовать. Это приводит к проблемам при изменении общего стиля оформления документа, а также зачастую при конвертировании в другие форматы. </li></ul></div><p>В связи с последним пунктом можно упомянуть системы с похожим девизом — WISIWYM (What You See Is What You Mind). Таких систем значительно меньше. К ним относится, например, LyX (<a class="ulink" href="http://www.lyx.org/" target="_top">http://www.lyx.org</a>). Такие системы вобще не позволяют использовать физическую разметку. LyX, например, не позволит вам поставить два пробела или два разрыва строки подряд.</p><p>Известные минусы H&M и аналогов:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"> Закрытый бинарный формат файлов; впрочем, это исправляется в новой версии H&M, где используется XML-формат; </li><li class="listitem"> Следствие: для просмотра и редактирования нужна сама программа H& M; </li><li class="listitem"> Все эти системы платные; </li><li class="listitem"> Нет средств для управления версиями документации; </li><li class="listitem"> Нет средств для обеспечения одновременной работы нескольких человек над одним документом. </li></ul></div><p>Наш отдел документации, видимо, сравнительно небольшой. Важнее, на самом деле, что над одним документом у нас работает 1-2 человека - писатель и переводчик. Поэтому пока что отсутствие средств для управления версиями нам не слишком мешает.</p></div><div class="chapter" title="Глава 4. DocBook"><div class="titlepage"><div><div><h2 class="title">Глава 4. DocBook</h2></div></div></div><p>DocBook — это формат для ведения технической документации, основанный на XML. Разработка DocBook началась в 1991 году и, в разное время, развивался и поддерживался этот формат различными организациями:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"> 1991—1994: HaL Computer Systems и O’Reilly & Associates (+ большое влияние Novell и Digital); </li><li class="listitem"> 1994—1998: Davenport Group (+ большое влияние Novell и Sun); </li><li class="listitem"> 1998—2007: OASIS. DocBook XML v4.5; </li><li class="listitem"> 2 Aug 2008: OASIS. DocBook XML v5.0. </li></ul></div><p>Раньше поддерживалась версия DocBook, основанная на SGML вместо XML (DocBook/SGML). SGML более «дружелюбен», более приспособлен к написанию человеком. Например, некоторые теги можно не закрывать, некоторые можно даже и не открывать. Это свойство досталось в наследство от SGML в HTML. Если в HTML-файле написать</p><br /><br /><pre class="literallayout">Привет мир! <a href=http://site.com/>site.com</a></pre><br /><br /><p>(это полное содержимое файла), то браузер обязан интерпретировать это так же, как код</p><br /><br /><pre class="literallayout"><html><br /><header></header><br /><body><br /><p>Привет мир! <a href="http://site.com/">site.com</a></p><br /></body><br /></html><br /></pre><p>Сразу видно, что это позволяет значительно упростить разметку. Однако это делает программы для обработки SGML гораздо более сложными и неустойчивыми в работе. До сих пор не существует программ, разбирающих любую разновидность SGML!</p><p>Так что сейчас DocBook/SGML не поддерживается, поддерживается только DocBook/XML.</p><p>К плюсам этой технологии можно отнести:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"> Не только открытый, но и стандартный формат, широко используемый как в OpenSource, так и в коммерческих компаниях; </li><li class="listitem"> Существующие инструменты позволяют преобразовывать DocBook во все распространённые форматы (HTML, CHM, PDF итп); </li><li class="listitem"> За счёт открытости и стандартности возможно реализовать преобразование в любой другой формат; </li><li class="listitem"> Формат очень полный; в спецификации предусмотрены, например, специальные теги для сочетаний клавиш, для пунктов меню, и т.п; </li><li class="listitem"> Формат можно расширять, дописывая собственные DTD; </li><li class="listitem"> Это XML, а для работы с XML существует огромное количество инструментов и технологий; </li><li class="listitem"> Т.к. исходные файлы текстовые, их можно держать под управлением системы контроля версий. </li></ul></div><p>К минусам я бы отнёс:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"> Высокий порог вхождения; нужно знать XML и саму спецификацию DocBook; </li><li class="listitem"> Нетривиальная настройка вывода в HTML, PDF и т.п; Например, для вывода в PDF используется либо промежуточный вывод в TeX (так что для настройки нужно знать TeX), либо промежуточный вывод в XSL-FO (так что для настройки нужно знать XSL-FO); </li><li class="listitem"> Системы контроля версий не так уж хорошо приспособлены к хранению XML-файлов. </li></ul></div><p>Менее очевидным минусом является малое количество WISYWIG-программ для редактирования DocBook (но они есть, из бесплантых — Serna, разрабатываемая нашими соотечественниками).</p><p>На мой личный взгляд, XML не приспособлен для написания и чтения человеком «вручную». Тем не менее, я знаю как минимум одного технического писателя, благополучно пишущего большие документы в DocBook именно «вручную».</p></div><div class="chapter" title="Глава 5. DITA"><div class="titlepage"><div><div><h2 class="title">Глава 5. DITA</h2></div></div></div><p>Некоторыми (особенно в США) формат DocBook рассматривается как устаревающий. На смену ему приходит технология DITA. Это также формат, основанный на XML. Однако базовый формат определяет только основные теги, которые используются в любом документе. Все более специфичные теги определяются в так называемых специализациях DITA с помощью деклараций DTD. Существует несколько стандартных специализаций. Предполагается, что каждый пишет специализацию для своих нужд.</p><p>DITA содержит следующие нетривиальные идеи:</p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"> Текст пишется во многих небольших файлах, называемых топиками (topics). Каждый топик — это логически завершённый кусок документа. </li><li class="listitem">Эти топики объединяются в выходной документ согласно содержимому других файлов — карт документа. Таким образом, получается, что текст отделён от структуры документации, текст и структуру можно менять независимо. </li><li class="listitem">Каждый топик имеет определённый тип (doctype в смысле DTD), и структура топика определяется его типом. Стандартные типы — Задача, Концепция и Справочник. Например, топик типа Задача описывает последовательность шагов, необходимых для выполнения задачи. Топик типа Справочник содержит перечень элементов и их описаний, и т.д. Благодаря специализации всегда можно добавить свои типы топиков. </li></ol></div><p>Плюсы:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"> БОльшая, даже по сравнению с DocBook, гибкость формата; </li><li class="listitem"> И все плюсы DocBook. </li></ul></div><p>Минусы:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"> По сравнению с DITA Docbook рассматривается как формат для чайников; Как минимум, здесь надо знать не только XML, а ещё и DTD; </li><li class="listitem"> Т.к. формат ещё относительно новый, он пока слабее поддерживается; </li><li class="listitem"> Тот же минус, что у DocBook: не лучшая поддержка XML со стороны систем контроля версий; </li><li class="listitem"> Тот же минус, что у DocBook: сложность настройки вывода в PDF, по тем же причинам. </li></ul></div><p>Про бесплатные WISIWYG-редакторы для DITA я ещё вобще не слышал.</p></div><div class="chapter" title="Глава 6. Wiki-подобные разметки"><div class="titlepage"><div><div><h2 class="title">Глава 6. Wiki-подобные разметки</h2></div></div></div><p>Чтобы не писать в XML, широко используются различные Wiki-подобные разметки. Их очень много. Практически каждый wiki-движок предлагает свой язык разметки. Наиболее известны разметки MediaWiki (используется википедией) и dokuwiki.</p><p>Не все wiki-разметки поддерживают все средства форматирования, многие ограничены. Разметка MediaWiki поддерживает практически любое форматирование, но выглядит такая разметка страшновато. Dokuwiki выглядит более читаемо, но не поддерживает некоторое сложное форматирование.</p><p>Среди таких разметок известна ещё разметка Asciidoc. asciidoc - это скрипт на python, преобразующий соответствующую разметку в несколько выходных форматов. Есть возможность определить «бэкенды» для вывода в какие-нибудь ещё форматы. Достаточно хорошо поддерживаются выходные форматы HTML и DocBook. DocBook, в свою очередь, можно преобразовать практически во что угодно.</p><p>Существует программа (и библиотека) pandoc, предназначенная для конвертации различных форматов разметки. Например, с помощью этой программы можно преобразовать RST в Markdown, HTML или MediaWiki, или HTML в Markdown, и т.п. Эта библиотека достаточно гибкая, к ней можно писать фильтры — readers и writers. Я поддерживаю reader и writer для Asciidoc.</p><p>Плюсы такой технологии:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"> Меньший порог вхождения, чем у XML-технологий; </li><li class="listitem"> Системы контроля версий лучше всего работают как раз с plain text файлами; </li><li class="listitem"> При использовании DocBook в качестве промежуточного формата получаем все плюсы технологии DocBook; </li><li class="listitem"> В принципе, можно использовать DITA вместо DocBook в качестве промежуточного формата; </li><li class="listitem"> За счёт наличия конвертеров всегда есть возможность уйти с этой технологии на какую-то другую. </li></ul></div><p>Минусы:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"> Порог вхождения всё-таки есть, хоть и небольшой; </li><li class="listitem"> Из-за использования DocBook в качестве промежуточного формата получаем минус DocBook — сложность настройки вывода в PDF. </li></ul></div></div><div class="chapter" title="Глава 7. Применение для совместной работы"><div class="titlepage"><div><div><h2 class="title">Глава 7. Применение для совместной работы</h2></div></div></div><p>В основном здесь рассматривается использование web-интерфейса для работы с документами. Однако надо заметить, что это, не единственный вариант. Как минимум не стоит забывать про возможность использования систем контроля версий для совместной работы над документами.</p><p>Сначала я стал рассматривать возможность использования уже имеющейся веб-платформы — Confluence. Её, в принципе, можно использовать для разработки документации, но:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"> Своеобразная разметка; это тоже wiki-разметка, но уникальная, никто кроме confluence её не поддерживает; </li><li class="listitem"> Слабая поддержка импорта: практически есть возможность только импортировать doc-файлы; </li><li class="listitem"> Слабая поддержка экспорта: confluence может экспортировать pdf, но так, что этот вариант не многим лучше распечатки веб-страницы на pdf-принтере; другие форматы вывода не поддерживаются; </li><li class="listitem">Сами тексты хранятся в БД, и в случае, если придётся переходить на другую технологию, тексты придётся как-то нетривиально добывать из БД. </li></ul></div><p>Я давно использую Asciidoc, поэтому стал искать wiki-движки, которые бы поддерживали asciidoc. Как оказалось, среди огромного количества вики-движков только единицы поддерживают больше одного формата разметки. Единственный движок, поддерживающий Asciidoc — это Gitit, и как раз за счёт использования библиотеки pandoc. Кроме того, Gitit хранит тексты не в БД, а в текстовых файлах. Для контроля версий может использоваться одна из нескольких систем контроля версий (Git, Mercurial, Darcs). Так что если открыть доступ к этому репозиторию по сети, то получаем возможность редактирования содержимого вики с локального компьютера, любимым текстовым редактором, без веб-форм и вообще без соединения по сети (сеть нужна только чтобы взять содержимое и положить его обратно).</p><p>Gitit использует для обеспечения совместного редактирования достаточно простую модель. Предположим, один человек (A) начал редактировать страницу, затем (не дожидаясь пока A закончит) её начал редактировать Б. В таком случае, если изменения, внесённые двумя людьми, не пересекаются (например, они редактировали разные части страницы), будут применены все изменения. Иначе тому, кто закончит последним, будет предложено разрешить конфликты вручную.</p><p>Системы контроля версий используют примерно ту же модель, но могут обеспечивать бОльшую гибкость: см., например, git merge vs git rebase.</p></div><div class="chapter" title="Глава 8. Об импорте и экспорте"><div class="titlepage"><div><div><h2 class="title">Глава 8. Об импорте и экспорте</h2></div></div></div><p>Собственно, об экспорте я кратко уже упомянул. Программа pandoc позволяет конвертировать документы в разметке asciidoc в несколько распространённых форматов, включая docbook. В свою очередь, docbook можно сконвертировать во что угодно, даже в xml-формат последней версии H&M.</p><p>В случае необходимости можно преобразовать имеющиеся документы в формат asciidoc. В случае с исходными документами в формате H&M используемой, 4й версии, для этого придётся:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"> Конвертировать исходный hmx файл в xml-формат новой версии H&M, используя конвертер, поставляемый с этой новой версией; </li><li class="listitem"> С помощью небольшого скрипта и XSL-таблицы преобразовать полученный набор xml-файлов в один xml-файл docbook; </li><li class="listitem"> С помощью ещё одной XSL-таблицы преобразовать docbook в asciidoc. </li></ul></div><p>При этом возникают мелкие недочёты, связанные с особенностями работы H&M. Например, в его xml-файлах встречается разметка вида:</p><br /><pre class="literallayout">слово слово<strong> выделено </strong>текст...</pre><br /><p>или даже</p><br /><pre class="literallayout">слово <strong></strong> дальше текст...</pre><br /><p>Такая разметка, конечно, превращается в не вполне корректную asciidoc-разметку. Но это, вообще говоря, мелочи, и исправляется "поиском и заменой" в текстовом редакторе.</p></div><div class="chapter" title="Глава 9. Улучшение поддержки DocBook"><div class="titlepage"><div><div><h2 class="title">Глава 9. Улучшение поддержки DocBook</h2></div></div></div><p>Для обеспечения более полной поддержки DocBook я определил в своём конфигурационном файле Asciidoc дополнительные макросы:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"> <code class="literal">icon:image.png[]</code> для вставки иконок; </li><li class="listitem"> <code class="literal">button:button.png[Текст кнопки]</code> для упоминаний кнопок; </li><li class="listitem"> <code class="literal">label:[Текст]</code> для заголовков элементов управления; </li><li class="listitem"> <code class="literal">screenshot::screen.png[]</code> для больших скриншотов. </li></ul></div></div><div class="chapter" title="Глава 10. Настройка вывода в PDF"><div class="titlepage"><div><div><h2 class="title">Глава 10. Настройка вывода в PDF</h2></div></div></div><p>Существует несколько способов преобразовать DocBook в PDF. Наиболее распространены два:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"> С использованием XSL-FO в качестве промежуточного формата; </li><li class="listitem"> С использованием LaTeX в качестве промежуточного формата. </li></ul></div><p>XSL-FO — это ещё один XML-формат для представления форматированного текста. В отличие от DocBook, этот формат ориентирован на низкоуровневую физическую разметку. Преобразовать DocBook в XSL-FO несложно, для этого есть свободно доступные XSL-таблицы. Преобразованием XSL-FO в PDF занимаются специальные программы, называемые XSL-FO Processors. Их существует несколько:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"> Apache FOP (бесплатный, но не поддерживает некоторых возможностей и имеет некоторые сложности с настройкой русскоязычного вывода); </li><li class="listitem"> JadeTeX (специальная версия TeX, настроенная так, что понимает xml на входе); </li><li class="listitem"> PassiveTeX (что-то из той же серии); </li><li class="listitem"> XEP (коммерческий продукт, полностью функциональный, но опять же есть сложности с русским языком). </li></ul></div><p>Собственно, различные сложности с русским языком есть у всех этих вариантов (они всегда решаемы, но это требует времени). Разработка JadeTeX и PassiveTeX, к тому же, на данный момент заморожена.</p><p>Другой вариант преобразования — использовать в качестве промежуточного формата TeX. Для преобразования DocBook в TeX используется программа dblatex, представляющая собой скрипт на Python и набор XSL-таблиц. Полученный TeX-файл, в зависимости от настроек dblatex, должен быть затем обработан LaTeX, PDFLaTeX или XeLaTeX. Я использую XeLaTeX. Он имеет следующие преимущества:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"> Полная поддержка Unicode (те, кто работал с «традиционными» версиями TeX, знают, что поддержка Unicode в них реализована с помощью хитроумных хаков); </li><li class="listitem">XeLaTeX использует шрифтовую подсистему операционной системы (GDI на Windows, Freetype на Linux) вместо специфической и местами устаревшей шрифтовой подсистемы TeX. В частности, доступны все установленные в системе шрифты; </li><li class="listitem"> Формат вывода по умолчанию — PDF. </li></ul></div><p>Сгенерированные dblatex документы используют специальный стиль, поставляемый в комплекте dblatex. Его, конечно, можно модифицировать. XSL-таблицы из комплекта тоже можно настроить под свои задачи.</p></div><div class="chapter" title="Глава 11. Вывод в CHM"><div class="titlepage"><div><div><h2 class="title">Глава 11. Вывод в CHM</h2></div></div></div><p>Вывод в CHM поддерживается «стандартным» набором DocBook-XSL. Правда, для полноценной поддержки русского языка надо добавить следующие параметры к xsltproc:</p><pre class="literallayout">xsltproc -stringparam chunker.output.encoding windows-1251 \<br /> -stringparam htmlhelp.encoding windows-1251 -stringparam chunker.output.indent yes</pre><p>и в получившемся файле <code class="literal">htmlhelp.hhp</code> указать русский язык:</p><pre class="literallayout">sed -i -e 's/0x0409.*/0x0419 Russian (RUSSIA)/' chm/htmlhelp.hhp</pre><p>Компилятор CHM (<code class="literal">hhc.exe</code>) из комплекта MS HTML Help Workshop благополучно запускается под Wine, достаточно «подсунуть» ему windows-библиотеки <code class="literal">mfc40.dll</code> и <code class="literal">itss.dll</code>.</p></div><div class="chapter" title="Глава 12. Поддержка составных документов"><div class="titlepage"><div><div><h2 class="title">Глава 12. Поддержка составных документов</h2></div></div></div><p>Поддержку документов, состоящих из многих файлах, в рассматриваемой схеме можно реализовать, как минимум, двумя способами. Во-первых, asciidoc сам поддерживает директиву include, работающую подобно директиве #include препроцессора C. Устанавливая значение параметра <code class="literal">leveloffset</code> до и после директивы include, можно «сдвинуть» все заголовки во включаемом файле (например, все заголовки первого уровня во включаемом файле будут считаться заголовками второго уровня, и т.п.). Однако для документов с большой степенью вложенности это не очень удобно, легко запутаться в значениях параметра <code class="literal">leveloffset</code> (осбоенно в случае, если нужно делать несколько вариантов документации в разной комплектации).</p><p>Другой вариант — использовать XInclude уже после преобразования документов в DocBook/XML. Этот вариант более гибкий и удобный. Например, можно включать не весь файл целиком, а только нужные секции. Для этого пишется ещё один XML-файл, называемый мастер-документом, в котором указана мета-информация (заголовок и т.п.), а также структура документа и набор тегов <code class="literal"><xi:include></xi:include></code>, включающих нужные разделы других документов. Но такой XML содержит мало полезной информации и много служебной разметки. Чтобы не писать мастер-файл в XML руками, я использую небольшой скрипт, делающий этот файл из YAML-файла вида</p><pre class="literallayout">title: Заголовок составного документа<br />author: Ilya V. Portnov<br />date: September 2010<br />contents: # Содержание<br />- programmer: # Здесь будут включены разделы из файла programmer.xml<br />- used_abbrevs # \<br />- add_docs # }- Список разделов из programmer.xml<br />- sys_reqs # /<br />- part: # Здесь будет сформирована часть документа<br />Концепция: # с заголовком part<br />- concept: # Внутрь этой части будут помещены разделы из concept.xml<br />- concept_goals # ...<br />- concept_components<br />- technologies<br />- concept_features<br /># Здесь включаются все главы, являющиеся дочерними<br /># по отношению к элементу 'general_descr'<br />- part:<br />Руководство программиста. общее описание:<br />- programmer: [ xpointer(id('general_descr')/chapter) ]<br />- part:<br />...<br />Руководство администратора:<br />- admin:<br />- _radixware_starter<br />- _radixware_server<br />- _radixware_manager<br />- user: [ _radixware_explorer ]</pre><p>Таким образом получается подход, близкий к подходу DITA. Текст хранится в отдельных топиках и объединяется согласно картам документов (в формате YAML). При этом получаем даже дополнительную гибкость по сравнению с DITA, т.к. в YAML-карте документа упоминаются не только имена страниц, а и идентификаторы конкретных секций. Благодаря этому можно, например, переставить разделы в выходном документе, не меняя страниц, а меняя только карту документа.</p></div><div class="chapter" title="Глава 13. Профилирование"><div class="titlepage"><div><div><h2 class="title">Глава 13. Профилирование</h2></div></div></div><p>Профилированием документации называют подготовку документа, содержащего только разделы, предназначенные для данной аудитории. Например, если заказчик не покупает определённый модуль программного продукта, то и документация по этому модулю ему не нужна. Часть разделов специфична для той или иной ОС или аппаратной архитектуры, в готовом документе должны быть только разделы, относящиеся к тому программному и аппаратному обеспечению, которое имеется у заказчика. И т.д.</p><p>DocBook предусматривает атрибуты тегов для профилирования изначально. Их поддержка в asciidoc добавляется редактированием конфигурационного файла. Например, чтобы пометить абзац как относящийся только к ОС Windows, нужно перед абзацем поместить строку <code class="literal">[os="windows"]</code>.</p><p>Обычно для каждой задачи требуется профилирование сразу по нескольким переменным. Например, у данного заказчика OS Linux, архитектура процессора x86_64, и т.п. Чтобы не указывать каждый раз все параметры вручную, я пишу файл profiles.yaml вида</p><pre class="literallayout">me:<br />os: any<br />audience: author<br />review:<br />os: any<br />audience: reviewer<br />some_customer:<br />os: linux<br />arch: x86_64</pre><p>Здесь каждой цели профилирования сопоставлен набор пар (имя, значение), по которым должно производиться профилирование. Скрипту нужно только указать цель профилирования в качестве аргумента командной строки.</p><p>Кроме того, поддерживаются «профили по умолчанию» для отдельных документов. Эти профили указываются в файле <code class="literal">*.ymap</code>. Параметры профилирования, заданные при сборке, перекрывают параметры, заданные для документа по умолчанию.</p></div><div class="chapter" title="Глава 14. История изменений"><div class="titlepage"><div><div><h2 class="title">Глава 14. История изменений</h2></div></div></div><p>Часто регламент требует, чтобы в начале документа присутствовала таблица, описывающая историю версий документа. Ведение такой таблицы автоматизируется благодаря тому, что для хранения документации используется git. Специальный python-скрипт анализирует вывод команды <code class="literal">git log</code> для каждой страницы и ищет сообщениях коммитов пометку <code class="literal">[MAJOR]</code> (таким образом, в сводную таблицу попадут только действительно важные изменения). Этот же скрипт пытается получить из таких сообщений версию продукта, к которой относится изменение (ищет сразу после пометки <code class="literal">[MAJOR]</code> строку вида <code class="literal">PRODUCT: TXRBS-1.1.1.6</code>). Затем другой скрипт сводит таблицы изменений для отдельных страниц в одну таблицу для всего документа и для каждого изменения указывает список изменившихся разделов.</p></div><div class="chapter" title="Глава 15. Внешние ссылки"><div class="titlepage"><div><div><h2 class="title">Глава 15. Внешние ссылки</h2></div></div></div><p>Обычно комплект документации состоит не из одного документа, а из нескольких. И появляется необходимость делать ссылки между документами. Главная тонкость тут в том, что при разных вариантах сборки документации одна и та же страница может оказаться в разных документах, поэтому нужны какие-то действия, чтобы определять, на какой файл ставить ссылку в выходном документе.</p><p>Эта задача у меня решается следующим образом. Во всех ссылках указываются только идентификаторы целей. При сборке xsl-стили автоматически делают внешними те ссылки, которые ссылаются на идентификаторы, определённые не в собираемом документе. В случае, когда все связанные документы описаны в одном ymap-файле, на этом этапе сборки все документы существуют в виде одного большого xml-документа (правда, он целиком никогда не формируется в виде файла, а только передаётся между скриптами через unix pipes). В такой ситуации не составляет труда с помощью xsl выяснить, какой идентификатор в каком документе определён.</p><p>В случаях же, когда некоторые из документов, на которые указывают ссылки из собираемого документа, описаны другими ymap-файлами, эти другие ymap-файлы перечисляются в собираемом ymap-файле в специальном разделе <code class="literal">external-documents</code>. Скрипт, преобразующий ymap в xml-документ, читает ymap-файлы, на которые ссылается данный, и в выходном xml-документе формирует тег вида</p><pre class="literallayout"> <externs><br /><document name="another_document"><br /> <title>Руководство администратора</title><br /> <pointer target="target1"/><br /> <pointer target="target2"/><br /> ...<br /></pointer><br /><document name="another_document_2"><br /> ...<br /></document><br /></externs></pre><br /><p>Таким образом, в xml-документе опять же оказывается определено, какие идентификаторы в каких документах определены, и xsl-стиль может правильно расставить внешние ссылки.</p></div><div class="chapter" title="Глава 16. Полная схема сборки"><div class="titlepage"><div><div><h2 class="title">Глава 16 Полная схема сборки</h2><br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/-JfsHbKOIx4w/TYTPTKqzZuI/AAAAAAAAACE/PtNkMDpCjy0/s1600/make.png"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 274px;" src="http://2.bp.blogspot.com/-JfsHbKOIx4w/TYTPTKqzZuI/AAAAAAAAACE/PtNkMDpCjy0/s320/make.png" alt="" id="BLOGGER_PHOTO_ID_5585817366034605794" border="0" /></a><br /><br /></div></div></div></div>Portnovhttp://www.blogger.com/profile/07229550690332111129noreply@blogger.com4tag:blogger.com,1999:blog-3628051270185801752.post-60842343557817062752010-10-19T07:17:00.000-07:002010-10-19T07:18:16.244-07:00Haskell monads для физматовца. Краткое введение<div id="content"> <div id="preamble"> <div class="sectionbody"> <div class="paragraph"><p>Ну что, попробую пополнить ряды haskell newbies ;)</p></div> <div class="paragraph"><p>Я не уверен, что данная заметка сделает концепцию монад понятнее для профессиональных программистов на чём-нибудь типа Java. Но я надеюсь, что она поможет людям с некоторым математическим бэкграундом. Но даже им, думаю, эта заметка поможет в практическом программировании только в сочетании с другими monad tutorials.</p></div> <div class="paragraph"><p>АФАИК, во многих наших провинциальных вузах на физмат-специальностях теория категорий (вместе с современной теорией множеств) игнорируется полностью (т.е. о её существовании за 4-5-6 лет ни разу не упоминают даже, как это было у меня). Я не собираюсь излагать всю теорию — кому нужно, смотрите <a href="http://www.ozon.ru/context/detail/id/3250036/">книжки</a> или хотя бы <a href="http://ru.wikipedia.org/wiki/%D0%A2%D0%B5%D0%BE%D1%80%D0%B8%D1%8F_%D0%BA%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D0%B9">википедию</a>. Я изложу только то, что нужно для нашего применения.</p></div> </div> </div> <h2 id="_">Категории</h2> <div class="sectionbody"> <div class="paragraph"><p>Итак. Вводится понятие <strong>категории</strong>. Это некая очень абстрактная сущность (абстрактнее даже, чем множество). В некотором смысле, категория состоит из двух вещей: набора объектов и набора морфизмов. Относительно этих объектов и морфизмов выполняются какие-то там аксиомы. Из них следует, что категорию можно представлять в виде <a href="http://ru.wikipedia.org/wiki/%D0%93%D1%80%D0%B0%D1%84_%28%D0%BC%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0%29">направленного графа</a>, где вершины — это объекты категори, а дуги — это морфизмы категории. Существенно, что у морфизма, как у дуги графа, есть «начало» и «конец» (называемые домен и кодомен), и это объекты той же категории. Домен и кодомен морфизма (начало и конец дуги графа) могут совпадать. Такой морфизм называется эндоморфизмом.</p></div> <div class="paragraph"><p>Классический пример категории — Set. Объекты категории Set — это всевозможные множества, а морфизмы — это функции между этими множествами.</p></div> <div class="paragraph"><p>Более интересный для нас пример: категория Hask. Здесь объекты — это типы данных, возможные в языке Haskell, а морфизмы — функции языка Haskell.</p></div> </div> <h2 id="__2">Функторы</h2> <div class="sectionbody"> <div class="paragraph"><p>Следующее понятие: <strong>функтор</strong>. Функтор — это, условно говоря, отображение одной категории в другую. При этом функтор отображает объекты первой категории в объекты второй, а морфизмы первой — в морфизмы второй категории. К тому же накладываются определённые ограничения — аксиомы.</p></div> <div class="paragraph"><p>Если функтор отображает категорию саму в себя, такой функтор называется <strong>эндофунктором</strong>.</p></div> <div class="paragraph"><p>Возьмём нашу категорию Hask. Любой полиморфный тип данных языка Haskell с одним тИповым аргументом задаёт отображение, сопоставляющему каждому объекту категории Hask (т.е. типу языка Haskell) какой-то другой объект (другой тип). Например, конструктор типов [] сопоставляет любому типу a тип [a] (список элементов типа a). Конструктор типов Maybe сопоставляет каждому типу a другой тип — Maybe a (значение типа a или никакого значения). Таким образом, мы можем привести много примеров отображений класса объектов категории Hask в объекты той же категории. Пусть, например, у нас есть конструктор типов C (т.е. где-то написано <tt>data C a = …</tt>).</p></div> <div class="paragraph"><p>Если теперь такое отображение объектов (конструктор типов C с одним параметром) дополнить отображением морфизмов, то получим функтор, действующий из Hask в Hask (говорят «эндофунктор на категории Hask»). Напомню, морфизм категории Hask между объектами (типами) a и b — это любая функция типа a → b. Согласно аксиомам функторов, морфизм между объектами a и b должен отображаться в морфизм между объектами (C a) и (C b). Таким образом, отображение морфизмов должно быть функцией следующего вида:</p></div> <div class="listingblock"> <div class="content"> <pre><tt> fmap :: (a -> b) -> (C a -> C b)</tt></pre> </div></div> <div class="paragraph"><p>В модуле Prelude определён класс типов Functor:</p></div> <div class="listingblock"> <div class="content"> <pre><tt> class Functor f where<br /> fmap :: (a -> b) -> (f a -> f b)</tt></pre> </div></div> <div class="paragraph"><p>Итак, любой тип, являющийся экземпляром класса Functor, является эндофунктором на категории Hask (т.е., название класса Functor не вполне точное, его бы следовало назвать, скажем, HaskEndoFunctor). При этом сам конструктор типов задаёт отображение объектов (типов), а связанная с ним функция fmap задаёт отображение морфизмов (функций).</p></div> </div> <h2 id="__3">Моноиды</h2> <div class="sectionbody"> <div class="paragraph"><p><strong>Моноид</strong> — это термин несколько из другой (хоть и смежной) области — из абстрактной алгебры. Моноид определяется следующими вещами:</p></div> <div class="ulist"><ul><li> <p> Множество M; </p> </li><li> <p> Бинарная операция ⊕ на этом множестве; от неё требуется ассоциативность; </p> </li><li> <p> Нейтральный элемент ε этой операции, входящий в множество (т.е. такой, что (∀a ∈ M) ε⊕a = a⊕ε = a). </p> </li></ul></div> <div class="paragraph"><p>Можно видеть, что моноид — это ослабление понятия группы. Благодаря этому, очень многие структуры являются моноидами. Ну, скажем, множество действительных чисел с операцией сложения. Или множество списков элементов какого-то одного типа с операцией (++).</p></div> </div> <h2 id="__4">Монады</h2> <div class="sectionbody"> <div class="paragraph"><p>Ну а теперь главное определение ;). <strong>Монада</strong> — это моноид в категории эндофункторов. Расшифровываю.</p></div> <div class="paragraph"><p>Пусть у нас есть какой-нибудь эндофунктор на категории Hask (т.е. тип f, являющийся экземпляром класса Functor). Дополним его структурой моноида. Для этого нам понадобится бинарная операция и её единичный элемент. Подходящая бинарная операция традиционно называется bind (и в haskell обозначается >>=). Подходящий единичный элемент традиционно называется return. В Haskell это выражается в следующее определение:</p></div> <div class="listingblock"> <div class="content"> <pre><tt> class Monad m where<br /> (>>=) :: m a -> (a -> m b) -> m b -- бинарная операция<br /> return :: a -> m a -- единичный элемент</tt></pre> </div></div> <div class="paragraph"><p>В модуле Prelude объявлены экземпляры класса Monad для некоторых типов: [], Maybe, итп.</p></div> </div> <h2 id="__5">К чему это я всё</h2> <div class="sectionbody"> <div class="paragraph"><p>Из всего вышесказанного можно вывести очевидную вещь: монады — очень абстракная сущность. Настолько абстрактная, что чуть ли не всё на свете является монадой.</p></div> <div class="paragraph"><p>С другой стороны, это очень простая штука: некий аналог действия «композиция функций», только при композиции используется какая-то дополнительная информация.</p></div> </div> </div>Portnovhttp://www.blogger.com/profile/07229550690332111129noreply@blogger.com9tag:blogger.com,1999:blog-3628051270185801752.post-46277829182021199372010-08-24T02:36:00.000-07:002010-08-24T02:37:18.999-07:00Душа?<div id="content"> <div id="preamble"> <div class="sectionbody"> <div class="paragraph"><p>Я, кажется, понял, что такое душа :) По крайней мере, с точки зрения кибернетики.</p></div> <div class="paragraph"><p>Понятно, что человек — достаточно сложное устройство. Так что будем рассматривать устройство попроще, а именно: чёрный ящик с десятью кнопочками и десятью лампочками. В зависимости от нажатия кнопочек загораются какие-то лампочки. Можно выделить два типа поведения таких ящиков:</p></div> <div class="olist arabic"><ol class="arabic"><li> <p> Какие лампочки сейчас горят, зависит только от того, какие кнопочки сейчас нажаты, и ни от чего больше. </p> </li><li> <p> Включение лампочек зависит не только от того, какие кнопки нажаты, но и от порядка нажатия. </p> </li></ol></div> <div class="paragraph"><p>Пусть мы хотим моделировать такие ящики на Haskell. Пусть мы ввели тип X для обозначения множества нажатых в данный момент кнопок, и тип Y — для множества горящих лампочек. Например, это можно сделать так:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>import qualified Data.Set as S<br />type Button = Int<br />type Lamp = Int<br />type X = S.Set Button<br />type Y = S.Set Lamp</tt></pre> </div></div> <div class="paragraph"><p>Тогда ящик из пункта 1) можно полностью описать функцией с типом</p></div> <div class="literalblock"> <div class="content"> <pre><tt>f1 ∷ X -> Y</tt></pre> </div></div> <div class="paragraph"><p>Это так называемая чистая функция. Заметим, что такая функция не может создавать слишком сложного поведения ящика. Действительно, если f1 очень замысловатая, то, тыкая в кнопки на ящике, мы не сможем разгадать устройства функции f1. Но мы сможем составить табличку из двух колонок: «нажатые кнопочки», «горящие лампочки». Формально говоря, эта таблица — график функции f1. Строк в этой таблице должно быть всего-то навсего <tt>2^10</tt>, т.е. 1024. Пользуясь этой таблицей, мы легко сможем предсказывать поведение ящика, сколь бы сложной ни была функция f1.</p></div> <div class="paragraph"><p>Ящик же из пункта 2) можно описать функцией с более сложным типом:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>import Control.Monad.State</tt></pre> </div></div> <div class="literalblock"> <div class="content"> <pre><tt>f2 ∷ X -> State St Y</tt></pre> </div></div> <div class="paragraph"><p>где St — какой-нибудь более или менее замысловатый тип данных. Гипотеза: чем сложнее тип St, тем сложнее может быть поведение функции f2. Возьмём сначала очень простой тип: type St = Bool. Тогда f2 представляет собой конечный автомат с двумя состояниями. Ничего принципиально более сложного, чем двухтактный счётчик, из такого автомата не сделать. Возможно, например, такое поведение: при чётных нажатиях на первую кнопку загорается пятая лампочка, а при нечётных - десятая. Этот алгоритм легко разгадать наблюдениями, никакой загадочности в нём нет. Если type St = Int, то можно придумать уже гораздо более сложное поведение. А уж если, скажем,</p></div> <div class="literalblock"> <div class="content"> <pre><tt>data Tree a = Node a | Branch [Tree a]<br />type St = Tree [Maybe Int]</tt></pre> </div></div> <div class="paragraph"><p>или что-нибудь ещё такое понавороченнее, то поведение f2 может быть очень сложным и даже загадочным. В процессе нажимания на кнопочки на таком ящике и наблюдения за лампочками нас вполне может посетить мысль, что в ящике сидит какой-то инопланетянин, а кнопочки и лампочки — это у него такой способ общения. Ну, я думаю, вы уже поняли мою гипотезу: душа — это и есть вот это состояние в смысле монады State!</p></div> <div class="paragraph"><p>Отсюда следуют некоторые интересные мысли. Например, мы часто говорим о человеке что-нибудь вроде «у него сложный характер» или там «у него настоящая загадочная русская душа»… Оказывается, в указанном выше смысле, понятию сложности души можно придать вполне конкретный смысл — это просто сложность типа данных! :)</p></div> <div class="paragraph"><p>Или рассмотрим какую-нибудь очень сложную программу, например банковскую опердень или ОС Windows. Такие программы обладают одновременно двумя свойствами:</p></div> <div class="ulist"><ul><li> <p> Их поведение во многих случаях загадочно, далёкие от ИТ пользователи даже часто склонны их одушевлять; </p> </li><li> <p> Они написаны так, что содержат изменяемое состояние, причём это состояние само по себе очень сложное (ну, например, состояние базы данных). </p> </li></ul></div> <div class="paragraph"><p>В свете рассуждений выше мы видим, что одновременность появления этих свойств отнюдь не случайна. «Душа» таких программ по сложности сопоставима с душой человека, что ж удивляться, что она загадочна? Кроме того, оказывается, те далёкие от ИТ пользователи правы в вышеприведённом смысле.</p></div> </div> </div> </div>Portnovhttp://www.blogger.com/profile/07229550690332111129noreply@blogger.com3tag:blogger.com,1999:blog-3628051270185801752.post-2681184175591784252010-08-02T01:22:00.000-07:002010-08-02T01:35:41.887-07:00Небольшая иллюстрация к предыдущемуНашёл у Гейтинга [1] иллюстрацию к изоморфизму Карри-Ховарда. Что интересно: насколько я понял, эта иллюстрация была сформулирована до самого изоморфизма.<br /><br />«Пусть A обозначает свойство натурального числа быть кратным 8, B — быть кратным 4, C — кратным 2. <span style="font-style: italic;">8a</span> мы можем записать как <span style="font-style: italic;">4∙2a</span>; благодаря этому математическому построению <span style="font-style: italic;">(P)</span> мы видим, что свойство A влечёт свойство B, или A → B. Подобное построение <span style="font-style: italic;">(Q) </span>показывает, что B → C. Употребляя сначала P, потом Q (суперпозиция P и Q), мы получаем <span style="font-style: italic;">8a = 2∙(2∙2a)</span>, что доказывает A → C. Этот процесс остаётся пригодным, если вместо A, B, C мы подставим произвольные свойства. А именно, если построение P доказывает, что A → B, и построение Q доказывает, что B → C, то суперпозиция P и Q доказывает, что A → C».<br /><br />Если считать «построения» функциями, то из этого рассуждения увидим, что существование операции суперпозиции двух функций (P и Q) доказывает транзитивность импликации:<br /><br />(.) :: (b -> c) -> (a -> b) -> a -> c<br /><br />[1] А. Гейтинг. Введение в интуиционизм. М.: Мир, 1965.Portnovhttp://www.blogger.com/profile/07229550690332111129noreply@blogger.com2tag:blogger.com,1999:blog-3628051270185801752.post-19231474097010336862010-07-23T09:31:00.000-07:002010-07-23T09:35:00.598-07:00Вычислимость, λ-исчисление, теория типов, автоматизация доказательствЭто краткое и весьма поверхностное изложение результатов нескольких связанных разделов математики за последний век. Размещаю, в основном, чтобы несколько упорядочить мысли в голове. Ну и чтобы не забыть. Тут могут быть неточности и даже фактические ошибки, если увидите - сообщите в комментариях.<br /><br /><div id="content"> <h2 id="_">λ-исчисление</h2> <div class="sectionbody"> <div class="paragraph"><p>λ-исчисление было создано в начале 50-х гг. XX века для формализации понятий вычисления и вычислимости в математике. λ-исчисление оперирует символами и λ-выражениями. Символ — это одиночный абстрактный объект, иногда символы называют переменными. Символы обозначают маленькими латинскими буквами: x,y,z… λ-выражение определяется рекурсивно:</p></div> <div class="ulist"><ul><li> <p> Если x — это символ, то x — это λ-выражение; </p> </li><li> <p> Если x — символ, а E — λ-выражение, то запись λx.E — тоже λ-выражение; </p> </li><li> <p> Если E — λ-выражение, то (E) — λ-выражение; </p> </li><li> <p> Если E1 и E2 — λ-выражения, то E1 E2 — тоже λ-выражение. </p> </li></ul></div> <div class="paragraph"><p>Выражения вида λx.E называют λ-функциями, или просто функциями. Если в таком выражении символ x встречается в выражении E, он называется связанным. Несвязанные символы, встречающиеся в λ-выражении, называются свободными.</p></div> <div class="paragraph"><p>Для любого λ-выражения E можно записать выражение λx.E, где x — свободный символ выражения E (и, таким образом, связать символ x). Эта операция называется λ-абстракцией.</p></div> <div class="paragraph"><p>Введём обозначение: записью E[x=T] будем обозначать выражение, полученное из E заменой всех вхождений символа x на T.</p></div> <div class="paragraph"><p>Над λ-выражениями можно производить следующие операции:</p></div> <div class="dlist"><dl><dt class="hdlist1"> α-конверсия </dt><dd> <p> E → E[x=y]; т.е. переменные можно переименовывать; </p> </dd><dt class="hdlist1"> β-редукция </dt><dd> <p> (λx.E1) E2 → E1[x=E2]; это подстановка; </p> </dd><dt class="hdlist1"> η-редукция </dt><dd> <p> λx.(E x) → E (избавление от лишней абстракции). </p> </dd></dl></div> <div class="paragraph"><p>Видно, что α-конверсию можно применить к любому выражению, а редукции — только к выражениям определённого вида.</p></div> <div class="paragraph"><p>Если к выражению нельзя применить никаких редукций, говорят, что оно находится в нормальной форме.</p></div> <div class="paragraph"><p><strong>Теорема</strong>. Если у выражения есть нормальная форма, то она только одна. Любая последовательность редукций приведёт к этой нормальной форме.</p></div> <div class="paragraph"><p>Таким образом, выполняется единственность. Но существование выполняется не всегда: не у всех выражений есть нормальная форма.</p></div> <div class="exampleblock"> <div class="title">Example 1: Пример.</div> <div class="exampleblock-content"> <div class="paragraph"><p>ω = (λx.x x) (λx.x x)</p></div> <div class="paragraph"><p>ω = (λx.x x) (λy.y y) = (λy.y y) (λy.y y) = (λy.y y) (λz.z z) = (λz.z z) (λz.z z) = …</p></div> <div class="paragraph"><p>Последовательность редукций не изменяет это выражение.</p></div> </div></div> <div class="paragraph"><p>Процесс применения редукций к выражению называется вычислением. Выражения, имеющие нормальную форму, называются вычислимыми.</p></div> <div class="paragraph"><p>Это вполне соответствует ситуации с программами для машины Тьюринга, среди которых есть как завершающиеся, так и не завершающиеся (работающие бесконечно).</p></div> <div class="paragraph"><p>Доказывается, что λ-исчисление тьюринг-полно, то есть любой алгоритм, который можно записать для машины Тьюринга, можно записать в виде λ-выражения, и наоборот. В качестве иллюстрации рассмотрим, как вводятся в λ-исчислении некоторые привычные в программировании сущности.</p></div> <div class="paragraph"><p>Введём обозначение: λx.λy.λz.λ…E будем записывать как λx y z….E.</p></div> <div class="paragraph"><div class="title">Натуральные числа.</div><p>За 1 примем выражение λx.x. Для каждого «числа» E следующим числом будем считать выражение λx.E. Таким образом, двойке будет соответствовать выражение λx y.y, тройке — λx y z.z, и т.д. Далее определения действий над числами вводятся как в аксиоматике Пеано. Такая запись чисел называется кодировкой Чёрча.</p></div> <div class="paragraph"><div class="title">Выражения «если-то».</div><p>За логическую истину примем выражение λx y.x, за логическую ложь — λx y.y. Тогда выражение λc x y.c x y будет соответствовать конструкции «if-then-else». Действительно,</p></div> <div class="ulist"><ul><li> <p> if TRUE A B = (λc x y.c x y) (λx y.x) A B = (λx y.x) A B = A; </p> </li><li> <p> if FALSE A B = (λc x y.c x y) (λx y.y) A B = (λx y.y) A B = B. Кроме того, оказывается, что можно ввести и обычные логические действия — and, or и т.д. </p> </li></ul></div> <div class="paragraph"><div class="title">Пары (кортежи из двух элементов).</div><p>Пусть</p></div> <div class="literalblock"> <div class="content"> <pre><tt>pair = λf.λs.λb.b f s<br />fst = λp.p TRUE<br />snd = λp.p FALSE</tt></pre> </div></div> <div class="paragraph"><p>Тогда выражение pair x y создаёт кортеж (x,y), функция fst возвращает первый элемент кортежа, snd — второй. Действительно,</p></div> <div class="literalblock"> <div class="content"> <pre><tt>pair x y = λb.b x y;<br />fst (pair x y) = (λp.p TRUE) (λb.b x y) = (λb.b x y) TRUE = TRUE x y = x;<br />snd (pair x y) = (λp.p FALSE) (λb.b x y) = (λb.b x y) FALSE = FALSE x y = y.</tt></pre> </div></div> <div class="paragraph"><p>Кортежи из трёх и более элементов, очевидно, можно составлять из пар, например pair x (pair y z) — кортеж из трёх элементов.</p></div> </div> <h2 id="__2">Проблема останова</h2> <div class="sectionbody"> <div class="paragraph"><p>Проблема останова ставится следующим образом. Дан алгоритм (записанный в виде программы для машины Тьюринга или в виде λ-выражения). Нужно, не выполняя его, выяснить, завершается ли он или работает бесконечное время.</p></div> <div class="paragraph"><p><strong>Теорема</strong>. Проблема останова в общем случае неразрешима.</p></div> <div class="paragraph"><p>Дальнейшее развитие теории вычислимости шло в направлении выяснения классов алгоритмов, для которых проблема останова разрешима. В случае с теорией машины Тьюринга такая задача не решена до сих пор. В случае с λ-исчислением решением стала теория типов.</p></div> </div> <h2 id="__3">Теория типов</h2> <div class="sectionbody"> <div class="paragraph"><p>Введём в λ-исчисление типизацию. Именно, кроме символов и выражений, теперь в теории будут фигурировать типы — тоже абстрактные объекты. Типы будем обозначать маленькими греческими буквами: τ, σ… Любое λ-выражение должно иметь тип, и при том только один. Это записывается как E : τ. Типы определяются также рекурсивно:</p></div> <div class="ulist"><ul><li> <p> Если τ — тип, то (τ) — тип; </p> </li><li> <p> Если τ и σ — типы, то τ → σ — тип. </p> </li></ul></div> <div class="paragraph"><p>Типы без стрелок и других операций называются простыми типами. Все символы имеют простые типы.</p></div> <div class="paragraph"><p>Тип выражения определяется по следующим правилам:</p></div> <div class="ulist"><ul><li> <p> Если x : τ и E : σ, то (λx.E) : τ → σ; </p> </li><li> <p> Если F : τ → σ и x : τ, то (F x) : σ. </p> </li></ul></div> <div class="paragraph"><p>При этом если в выражении F x окажется, что F : τ → σ и x : τ1, причём τ ≠ τ1, то говорят, что выражение неверно типизированное. В типизированном λ-исчислении рассматриваются только верно типизированные выражения.</p></div> <div class="paragraph"><p>Тип называется <strong>населённым</strong>, если существует хотя бы одно λ-выражение, имеющее такой тип.</p></div> <div class="exampleblock"> <div class="title">Example 2: Пример.</div> <div class="exampleblock-content"> <div class="paragraph"><p>Попробуем типизировать выражение λx.x x. Пусть x : τ. Тогда, чтобы к x, стоящему в выражении последним, можно было применить предыдущий x, этот предыдущий x должен иметь тип τ → σ, где σ — какой-то ещё тип. Но (x : τ) и (x : τ → σ) не может выполняться одновременно, т.к. у каждого символа может быть только один тип. Пришли к противоречию. Итак, рассматриваемое выражение неверно типизировано, а значит, неверно типизировано и упомянутое в предыдущем примере выражение ω. Таким образом, оказалось, что невычислимое выражение ω не входит в типизированное λ-исчисление.</p></div> </div></div> <div class="paragraph"><p>Оказывается, что верна следующая</p></div> <div class="paragraph"><p><strong>Теорема</strong>. Если λ-выражение верно типизировано, то оно вычислимо.</p></div> <div class="paragraph"><p>Таким образом, в рамках типизированного λ-исчисления проблема останова решается очень просто: все выразимые в этой системе алгоритмы завершаются.</p></div> <div class="paragraph"><p>Неудивительно, что при этом оказывается, что эта система не является тьюринг-полной. Т.е. существуют алгоритмы, которые можно представить в виде программы для машины Тьюринга, но нельзя записать в рамках типизированного λ-исчисления. Из этого можно сделать два замечания:</p></div> <div class="ulist"><ul><li> <p> Само по себе типизированное λ-исчисление не несёт большой практической ценности, т.к. не позволяет выразить многие алгоритмы; </p> </li><li> <p> С другой стороны, т.к. множество типизированных выражений является подмножеством нетипизированных, появляется намёк на решение проблемы останова: чтобы выяснить, вычислимо ли данное выражение, нужно только проверить, является ли оно верно типизированным. Неудивительно, что как раз эта задача (проверить возможность типизации для произвольного выражения) оказывается неразрешимой. </p> </li></ul></div> <div class="paragraph"><p>Для возможности практического применения λ-исчисления разработаны системы типов, накладывающие меньше ограничений, чем вышеприведённая. Такие системы:</p></div> <div class="ulist"><ul><li> <p> достаточно выразительны, т.к. являются тьюринг-полными; </p> </li><li> <p> вывод типов в них оказывается разрешимой задачей; </p> </li><li> <p> но проблема останова, как и в случае нетипизированного λ-исчисления, неразрешима. </p> </li></ul></div> <div class="paragraph"><p>На таких "промежуточных" системах типов основаны имеющие практическое применения функциональные языки программирования.</p></div> </div> <h2 id="__4">Автоматизация доказательств</h2> <div class="sectionbody"> <div class="paragraph"><p>Для нужд практического программирования в теории типов обычно добавляют такое правило:</p></div> <div class="ulist"><ul><li> <p> Если τ и σ — типы, то τ*σ и τ+σ — тоже типы. </p> </li></ul></div> <div class="paragraph"><p>В λ-исчислении можно определить понятие пары (E1,E2) и понятие «одно из двух» E1|E2. При этом оказывается, что:</p></div> <div class="ulist"><ul><li> <p> Если E1 : τ, E2 : σ, то (E1,E2) : τ*σ, </p> </li><li> <p> и E1 | E2 : τ+σ. </p> </li></ul></div> <div class="paragraph"><p>Оказывается, что между терминами теории типов и терминами логики высказываний существует естественное соответствие. Именно:</p></div> <div class="ulist"><ul><li> <p> Простой тип τ соответствует простому высказыванию; </p> </li><li> <p> Сумма типов соответствует дизъюнкции; </p> </li><li> <p> Произведение типов — коньюнкции; </p> </li><li> <p> Стрелка — импликации. </p> </li></ul></div> <div class="paragraph"><p>Таким образом, любую теорему логики высказываний можно записать как некий тип. При этом оказывается верной следующая важная</p></div> <div class="paragraph"><p><strong>Теорема</strong> (изоморфизм Карри-Ховарда). Теорема логики высказываний верна тогда и только тогда, когда соответствующий ей тип населён, т.е. любое λ-выражение, имеющее этот тип, можно рассматривать как доказательство теоремы.</p></div> <div class="paragraph"><p>Более сложные, чем вышеприведённая, системы типов позволяют записывать не только теоремы логики высказываний, но и теоремы логики более высоких порядков. При этом изоморфизм Карри-Ховарда остаётся в силе.</p></div> <div class="paragraph"><p>Задача «по данному типу найти выражение, имеющее такой тип, или доказать, что таких выражений нет» для многих классов типов решена. Таким образом, задача «по данной теореме найти её доказательство или опровержение» сводится к следующему:</p></div> <div class="ulist"><ul><li> <p> Записать данную теорему в виде типа в одной из систем типов; </p> </li><li> <p> Найти выражение, населяющее этот тип; </p> </li><li> <p> Записать это выражение на языке предметной области. </p> </li></ul></div> </div> </div>Portnovhttp://www.blogger.com/profile/07229550690332111129noreply@blogger.com0tag:blogger.com,1999:blog-3628051270185801752.post-29136632914340126222010-03-20T11:34:00.000-07:002010-03-20T13:03:56.484-07:00LiveMath IIIЭто продолжение к стародавнему посту: http://iportnov.blogspot.com/2007/09/livemath-livecd.html.<br /><br />К сожалению, редко оказывается достаточно времени, чтобы собрать свежую версию LiveMath. Однако же вот, собрал. В этот раз LiveMath основан на Ubuntu 9.10 (Karmic) с добавлениями из Ubuntu Lucid и "Ubuntu Scientific Remix". LiveMath III содержит (среди прочего):<br /><br /><span style="font-weight: bold;">Системы компьютерной алгебры:</span><br />Maxima (http://maxima.sourceforge.net) - полнофункциональная система аналитических вычислений;<br />Fricas (http://fricas.sourceforge.net) - мощная система компьютерной алгебры;<br />YaCas (http://yacas.sourceforge.net) - еще одна система компьютерной алгебры;<br />PARI/GP (http://pari.math.u-bordeaux.fr/) - широко используемая компьютерно-алгебраическая система, разработанная для быстрых вычислений в теории чисел (факторизации, алгебраическая теория чисел, эллиптические кривые...);<br />GAP (http://www.gap-system.org/) - свободно распространяемый, открытый и расширяемый программный комплекс для применения в области вычислительной дискретной математики, в частности, теории групп;<br />Mathomatic (http://www.mathomatic.org/) - переносимая, универсальная программа, которая может решать, упрощать, группировать, дифференцировать, интегрировать и сравнивать алгебраические выражения;<br /><span style="font-weight: bold;"><br />Системы автоматизации доказательств:</span><br />ACL2 (http://www.cs.utexas.edu/users/moore/acl2/) - язык программирования для моделирования компьютерных систем и средство, помогающее доказывать свойства этих моделей;<br />Coq (http://coq.inria.fr/) - система автоматизированного построения доказательств, с помощью которой, кроме всего прочего, была решена проблема четырех красок;<br />Также Prover9/Mace4 и некоторые другие;<br /><span style="font-weight: bold;"><br />Системы численных вычислений:</span><br />SciLab (http://www.scilab.org/) - пакет научных программ для численных вычислений, предоставляющий мощное открытое окружение для инженерных и научных расчетов;<br />GNU Octave (http://www.octave.org/) - язык высокого уровня, предназначенный для выполнения математических вычислений;<br />FreeMat (http://freemat.sourceforge.net/) - свободная среда для быстрой разработки, научного прототипирования и обработки данных, имеет интерфейс и синтаксис языка, подобные MatLab;<br /><span style="font-weight: bold;"></span>Yorick (http://yorick.sourceforge.net/) - компактная программная среда, предназначенная для комплексного решения научно-инженерных вычислительных задач;<br /><br /><span style="font-weight: bold;">Образовательные программы:</span><br />Kig (http://edu.kde.org/kig/), Carmetal, DrGeo, GeoGebra - интерактивная геометрия;<br />KAlgebra;<br />Инструменты построения графиков - kmplot, gnuplot;<br /><br /><span style="font-weight: bold;">Обработка и визуализация данных:</span><br />Mayavi2 (http://code.enthought.com/projects/mayavi/#Mayavi2) - открытый пакет научной 2D и 3D визуализации данных;<br />OpenDX (http://www.opendx.org/) - программное средство для анализа данных в графическом виде, визуализации научных данных;<br />GGobi (http://www.ggobi.org/) - среда визуализации многомерных данных;<br />LabPlot (http://labplot.sourceforge.net/) - программа для анализа и визуализации различных данных;<br />QtiPlot - позиционируется как замена для Microcal Origin - программа для несложной статистической обработки данных, построения всяческих графиков;<br />Grace6 (http://plasma-gate.weizmann.ac.il/Grace/) - программа для подготовки двумерных графиков по численным данным;<br />PAW (http://cern.ch/paw/) - интерактивная программа анализа и графического представления результатов. Может применяться для анализа большого и очень большого объёма данных;<br />ROOT (http://cern.ch/root/) - наследник PAW, интерактивная система обработки и визуализации очень больших объёмов научных данных;<br />GNU R (http://r-project.org/) - мощный язык статистических вычислений, используемый профессиональными статистиками;<br />GRETL (http://gretl.sourceforge.net/) - система эконометрического анализа;<br /><br /><span style="font-weight: bold;">Научные редакторы:<br /></span>TeXLive - полноценный дистрибутив TeX;<br />TeXmacs (http://texmacs.org) - текстовый редактор для набора математических и прочих научных текстов, также позволяет включать в документ сессии Axiom, Maxima, Octave, SciLab и других систем компьютерной математики;<br />Kile (http://kile.sourceforge.net/) - интегрированная среда подготовки документов с помощью TeX;<br />Texmaker (http://www.xm1math.net/texmaker/) - интегрированная оболочка для LaTeX;<br /><br />Также LiveMath III содержит среду Gnome 2.28, OpenOffice.org 3.1, Gnumeric. Для "больших" систем (ROOT, PAW, R, Octave) включена значительная часть имеющихся в репозиториях Ubuntu пакетов. Для многих изначально "консольных" систем включены GUI-обёртки, для некоторых по несколько, на выбор. К большинству программ есть документация. Возможна установка системы на жёсткий диск с помощью стандартного установщика Ubuntu.<br /><br />UPD. Полный список установленных пакетов: http://iportnov.ru/files/LiveMath.packages.txt<br /><br />К сожалению, у меня нет времени, чтобы тестировать все эти программы. То, что я протестировал - работает. Багрепорты принимаются в комментариях или на e-mail portnov at bk dot ru, но мгновенного исправления не обещаю.<br /><br />LiveMath сделан с помощью Ubuntu Construction Kit (http://uck.sourceforge.net/), так что каждый, в принципе, может сделать себе нечто подобное. Вероятно, это окажется проще, чем качать моё изделие.<br /><br />Взять можно здесь: http://portnov.homelinux.net/LiveMath%20III.iso (размер образа - 2Gb), может быть удобнее окажется торрент: http://iportnov.ru/files/LiveMath%20III.iso.torrent (честно говоря, не знаю, заработает ли). У меня сейчас нет хостинга, на котором я бы мог размещать большие ISO-образы. Так что учтите, что portnov.homelinux.net - это мой домашний сервер, обычно бывает включён примерно с 8:00 до 22:00 MSK, суперскоростей не обещаю. Если кому-то позарез нужно скачать в другое время - пишите, так уж и быть, оставлю включённым на ночь :)Portnovhttp://www.blogger.com/profile/07229550690332111129noreply@blogger.com8tag:blogger.com,1999:blog-3628051270185801752.post-80291529118573404172010-03-13T06:36:00.000-08:002010-03-28T11:45:49.750-07:00Ядро Linux за 10 минут (обзор)<div id="content"><br /><p><em>Это конспект доклада для семинара, проведённого <a href="http://lug-mgn.ru/">нашей LUG</a> совместно с университетом.</em></p><br /><p><em>У меня, натурально, было 10 минут, поэтому изложение — галопом по европам, многое упрощено, многое упущено.</em></p><br /><h2 id="_">Немного истории</h2> <div class="sectionbody"> <div class="paragraph"><p>Относительно подробную историю создания ядра Linux можно найти в известной книге Линуса Торвальдса «Just for fun». Нас из неё интересуют следующие факты:</p></div> <div class="ulist"><ul><li> <p> Ядро создал в 1991 году студент университета Хельсинки Линус Торвальдс; </p> </li><li> <p> В качестве платформы он использовал ОС Minix, написанную его преподавателем Эндрю Таненбаумом, запущенную на персональном компьютере с процессором Intel 80386; </p> </li><li> <p>В качестве примера для подражания он использовал ОС семейства Unix, а в качестве путеводителя — сначала стандарт POSIX, а затем просто исходные коды программ из комплекта GNU (bash, gcc и пр). </p> </li></ul></div> <div class="paragraph"><p>Эти факты в значительной мере определили пути развития ядра в дальнейшем, их следствия заметны и в современном ядре.</p></div> <div class="paragraph"><p>В частности, известно, что Unix-системы в своё время разделились на два лагеря: потомки UNIX System V Release 4 (семейство SVR4) против потомков Berkley Software Distribution v4.2 (BSD4.2). Linux по большей части принадлежит к первому семейству, но заимствует некоторые существенные идеи из второго.</p> <h2>Ядро в цифрах</h2> <ul><li> Около 30 тыс. файлов</li><li> Около 8 млн. строк кода (не считая комментариев)</li><li> Репозиторий занимает около 1 Гб</li><li> linux-2.6.33.tar.bz2: 63 Mb</li><li> patch-2.6.33.bz2: 10Mb, около 1.7 млн изменённых строк</li><li> Около 6000 человек, чей код есть в ядре</li></ul></div> </div> <h2 id="__2">Об архитектуре ядра</h2> <div class="sectionbody"> <div class="paragraph"><p>Все (или почти все) процессоры, которыми когда-либо интересовались производители Unix-подобных ОС, имеют аппаратную поддержку разделения привелегий. Один код может всё (в т.ч. общаться напрямую с оборудованием), другой — почти ничего. Традиционно говорят о «режиме ядра» (kernel land) и «режиме пользователя» (user land). Различные архитектуры ядер ОС различаются прежде всего подходом к ответу на вопрос: какие части кода ОС должны выполняться в kernel land, а какие — в user land? Дело в том, что у подавляющего большинства процессоров переключение между двумя режимами занимает существенное время. Выделяют следующие подходы:</p></div> <div class="ulist"><ul><li> <p> Традиционный: монолитное ядро. Весь код ядра компилируется в один большой бинарный файл. Всё ядро исполняется в режиме ядра; </p> </li><li> <p> Противоположный, новаторский: микроядро. В режиме ядра выполняются только самые необходимые части, всё остальное — в режиме пользователя; </p> </li><li> <p>В традиционном подходе позже появился вариант: модульное ядро. Всё исполняется в режиме ядра, но при этом ядро компилируется в виде одного большого бинарного файла и кучки мелких модулей, которые могут загружаться и выгружаться по необходимости; </p> </li><li> <p> И, конечно, всевозможные варианты гибридных архитектур. </p> </li></ul></div> <div class="paragraph"><p>Ядро Linux начиналось как монолитное (глядя на существовавшие тогда Unix-ы). Современное Linux-ядро модульное. По сравнению с микроядром монолитное (или модульное) ядро обеспечивает существенно бо́льшую производительность, но предъявляет существенно более жёсткие требования к качеству кода различных компонентов. Так, в системе с микроядром «рухнувший» драйвер ФС будет перезапущен без ущерба для работы системы; рухнувший драйвер ФС в монолитном ядре — это Kernel panic и останов системы.</p></div> </div> <h2 id="_linux">Подсистемы ядра Linux</h2> <div class="sectionbody"> <div class="paragraph"><p>Существует довольно широко известная диаграмма, изображающая основные подсистемы ядра Linux и их взаимодействие. Вот она:</p></div> <div class="paragraph"><p>:<span class="image"> <img src="http://lug-mgn.ru/files/kernel/linux-kernel-big.png" alt="linux-kernel-big.png" /> </span></p></div> <div class="paragraph"><p>Собственно, в настоящий момент видно только, что частей много и их взаимосвязи очень сложные. Поэтому мы будем рассматривать упрощённую схему:</p></div> <div class="paragraph"><p>:<span class="image"> <img src="http://lug-mgn.ru/files/kernel/linux-kernel-simple.png" alt="linux-kernel-simple.png" /> </span></p></div> <h3 id="__3">Системные вызовы</h3> <div class="paragraph"><p>Уровень системных вызовов — это наиболее близкая к прикладному программисту часть ядра Linux. Системные вызовы предоставляют интерфейс, используемый прикладными программами — это API ядра. Большинство системных вызовов Linux взяты из стандарта POSIX, однако есть и специфичные для Linux системные вызовы.</p></div> <div class="paragraph"><p>Здесь стоит отметить некоторую разницу в подходе к проектированию API ядра в Unix-системах с одной стороны и в Windows[NT] и других идеологических потомках VMS с другой. Дизайнеры Unix предпочитают предоставить десять системных вызовов с одним параметром вместо одного системного вызова с двадцатью параметрами. Классический пример — создание процесса. В Windows функция для создания процесса — <tt>CreateProcess()</tt> — принимает 10 аргументов, из которых 5 — структуры. В противоположность этому, Unix-системы предоставляют два системных вызова (<tt>fork()</tt> и <tt>exec()</tt>), первый — вообще без параметров, второй — с тремя параметрами.</p></div> <div class="paragraph"><p>Системные вызовы, в свою очередь, обращаются к функциям более низкоуровневых подсистем ядра.</p></div> <h3 id="__4">Управление памятью</h3> <div class="paragraph"><p>Ядро Linux использует в качестве минимальной единицы памяти страницу. Размер страницы может зависеть от оборудования; на x86 это 4Кб. Для хранения информации о странице физической памяти (её физический адрес, принадлежность, режим использования и пр) используется специальная структура <tt>page</tt> размером в 40 байт.</p></div> <div class="paragraph"><p>Ядро использует возможности современных процессоров для организации виртуальной памяти. Благодаря манипуляциям с каталогами страниц виртуальной памяти, каждый процесс получает адресное пространство размером в 4Гб (на 32х-разрядных архитектурах). Часть этого пространства доступна процессу только на чтение или исполнение: туда отображаются интерфейсы ядра.</p></div> <div class="paragraph"><p>Существенно, что процесс, работающий в пространстве пользователя, в большинстве случаев «не знает», где находятся его данные: в ОЗУ или в файле подкачки. Процесс может попросить у системы выделить ему память именно в ОЗУ, но система не обязана удовлетворять такую просьбу.</p></div> <h3 id="__5">Управление процессами</h3> <div class="paragraph"><p>Ядро Linux было многозадачным буквально с первого дня. К настоящему моменту оно имеет довольно хорошую поддержку вытесняющей многозадачности.</p></div> <div class="paragraph"><p>В истории было известно два типа многозадачности:</p></div> <div class="dlist"><dl><dt class="hdlist1"> Корпоративная многозадачность. </dt><dd> <p> В этом варианте каждый процесс передаёт управление какому-нибудь другому, когда сочтёт нужным. Это экономит время на переключение режимов процессора, но, очевидно, о надёжности такой системы говорить не приходится: зависший процесс не передаст управление никому. В современных ОС этот вариант не используется. </p> </dd><dt class="hdlist1"> Вытесняющая многозадачность. </dt><dd> <p>Ядро ОС выделяет каждому процессу определённый квант процессорного времени и «насильно» передаёт управление другому процессу по истечении этого кванта. Это создаёт накладные расходы на переключение режимов процессора и расчёт приоритетов, но повышает надёжность и производительность. </p> </dd></dl></div> <div class="paragraph"><p>Переключение процессов в linux может производиться по наступлению двух событий: аппаратного прерывания или прерывания от таймера. Частота прерываний таймера устанавливается при компиляции ядра в диапазоне от 100Гц до 1000Гц. Аппаратные прерывания возникают чуть ли не чаще: достаточно двинуть мышку или нажать кнопку на клавиатуре, да и внутренние устройства компьютера генерируют прерывания. Начиная с версии 2.6.23, появилась возможность собрать ядро, не использующее переключение процессов по таймеру. Это позволяет снизить энергопотребление в режиме простоя компьютера.</p></div> <div class="paragraph"><p>Планировщик процессов использует довольно сложный алгоритм, основанный на расчёте приоритетов процессов. Среди процессов выделяются те, что требуют много процессорного времени и те, что тратят больше времени на ввод-вывод. На основе этой информации регулярно пересчитываются приоритеты процессов. Кроме того, используются задаваемые пользователем значения <tt>nice</tt> для отдельных процессов.</p></div> <div class="paragraph"><p>Кроме многозадачности в режиме пользователя, ядро Linux использует многозадачность в режиме ядра: само ядро многопоточно.</p></div> <div class="paragraph"><p>Традиционные ядра Unix-систем имели следующую… ну если не проблему, то особенность: само ядро не было вытесняемым. Пример: процесс <tt>/usr/bin/cat</tt> хочет открыть файл <em>/media/cdrom/file.txt</em> и использует для этого системный вызов <em>open()</em>. Управление передаётся ядру. Ядро обнаруживает, что файл расположен на CD-диске и начинает инициализацию привода (раскручивание диска и пр). Это занимает существенное время. Всё это время управление не передаётся пользовательским процессам, т.к. планировщик не активен в то время, когда выполняется код ядра. Все пользовательские процессы ждут завершения этого вызова <tt>open()</tt>.</p></div> <div class="paragraph"><p>В противоположность этому, современное ядро Linux полностью вытесняемо. Планировщик отключается лишь на короткие промежутки времени, когда ядро никак нельзя прервать — например, на время инициализации некоторых устройств, которые требуют, чтобы определённые действия выполнялись с фиксированными задержками. В любое другое время поток ядра может быть вытеснен, и управление передано другому потоку ядра или пользовательскому процессу.</p></div> <h3 id="__6">Сетевая подсистема</h3> <div class="paragraph"><p>Сетевая подсистема ядра ОС, теоретически, почти вся может выполняться в пространстве пользователя: для таких операций, как формирование пакетов TCP/IP, никакие привелегии не нужны. Однако в современных ОС, тем более применяемых на высоконагруженных серверах, от всего сетевого стека — всей цепочки от формирования пакетов до работы непосредственно с сетевым адаптером — требуется максимальная производительность. Сетевой подсистеме, работающей в пространстве пользователя, пришлось бы постоянно обращаться к ядру для общения с сетевым оборудованием, а это повлекло бы весьма существенные накладные расходы.</p></div> <div class="paragraph"><p>Сетевая подсистема Linux обеспечивает следующую функциональность:</p></div> <div class="ulist"><ul><li> <p> Абстракцию сокетов; </p> </li><li> <p> Стеки сетевых протоколов (TCP/IP, UDP/IP, IPX/SPX, AppleTalk и мн. др); </p> </li><li> <p> Маршрутизацию (routing); </p> </li><li> <p> Пакетный фильтр (модуль Netfilter); </p> </li><li> <p> Абстракцию сетевых интерфейсов. </p> </li></ul></div> <div class="paragraph"><p>В различных Unix-системах использовалось два различных прикладных интерфейса, обеспечивающих доступ к функциональности сетевой подсистемы: Transport Layer Interface (TLI) из SVR4 и sockets (сокеты) из BSD. Интерфейс TLI, с одной стороны, тесно завязан на подсистему STREAMS, отсутствующую в ядре Linux, а с другой — не совместим с интерфейсом сокетов. Поэтому в Linux используется интерфейс сокетов, взятый из семейства BSD.</p></div> </div> <h2 id="__7">Файловая система</h2> <div class="sectionbody"> <h3 id="_vfs">Виртуальная файловая система (VFS)</h3> <div class="paragraph"><p>С точки зрения приложений, в Unix-подобных ОС существует только одна файловая система. Она представляет собой дерево директорий, растущее из «корня». Приложениям, в большинстве случаев, не интересно, на каком носителе находятся данные файлов; они могут находиться на жёстком диске, оптическом диске, флеш-носителе или вообще на другом компьютере и другом континенте. Эта абстракция и реализующая её подсистема называется виртуальной файловой системой (VFS).</p></div> <div class="paragraph"><p>Стоит заметить, что VFS в ядре Linux реализована с учётом идей из ООП. Например, ядро рассматривает набор структур <tt>inode</tt>, каждая из которых содержит (среди прочего):</p></div> <div class="ulist"><ul><li> <p> Данные из on-disk inode (права доступа, размер файла и др); </p> </li><li> <p> Указатель на структуру, описывающую драйвер ФС, к которой принадлежит данный inode; </p> </li><li> <p> Указатель на струкруру операций с inode, которая, в свою очередь, содержит указатели на функции для создания inode, изменения его атрибутов и т.д., реализованные в конкретном драйвере ФС. </p> </li></ul></div> <div class="paragraph"><p>Аналогично устроены структуры ядра, описывающие другие сущности ФС — суперблок, элемент каталога, файл.</p></div> <h3 id="__8">Драйверы ФС</h3> <div class="paragraph"><p>Драйверы ФС, как можно заметить из диаграммы, относятся к гораздо более высокому уровню, чем драйверы устройств. Это связано с тем, что драйверы ФС не общаются ни с какими устройствами. Драйвер файловой системы лишь реализует функции, предоставляемые им через интерфейс VFS. При этом данные пишутся и читаются в/из страницы памяти; какие из них и когда будут записаны на носитель — решает более низкий уровень. Тот факт, что драйверы ФС в Linux не общаются с оборудованием, позволил реализовать специальный драйвер FUSE, который делегирует функциональность драйвера ФС в модули, исполняемые в пространстве пользователя.</p></div> <h3 id="__9">Страничный кэш</h3> <div class="paragraph"><p>Эта подсистема ядра оперирует страницами виртуальной памяти, организованными в виде базисного дерева (radix tree). Когда происходит чтение данных с носителя, данные читаются в выделяемую в кэше страницу, и страница остаётся в кэше, а драйвер ФС читает из неё данные. Драйвер ФС пишет данные в страницы памяти, находящиеся в кэше. При этом эти страницы помечаются как «грязные» (dirty). Специальный поток ядра, <tt>pdflush</tt>, регулярно обходит кэш и формирует запросы на запись грязных страниц. Записанная на носитель грязная страница вновь помечается как чистая.</p></div> <h3 id="__10">Уровень блочного ввода-вывода</h3> <div class="paragraph"><p>Эта подсистема ядра оперирует очередями (queues), состоящими из структур <tt>bio</tt>. Каждая такая структура описывает одну операцию ввода-вывода (условно говоря, запрос вида «записать вот эти данные в блоки ##141-142 устройства /dev/hda1»). Для каждого процесса, осуществляющего ввод-вывод, формируется своя очередь. Из этого множества очередей создаётся одна очередь запросов к драйверу каждого устройства.</p></div> <h3 id="__11">Планировщик ввода-вывода</h3> <div class="paragraph"><p>Если выполнять запросы на дисковый ввод-вывод от приложений в том порядке, в котором они поступают, производительность системы в среднем будет очень низкой. Это связано с тем, что операция поиска нужного сектора на жёстком диске — очень медленная. Поэтому планировщик обрабатывает очереди запросов, выполняя две операции:</p></div> <div class="ulist"><ul><li> <p> Сортировка: планировщик старается ставить подряд запросы, обращающиеся к находящимся близко секторам диска; </p> </li><li> <p> Объединение: если в результате сортировки рядом оказались несколько запросов, обращающихся к последовательно расположенным секторам, их нужно объединить в один запрос. </p> </li></ul></div> <div class="paragraph"><p>В современном ядре доступно несколько планировщиков: Anticipatory, Deadline, CFQ, noop. Существует версия ядра от Con Kolivas с ещё одним планировщиком — BFQ. Планировщики могут выбираться при компиляции ядра либо при его запуске.</p></div> <div class="paragraph"><p>Отдельно следует остановиться на планировщике noop. Этот планировщик не выполняет ни сортировки, ни слияния запросов, а переправляет их драйверам устройств в порядке поступления. На системах с обычными жёсткими дисками этот планировщик покажет очень плохую производительность. Однако, сейчас становятся распространены системы, в которых вместо жёстких дисков используются флеш-носители. Для таких носителей время поиска сектора равно нулю, поэтому операции сортировки и слияния не нужны. В таких системах при использовании планировщика noop производительность не изменится, а потребление ресурсов несколько снизится.</p></div> <h3 id="__12">Обработка прерываний</h3> <div class="paragraph"><p>Практически все актуальные архитектуры оборудования используют для общения устройств с программным обеспечением концепцию прерываний. Выглядит это следующим образом. На процессоре выполняется какой-то процесс (не важно, поток ядра или пользовательский процесс). Происходит прерывание от устройства. Процессор отвлекается от текущих задач и переключает управление на адрес памяти, сопоставленный данному номеру прерывания в специальной таблице прерываний. По этому адресу находится обработчик прерывания. Обработчик выполняет какие-то действия в зависимости от того, что именно произошло с этим устройством, затем управление передаётся планировщику процессов, который, в свою очередь, решает, кому передать управление дальше.</p></div> <div class="paragraph"><p>Тут существует определённая тонкость. Дело в том, что во время работы обработчика прерывания планировщик задач не активен. Это не удивительно: предполагается, что обработчик прерывания работает непосредственно с устройством, а устройство может требовать выполнения каких-то действий в жёстких временных рамках. Поэтому если обработчик прерывания будет работать долго, то все остальные процессы и потоки ядра будут ждать, а это обычно недопустимо.</p></div> <div class="paragraph"><p>В ядре Linux в результате любого аппаратного прерывания управление передаётся в функцию <tt>do_IRQ()</tt>. Эта функция использует отдельную таблицу зарегистрированных в ядре обработчиков прерываний, чтобы определить, куда передавать управление дальше.</p></div> <div class="paragraph"><p>Чтобы обеспечить минимальное время работы в контексте прерывания, в ядре Linux используется разделение обработчиков на верхние и нижние половины. Верхняя половина — это функция, которая регистрируется драйвером устройства в качестве обработчика определённого прерывания. Она выполняет только ту работу, которая безусловно должна быть выполнена немедленно. Затем она регистрирует другую функцию (свою нижнюю половину) и возвращает управление. Планировщик задач передаст управление зарегистрированной верхней половине, как только это будет возможно. При этом в большинстве случаев управление передаётся нижней половине сразу после завершения работы верхней половины. Но при этом нижняя половина работает как обычный поток ядра, и может быть прервана в любой момент, а потому она имеет право исполняться сколь угодно долго.</p></div> <div class="paragraph"><p>В качестве примера можно рассмотреть обработчик прерывания от сетевой карты, сообщающего, что принят ethernet-пакет. Этот обработчик обязан сделать две вещи:</p></div> <div class="ulist"><ul><li> <p> Взять пакет из буфера сетевой карты и сигнализировать сетевой карте, что пакет получен операционной системой. Это нужно сделать немедленно по получении прерывания, через милисекунду в буфере будут уже совсем другие данные; </p> </li><li> <p>Поместить этот пакет в какие-либо структуры ядра, выяснить, к какому протоколу он относится, передать его в соответствующие функции обработки. Это нужно сделать как можно быстрее, чтобы обеспечить максимальную производительность сетевой подсистемы, но не обязательно немедленно. </p> </li></ul></div> <div class="paragraph"><p>Соответственно, первое выполняет верхняя половина обработчика, а второе — нижняя.</p></div> <h3 id="__13">Драйвера устройств</h3> <div class="paragraph"><p>Большинство драйверов устройств обычно компилируются в виде модулей ядра. Драйвер устройства получает запросы с двух сторон:</p></div> <div class="ulist"><ul><li> <p> От устройства — через зарегистрированные драйвером обработчики прерываний; </p> </li><li> <p> От различных частей ядра — через API, который определяется конкретной подсистемой ядра и самим драйвером. </p> </li></ul></div> </div> </div> <div id="footer"> <div id="footer-text"> <table width="100%" border="0"> <tbody><tr><td> Version 1.0</td> </tr><tr><td> Last updated 2010-03-05 20:25:06 YEKST</td> </tr></tbody></table> </div> </div>Portnovhttp://www.blogger.com/profile/07229550690332111129noreply@blogger.com15tag:blogger.com,1999:blog-3628051270185801752.post-80636284155456818262010-01-03T11:44:00.000-08:002010-01-03T11:48:21.577-08:00Текущие проекты<p>Давно я что-то сюда не писал. Замотался совсем. В частности, несколько неожиданно для себя стал техническим писателем :)</p><div id="content"><div id="preamble"><div class="sectionbody"> <div class="paragraph"><p>Пока что задокументирую несколько проектов, которые у меня сейчас в более-менее вялотекущей разработке. Пожалуй, в хронологическом порядке.</p></div> </div> </div> <h2 id="_framework">Framework</h2> <div class="sectionbody"> <div class="paragraph"><p>Это фреймворк (не очень высокого уровня, на настоящий момент) для создания web-приложений на <a href="http://haskell.org/">Haskell</a>. Страница проекта <a href="http://iportnov.ru/ru/projects/framework">тут</a>, haddock-документация <a href="http://iportnov.ru/files/framework/html/">тут</a>. Проект в значительной мере исследовательский: насколько сложно/просто писать веб-приложения на Haskell? А фреймворки? Какие новые идеи способен привнести Haskell в эту область? Кроме того, изначально я задумывал фреймворк для разработки приложений высокой нагруженности (так что само собой предполагаются всякие кэширования, работа со многими СУБД и мн.др.). Во фреймворке в настоящий момент много чего не хватает (начиная с названия) — нет полноценной ORM, нет генерации произвольных диалектов SQL… Вероятно, как раз в этих областях что-нибудь интересное получится в результате. Кое что [на мой взгляд] оригинальное в фреймворке уже есть. Т.к. Haskell — компилируемый язык, то всё приложение — это один бинарник. Включая шаблоны. Шаблоны пишутся в синтаксисе, похожем на Django-вский (вообще, я многие идеи старался взять из django), при сборке приложения по ним генерируется haskell-исходник и компилируется вместе с приложением. Достоинства и недостатки, собственно, очевидны: нет затрат времени на парсинг шаблонов на каждый запрос (но и затрат сложности на кэширование шаблонов тоже нет), генерация html по шаблонам быстрее, но при изменении шаблонов надо пересобирать приложение (но если речь идёт о большой нагрузке, шаблоны будут меняться редко).</p></div> </div> <h2 id="_mypaint">MyPaint</h2> <div class="sectionbody"> <div class="paragraph"><p>Как несложно догадаться из названия, MyPaint (<a href="http://mypaint.intilinux.com/">http://mypaint.intilinux.com</a>) — это программа для рисования (дословно — что-то вроде "моя живопись"; исторически, это название — ссылка на программу paint.exe от microsoft). Программ для рисования сейчас довольно много, в том числе и свободных, и под Linux (конечно, в первую очередь на ум приходит Gimp). Особенность MyPaint — это программа в первую очередь именно для рисования, а не для обработки готовых изображений (собственно, MyPaint даже не умеет таких вещей, как "кроп" или "уровни"; за такими функциями добро пожаловать в тот же Gimp). На самом деле, ближайшие конкуренты MyPaint — это Corel Painter и ArtRage (NB: это не означает, что они идут ноздря-в-ноздрю, просто это программы одного назначения).</p></div> <div class="paragraph"><p>Этим летом мои родные-художники дорвались до компьютера, а именно до mypaint, и засыпали меня багрепортами и фичреквестами. В связи с чем я начал разрабатывать свою ветку mypaint. Сейчас мои наработки будут постепенно вливаться в основную ветку.</p></div> <div class="paragraph"><p>Коротко изменения — i18n и перевод на русский (сейчас уже в основной ветке, вместе с переводами на французский, норвежский, шведский, упрощённый китайский и др.); палитра; что-то наподобие масок; диалог слоёв; группировка кистей; поддержка наклона пера. Подробнее <a href="http://wiki.mypaint.info/index.php?title=Iportnovs_ru">на вики mypaint</a>, <a href="http://wiki.mypaint.info/index.php?title=Iportnovs_en">там же рядом бурное обсуждение интерфейса</a>.</p></div> <div class="paragraph"><p>Надеюсь в ближайшее время сделать отдельную статью про MyPaint. А пока, «с первого и по тринадцатое», собираюсь поплотнее заняться разработкой с целью сделать мои нововведения пригодными для вливания в основную ветку — релиз планируется сразу после этого мержа.</p></div> </div> <h2 id="_todos">Todos</h2> <div class="sectionbody"> <div class="paragraph"><p>Ещё один проект без нормального названия :) Это простецкий TODO-менеджер на Haskell. Собственно, сами TODO пишутся в любом текстовом редакторе в plain-text файлике (желательно, с названием TODO) в простецком формате:</p></div> <div class="literalblock"> <div class="content"> <pre><tt><spaces><spaces>[spaces]status [TAGS] title (depends) description</spaces></spaces></tt></pre> </div></div> <div class="paragraph"><p>где <spaces> <tt><spaces></spaces></tt> [spaces] — отступ пробелами, <tt>status</tt> — состояние задачи, <tt>[TAGS]</tt> — список тегов в квадратных скобках через пробел, <tt>title</tt> — заголовок или описание, <tt>(depends)</tt> — список зависимостей (заголовков других записей) в скобках через запятую, <tt>description</tt> — описание. Все поля кроме статуса и заголовка необязательны. Отступами определяется подчинённость записей, благодаря зависимостям (которые в скобках) структура может быть не только деревом, но произвольным графом (даже циклическим). Собираюсь приделать поддержку дат (чтобы, например, можно было отобрать дела, запланированные на завтра).</spaces></p></div> <div class="paragraph"><p>Сама программа только отбирает записи из файла по условиям, заданным в командной строке (см. вывод <tt>todos --help</tt>). Фильтровать можно по любому из полей. Есть «сложные» запросы, вида «задачи с тегом BUG и статусом отличным от FIXED», правда парсер таких запросов работает пока не идеально.</p></div> <div class="paragraph"><p>Смотреть/брать код здесь: <a href="http://gitorious.org/todos/">http://gitorious.org/todos/</a>. Компилируется GHC 6.10.4. Из зависимостей — пакет text-regex-pcre.</p></div> </div> </div> <div id="footer"> <div id="footer-text"> <table width="100%" border="0"> <tbody><tr><td> Last updated 2010-01-04 00:42:20 YEKST</td> </tr></tbody></table> </div> </div>Portnovhttp://www.blogger.com/profile/07229550690332111129noreply@blogger.com6tag:blogger.com,1999:blog-3628051270185801752.post-4860336294008659842009-04-06T23:14:00.000-07:002009-04-06T23:18:13.199-07:00Файловая система Btrfs<p><small><em>Это конспект моего доклада на семинаре, организованном нашей LUG совместно с университетом. Опять же, времени было пшик, так что доклад весьма обзорный.</em></small></p><br /><h2 id="_">Введение</h2> <div class="sectionbody"> <div class="para"><p>Речь пойдёт о файловой системе нового поколения. Традиционно ФС играла значительную роль в организации Unix-систем. И во многом именно свойствами ФС определялись свойства той или иной реализации Unix.</p></div> <div class="para"><p>Файловая система должна хранить файлы и обеспечивать доступ к ним. При этом к ней предъявляется большое количество требований, зачастую взаимоисключающих: поддержка файлов любого размера, высокая производительность операций ввода/вывода, масштабируемость и т.д. Давно стало ясно, что ни одна файловая система не может быть одинаково эффективна во всех случаях. Поэтому все современные реализации Unix поддерживают работу с несколькими типами ФС одновременно. Есть такое выражение: "Linux - это Unix сегодня", и ядро Linux поддерживает свыше 50 (!) типов ФС.</p></div> </div> <h2 id="__2">ФС нового поколения</h2> <div class="sectionbody"> <div class="para"><p>В 2005-м году компания Sun Microsystems представила файловую систему ZFS, которая стала прорывом в области файловых систем. Из-за лицензионной политики Sun ZFS не может быть включена в ядро Linux. Однако в 2007-м году началась разработка файловой системы нового поколения для Linux - Btrfs. Разработку оплачивает компания Oracle, однако код выпускается под лицензией GNU GPL и входит в ядро Linux начиная с релиза 2.6.29, вышедшего на этой неделе.</p></div> <div class="para"><p>Приведу фрагмент интервью Chris Mason - основного разработчика Btrfs:</p></div> <div class="ilist"><ul><li> <p> Опишите Btrfs своими словами. </p> </li><li> <p> Btrfs - это новая файловая система, выпускаемая под GPL, которая разрабатывается с учётом масштабируемости на очень большие объёмы носителей. Масштабируемость означает не только возможность адресовать блоки носителя, но также возможность работать с повреждениями данных и метаданных. Это означает наличие инструментов для проверки и восстановления файловой системы без отмонтирования, и интегрированную проверку контрольных сумм, чтобы определять ошибки. </p> </li></ul></div> <div class="ilist"><ul><li> <p> Является ли Btrfs наследницей какой-нибудь другой ФС? </p> </li><li> <p>Да, всех их :) Здесь много идей из ReiserFS, отложенное размещение и другие идеи из XFS. ZFS популяризовала идею, что подсчёт контрольных сумм данных может быть быстрым, и что управление логическими томами может быть лучше. Идеи по реализации управления томами пришли из AdvFS. </p> </li></ul></div> </div> <h2 id="_btrfs">Основные возможности Btrfs</h2> <div class="sectionbody"> <div class="para"><p>Итак, основные возможности, которые будут в Btrfs:</p></div> <div class="ilist"><ul><li> <p> Поддержка доступных на запись снапшотов (аналог клонов ZFS). Кроме того, здесь можно создавать снапшоты снапшотов. </p> </li></ul></div> <div class="ilist"><ul><li> <p> Поддержка субтомов --- множественных именованных корней в одной файловой системе с общим пулом хранения. </p> </li></ul></div> <div class="ilist"><ul><li> <p>Поддержка сложных многодисковых конфигураций --- RAID уровней 0, 1, 5, 6 и 10, а также реализация различных политик избыточности на уровне объектов ФС --- то есть возможно назначить, к примеру, зеркалирование для какого-либо каталога или файла. </p> </li></ul></div> <div class="ilist"><ul><li> <p> Copy-on-write (CoW) журналирование. </p> </li></ul></div> <div class="ilist"><ul><li> <p> Контроль целостности блоков данных и метаданных с помощью контрольных сумм. </p> </li></ul></div> <div class="ilist"><ul><li> <p> Зеркалирование метаданных даже в однодисковой конфигурации. </p> </li></ul></div> <div class="ilist"><ul><li> <p> Полностью распределенное блокирование. </p> </li></ul></div> <div class="ilist"><ul><li> <p> Поддержка ACL. </p> </li></ul></div> <div class="ilist"><ul><li> <p> Защита от потери данных. </p> </li></ul></div> <div class="ilist"><ul><li> <p> Выбор хэш-алгоритма. </p> </li></ul></div> <div class="ilist"><ul><li> <p> Поддержка NFS. </p> </li></ul></div> <div class="ilist"><ul><li> <p> Флаги совместимости, необходимые для изменения дискового формата в новых версиях btrfs с сохранением совместимости со старыми. </p> </li></ul></div> <div class="ilist"><ul><li> <p> Резервные копии суперблока, по крайней мере --- по одной на устройство. </p> </li></ul></div> <div class="ilist"><ul><li> <p> Скоростные приоритеты для дисков. </p> </li></ul></div> <div class="ilist"><ul><li> <p>Гибридные пулы. btrfs старается перемещать наиболее используемые данные на самое быстрое устройство, вытесняя с него "залежавшиеся" блоки. Эта политика хорошо согласуется с появившейся недавно моделью использования SSD (Solid State Drive). </p> </li></ul></div> <div class="ilist"><ul><li> <p>Балансировка данных между устройствами в btrfs возможна сразу после добавления диска к пулу, отдельной командой --- а не только постепенно, в процессе использования (как это реализовано в ZFS). </p> </li></ul></div> <div class="ilist"><ul><li> <p> Диски для горячей замены, поддержка которых появилась и в ZFS. </p> </li></ul></div> <div class="ilist"><ul><li> <p>Он-лайн конфигурирование RAID будет реализовано на уровне объектов файловой системы --- субтомов, снапшотов, файлов. Возможно будет также устанавливать некоторые параметры ввода-вывода для каталогов --- с наследованием этих свойств всеми дочерними объектами. </p> </li></ul></div> <div class="ilist"><ul><li> <p> Конвертер из ext2/3/4. </p> </li></ul></div> <div class="para"><p>Большинство из этих возможностей уже реализованы и работают.</p></div> </div> <h2 id="__3">Принципы устройства</h2> <div class="sectionbody"> <div class="para"><p>С точки зрения устройства ФС, можно выделить следующие основные моменты, которые делают возможными все перечисленные особенности в сочетании с очень хорошей производительностью:</p></div> <div class="ilist"><ul><li> <p> B-деревья везде, где они имеют смысл </p> </li><li> <p> Copy-on-write везде, где это имеет смысл </p> </li><li> <p> Политика блокировок - высокая гранулярность блокировок. </p> </li></ul></div> <div class="para"><p>B-деревья и дали название файловой системе (B-tree FS). B-деревья - это сильноветвящиеся деревья (B-деревья почему-то часто путают с двоичными деревьями, видимо, из-за буквы B, но она означает Block; у каждого узла в B-дереве обычно несколько тысяч потомков), каждый узел которых, в свою очередь, содержит большое количество записей (они обычно организуются в двоичное сбалансированное дерево; в частности, в Btrfs используются красно-чёрные деревья). Читаются и пишутся узлы B-дерева целиком, что даёт значительный выигрыш в производительности.</p></div> <div class="para"><p>Copy-on-write (CoW) - это алгоритм, предназначенный для ситуаций, когда нужно создавать много похожих объектов. Рассмотрим, например, создание снапшота ФС. Снапшот - это мгновенная копия всех данных части ФС в данный момент времени. Реализация "в лоб" предусматривает создание копий всех файлов, что займёт много времени и много дискового пространства. При использовании CoW создаётся только один новый объект - копия корневого каталога, а на всех файлах, на которые ссылается корневой, ставится специальная метка. Когда приложение пытается писать в "помеченный" каталог, ФС прозрачно для приложения делает его копию (помечая при этом все файлы в этом каталоге), и приложение пишет уже в созданную копию. Таким образом, создаются копии только изменившихся данных, и только тогда, когда данные действительно изменяются. Это позволяет сделать операцию создания снапшотов почти мгновенной даже для очень больших разделов. Это немаловажно, т.к. одно из основных требований к операции создания снапшота - атомарность, т.е. эта операция не должна прерываться (и конфликтовать) никакими другими операциями. В Btrfs CoW используется не только при создании снапшотов, но и при ведении журнала и многих внутренних операциях.</p></div> <div class="para"><p>Блокировки - это сущность, позволяющая избежать конфликтов при одновременном доступе к данным из разных потоков. Поток, который хочет внести изменение в некоторую структуру данных, сначала проверяет, не заблокирована ли она другим потоком; если заблокирована - ждёт, пока блокировка не будет освобождена, иначе сам захватывает блокировку, и освобождает её по окончании записи. Когда речь идёт о доступе к сложным структурам данных, возникает вопрос о политике блокировок. Нужно ли блокировать всю структуру целиком, или каждый элемент в отдельности, или элементы какими-то группами? Чем крупнее единица блокировки, тем меньше блокировок, и меньше накладных расходов. Чем мельче - тем более эффективно расходуется процессорное время, т.к. потокам приходится ждать гораздо меньше. Btrfs стремится блокировать как можно меньшие элементы структур (но не слишком мелкие).</p></div> <div class="para"><p>Ещё один пункт, связанный с блокировками, специфичен для ядра. В ядре есть два вида блокировок: spin-lock и мьютексы. При использовании spin-lock ожидающий поток "крутится" в бесконечном цикле. При использовании мьютексов - поток переходит в заблокированное состояние TASK_INTERRUPTIBLE, и "пробуждается" планировщиком автоматически при освобождении блокировки. Понятно, что мьютексы более эффективны, т.к. не тратят процессорное время на пустые циклы. Но мьютексы не могут быть использованы в контексте обработчика прерывания, т.к. в этом состоянии планировщик не работает. Значительная часть функций любой ФС может быть вызвана как из обработчика прерывания, так и в контексте задачи. Поэтому во многих функциях приходится использовать менее эффективные спин-блокировки.</p></div> <div class="para"><p>Btrfs использует новый тип блокировок, которые могут работать в обоих режимах (и их можно переключать между режимами). Таким образом, один и тот же код будет использовать мьютексы в контексте задачи и спин-блокировки в режиме прерывания.</p></div> <div class="para"><p>Ещё одна особенность Btrfs: все структуры ФС могут находиться в произвольных местах раздела, они связаны между собой указателями. Этой особенностью обладают и некоторые другие ФС, но разработчики Btrfs нашли ей новое применение: конвертация разделов из других ФС (сейчас реализована конвертация из ext2/3, конвертер из ext4 в разработке, теоретически можно создать конвертеры из других ФС). При конвертации структуры Btrfs создаются в местах раздела, помеченных в исходной ФС как свободные. В Btrfs создаётся специальный файл, в который входят блоки, занятые структурами исходной ФС. Таким образом, эти блоки оказываются помеченными как занятые. Кроме того, этот файл представляет собой образ исходной ФС, который можно примонтировать (mount -o loop). Это позволяет выполнить откат к предыдущей ФС. Чтобы освободить место на диске, достаточно просто удалить файл с образом исходной ФС (возможность отката, соответственно, пропадёт).</p></div> <div class="para"><p>Одна из особенностей современных ФС не так давно вызвала небольшой скандал. Дело в том, что в ядрах unix (и linux) код ФС не занимается непосредственно записью данных на диск. Данные записываются в страницы памяти, и эти страницы помечаются как "грязные", и затем их сбрасывает на диск отдельный поток ядра (pdflush). Так вот, при использовании современных ФС (в той новости речь шла про ext4, но теми же свойствами обладают и XFS, и Btrfs, и многие другие) интервал между записью данных в страничный кэш и их записью на диск может достигать 150 секунд (больше двух минут). Unix традиционно пишется для хорошего оборудования. В частности, предполагается, что в любой системе, от которой нужна надёжность, применяются UPS. Поэтому большая задержка записи является не недостатком, а преимуществом: это даёт возможность разместить данные более удачно, и избежать фрагментации. А при использовании на менее надёжном оборудовании нужно просто перенастроить ядро средствами sysctl, чтобы заставить pdflush срабатывать чаще.</p></div> </div> <h2 id="_btrfs_vs_ext4">Btrfs vs Ext4</h2> <div class="sectionbody"> <div class="para"><p>Другая недавно вышедшая ФС для Linux - это Ext4. Она учитывает многие наработки современных ФС (экстенты, delayed allocation итп), но при этом основана на коде Ext3. Это более продвинутая ФС, чем та же ext3, по тестам она во многих случаях даёт бОльшую производительность и может быть рекомендована для использования на многих машинах уже сейчас. Но при этом её не назовёшь ФС нового поколения: архитектура осталась от ext3.</p></div> <div class="para"><p>Btrfs сейчас в стадии experimental, разработчики предупреждают, что сейчас её имеет смысл использовать только для тестирования и экспериментов. Даже дисковый формат до сих пор окончательно не устаканился. Но при этом это безусловно ФС нового поколения, по архитектуре она напоминает разве что ZFS, но не старые ФС linux-ядра.</p></div> </div> <h2 id="_btrfs_vs_zfs">Btrfs vs ZFS</h2> <div class="sectionbody"> <div class="para"><p>Больше всего Btrfs похожа на ZFS от компании Sun. Btrfs не поддерживает диски такого астрономического объёма, как zfs, но вряд ли это в ближайшее время будет иметь практическое значение. Зато Btrfs имеет некоторые возможности, отсутствующие в zfs: снапшоты снапшотов и скоростные приоритеты дисков, оптимизацию для ssd-накопителей. Но ZFS уже вовсю используется на production-серверах, а использование btrfs станет массовым, видимо, года через два (если предполагать, что распространение btrfs будет развиваться также, как zfs).</p></div> <div class="para"><p>Измерения производительности Btrfs сейчас мало информативны, т.к. оптимизация в разгаре. Однако уже сейчас Btrfs обходит zfs по производительности некоторых операций.</p></div> </div> <h2 id="__4">Текущее состояние разработки</h2> <div class="sectionbody"> <div class="para"><p>Основные возможности Btrfs уже реализованы. Дисковый формат близок к стабилизации, если он и будет меняться, то не сильно. Только что завершена реализация обработки ситуации нехватки места на диске (проблема в том, что фактическая запись на диск может происходить уже после закрытия файла программой, и было не совсем очевидно, как передать ошибку записи программе). Вовсю идёт поиск и исправление других ошибок. В разработке специальный ioctl-API для поддержки транзакционного I/O (несколько операций, объединённых в транзакцию, могут быть выполнены все или не выполнены совсем; кроме всего прочего, это позволяет минимизировать количество проверок между операциями в одной транзакции). Ближайшая задача - реализация удаления снапшотов, первый вариант кода уже появился в рассылке.</p></div> </div>Portnovhttp://www.blogger.com/profile/07229550690332111129noreply@blogger.com15tag:blogger.com,1999:blog-3628051270185801752.post-56611271065785883462009-02-14T03:00:00.000-08:002009-02-16T21:40:18.215-08:00Обзор свободного математического ПО<p><small><i>Это конспект моего доклада на <a href="http://lug-mgn.ru/news/master-klass-po-opensource-produktam">семинаре, организованном нашей LUG совместно с университетом</a>. Соответственно, я не мог охватить всё - у меня на доклад было где-то 15 минут.</i></small></p><br /><br /><h2 id="_">Вступление</h2> <div class="sectionbody"> <div class="para"><div class="title">Известные пакеты - это гиганты <em>всё-в-одном</em></div><p>Когда мы говорим о математическом ПО, на ум приходят такие гиганты, как Maple, Mathematica, MatLAB… У них есть одно общее свойство: они пытаются охватить всё. Конечно, Mathematica известна прежде всего как система для символьных вычислений, а Matlab - для численных, но одновременно в Mathematica есть мощные алгоритмы для вычислений с плавающей точкой, а в Matlab - пакет для символьных вычислений. Причём эти <em>второстепенные</em> функции в программах по сравнению с программами, для этого предназначенными, выглядят убого и смешно. А небезызвестный MathCAD пытается включить в себя всё, при этом всё реализовано так себе. Причина проста: нельзя объять необъятное.</p></div> <div class="para"><div class="title">Свободные программы - делают одно дело хорошо</div><p>В противоположность этому, большинство свободных программ следует философии UNIX, гласящей: программа должна делать одно дело, но делать его хорошо. Свободного математического ПО очень много, при этом бóльшая часть их предназначена для какой-нибудь одной задачи. Например, есть программы, которые только и умеют, что строить сетку для метода конечных разностей. Или программа, которая предназначена для вычисления цифр числа Пи. Или программа, которая умеет только строить графики, но зато очень хорошо.</p></div> <div class="para"><p>Однако, есть и программы, в той или иной степени являющиеся аналогами известных пакетов. Я расскажу о трёх.</p></div> </div> <h2 id="_maxima">Символьные вычисления: Maxima</h2> <div class="sectionbody"> <div class="para"><div class="title">История проекта</div><p>Начну я с истории этого проекта.</p></div> <div class="para"><p>Сначала я напомню, что компьютеры - это, вообще-то, Электронные Вычислительные Машины, они создавались для вычислений над числами. Однако уже в конце 50-х появилась идея, что можно заставить компьютер работать не только с числами, но и с алгебраическими выражениями. В начале 60-х начали появляться первые системы компьютерной алгебры. И, конечно, такая система нужна была одному мирному американскому ведомству (департаменту энергетики, это практически подразделение Пентагона). Был объявлен тендер, и его выиграл проект под названием Macsyma (пишется через CS). В течение многих лет DOE Macsyma развивалась как коммерческий проект, финансируемый правительством. В 1982-м году Уильям Шелтер создал форк Macsyma, называемый Maxima. В начале 90-х распался СССР, кончилась холодная война, и косвенным следствием этого стало практически полное прекращение финансирования DOE Macsyma. К концу 90-х проект практически загнулся. Исходники Macsyma по кусочкам распродали, и они оказались в Maple и Mathematica. В 1998-м Уильям Шелтер добился от DOE разрешения на публикацию исходных текстов Maxima под лицензией GPL. Maxima стала свободной программой. В 2001-м Шелтер скончался, но к этому моменту над Maxima работало уже довольно много людей, и они подхватили проект.</p></div> <div class="para"><div class="title">Интерфейс: командная строка или wxMaxima</div><p>Maxima имеет традиционный для UNIX интерфейс командной строки, однако также умеет слушать сетевой порт, работая как сервер. Этот факт используют различные оболочки (фронтенды), предоставляющие графический интерфейс. Наиболее распространены TeXmacs и wxMaxima. TeXmacs - это научный текстовый редактор, в котором можно в документ вставить сессию Maxima. wxMaxima выглядит примерно так:</p></div> <div class="para"><p><span class="image"> <img src="http://iportnov.ru/files/math-report/wxmaxima.png" alt="wxmaxima.png" /> </span></p></div> <div class="para"><p>Последняя версия, 0.8.0, стала больше походить на Mathematica и Maple: раньше командная строка для ввода была отдельно, внизу.</p></div> <div class="para"><div class="title">Lisp-подобный язык</div><p>Язык Maxima берёт основные идеи из Lisp, так как Maxima написана на Lisp-e. При этом он похож одновременно на языки Mathematica и Maple, так как эти программы позаимствовали многие идеи и часть кода из Macsyma. Чтобы избежать долгого и нудного перечисления возможностей, я приведу пример решения типичных задач с первого курса.</p></div> <div class="para"><div class="title">Пример</div><p>Пусть дана функция</p></div> <div class="literalblock"> <div class="content"> <pre><tt>maxima>> f(x) := x*tanh(x) + x + 1/x + 2;</tt></pre> </div></div> <div class="para"><p><span class="image"> <img src="http://iportnov.ru/files/math-report/img_6c5de9b1e9.png" alt="img_6c5de9b1e9.png" /> </span></p></div> <div class="para"><p>Проверим, не является ли она чётной или нечётной:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>maxima>> f(-x);</tt></pre> </div></div> <div class="para"><p><span class="image"> <img src="http://iportnov.ru/files/math-report/img_c6e53363be.png" alt="img_c6e53363be.png" /> </span></p></div> <div class="para"><p>Как видим, функция не является ни чётной, ни нечётной. Найдём пределы функции на плюс-минус бесконечности:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>maxima>> limit(f(x),x,-inf);</tt></pre> </div></div> <div class="para"><p><span class="image"> <img src="http://iportnov.ru/files/math-report/img_fa950582c8.png" alt="img_fa950582c8.png" /> </span></p></div> <div class="literalblock"> <div class="content"> <pre><tt>maxima>> limit(f(x),x,inf);</tt></pre> </div></div> <div class="para"><p><span class="image"> <img src="http://iportnov.ru/files/math-report/img_87feb85b90.png" alt="img_87feb85b90.png" /> </span></p></div> <div class="para"><p>Итак, на плюс бесконечности функция уходит в бесконечность. Нет ли у неё наклонной асимптоты?</p></div> <div class="literalblock"> <div class="content"> <pre><tt>maxima>> limit(f(x)/x, x,inf);</tt></pre> </div></div> <div class="para"><p><span class="image"> <img src="http://iportnov.ru/files/math-report/img_fa950582c8.png" alt="img_fa950582c8.png" /> </span></p></div> <div class="para"><p>Наклонная асимптота есть - <em>y=kx+b</em>, причём k=2. Найдём <em>b</em>:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>maxima>> limit(f(x)-2*x, x,inf);</tt></pre> </div></div> <div class="para"><p><span class="image"> <img src="http://iportnov.ru/files/math-report/img_fa950582c8.png" alt="img_fa950582c8.png" /> </span></p></div> <div class="para"><p>Наконец, построим график:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>maxima>> plot2d(f(x), [x,-5,5], [y,-10,10]);</tt></pre> </div></div> <div class="para"><p><span class="image"> <img src="http://iportnov.ru/files/math-report/plot_994344a98e.png" alt="plot_994344a98e.png" /> </span></p></div> <div class="para"><p>Найдём производную нашей функции:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>maxima>> diff(f(x),x);</tt></pre> </div></div> <div class="para"><p><span class="image"> <img src="http://iportnov.ru/files/math-report/img_f6d2ff964a.png" alt="img_f6d2ff964a.png" /> </span></p></div> <div class="para"><p>И заодно - неопределённый интеграл:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>maxima>> integrate(f(x), x);</tt></pre> </div></div> <div class="para"><p><span class="image"> <img src="http://iportnov.ru/files/math-report/img_d616f08f2e.png" alt="img_d616f08f2e.png" /> </span></p></div> <div class="para"><p>Интеграл до конца "не взялся". Можно показать, что этот интеграл в элементарных функциях и не берётся. Однако Maxima умеет брать некоторые из таких интегралов, используя специальные функции:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>maxima>> part: risch(x/(exp(2*x)+1), x);</tt></pre> </div></div> <div class="para"><p><span class="image"> <img src="http://iportnov.ru/files/math-report/img_af26a97797.png" alt="img_af26a97797.png" /> </span></p></div> <div class="para"><p>(здесь я присваиваю результат интегрирования переменной part). Таким образом, интеграл <em>f(x)</em> будет равен</p></div> <div class="literalblock"> <div class="content"> <pre><tt>maxima>> ir: -2*part + log(x) + x^2 + 2*x;</tt></pre> </div></div> <div class="para"><p><span class="image"> <img src="http://iportnov.ru/files/math-report/img_647150032b.png" alt="img_647150032b.png" /> </span></p></div> <div class="para"><p>Что-то ужасное. Раскроем скобки:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>maxima>> expand(ir);</tt></pre> </div></div> <div class="para"><p><span class="image"> <img src="http://iportnov.ru/files/math-report/img_4a9ed800a2.png" alt="img_4a9ed800a2.png" /> </span></p></div> <div class="para"><div class="title">Дифференциальные уравнения</div><p>Или вот пример более сложных вычислений. Пусть надо решить дифференциальное уравнение:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>maxima>> eq: 'diff(y,x) + x*y = 1-x^2;</tt></pre> </div></div> <div class="para"><p><span class="image"> <img src="http://iportnov.ru/files/math-report/img_4d77ef0914.png" alt="img_4d77ef0914.png" /> </span></p></div> <div class="para"><p>Знак апострофа здесь используется, чтобы указать, что не надо сейчас вычислять производную, а сохранить обозначение.</p></div> <div class="literalblock"> <div class="content"> <pre><tt>maxima>> solution: ode2(eq,y,x);</tt></pre> </div></div> <div class="para"><p><span class="image"> <img src="http://iportnov.ru/files/math-report/img_3700eead6d.png" alt="img_3700eead6d.png" /> </span></p></div> <div class="para"><p>Вот и решение. erf здесь - это специальная функция, известная как функция ошибки Лапласа. После раскрытия скобок получим вот что:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>maxima>> expand(solution);</tt></pre> </div></div> <div class="para"><p><span class="image"> <img src="http://iportnov.ru/files/math-report/img_64b81111f9.png" alt="img_64b81111f9.png" /> </span></p></div> <div class="para"><p>По Maxima есть некоторое количество русскоязычных руководств, которые можно найти в интернете. На мой взгляд, самое удачное введение с обзором возможностей содержится в цикле статей Тихона Тарнавского в журнале LinuxFormat. Сейчас эти статьи выложены в открытый доступ, в том числе на русском сайте Maxima. Документация по продвинутым возможностям maxima существует, к сожалению, только на английском языке. Официальная документация составляет 712 страниц.</p></div> </div> <h2 id="_scilab">Численные вычисления: Scilab</h2> <div class="sectionbody"> <div class="para"><div class="title">Scilab совместим с MatLAB-ом</div><p>Наиболее известный пакет для численных расчётов - это MatLAB. Scilab создавался как конкурент matlab-а, более скромный по ценовой политике. Однако коммерчески проект себя не оправдал, и исходные коды были открыты под лицензией, похожей на GNU GPL. Язык scilab сделан по возможности совместимым с матлабом, так что большинство ваших наработок из matlab заработают в scilab. Только вот, как известно, основная мощь matlab-a сосредоточена в его тулбоксах - отдельно поставляемых модулях. Модули для scilab-а тоже есть, однако их сильно меньше.</p></div> <div class="para"><p><span class="image"> <img src="http://iportnov.ru/files/math-report/scilab.png" alt="scilab.png" /> </span></p></div> <div class="para"><div class="title">Octave - это GPL-аналог Matlab</div><p>Позже появился проект GNU Octave, нацеленный на создание аналога matlab-a, распространяемого по GNU GPL без всяких заморочек. Язык тоже практически совместим с матлабом, но здесь нет аналога Simulink - средства моделирования и симулирования динамических систем.</p></div> <div class="para"><p>Зато Octave имеет чисто консольный интерфейс (конечно, графические фронтенды тоже есть, самый развитый - QtOctave), что позволяет использовать его в скриптах, для автоматизации расчётов, и упрощает встраивание в сложные программные комплексы. Для Octave написаны десятки пакетов расширений.</p></div> <div class="para"><p>По Scilab есть статьи на русском языке, кроме того, не так давно в издательстве AltLinux вышла книга `Scilab: Решение инженерных и математических задач'. Книгу можно приобрести в интернет-магазине, кроме того, её электронная версия свободно доступна на сайте AltLinux.</p></div> </div> <h2 id="_gnu_r">Обработка данных: GNU R</h2> <div class="sectionbody"> <div class="para"><div class="title">Обзор</div><p>Формально, средства обработки данных относятся к программам для численных расчётов, ибо всё что они делают - это вычисления над числами. Однако, как известно, специализированный инструмент всегда лучше универсального. Под словами <em>обработка данных</em> скрывается довольно много различных видов деятельности: статистический анализ, статистическое моделирование, выборка только нужных данных, преобразование данных, построение различных графиков и гистограмм.</p></div> <div class="para"><p>Программы для обработки данных можно разделить по типичному размеру выборки, для которого они предназначены. Для небольших выборок подойдёт, например, Statistica. Для средних по размеру выборок хорошо подходит GNU R (она хранит все данные в оперативной памяти, так что на типичном PC получим ограничение в 1-2-4 гигабайта). Для больших и очень больших объёмов данных (от сотен гигабайт до сотен терабайт) предназначены разработанные в CERN свободные системы PAW и ROOT.</p></div> <div class="para"><p>GNU R - это интерпретируемый язык программироваммирования, предназначенный для статистического анализа и моделирования. R - это свободная реализация давно существующего языка S. Язык этот весьма эклектичен, он местами похож на C, местами - на Python, местами - на Haskell. Для GNU R существует почти полторы тысячи пакетов расширений (написанных на самом R, на C или Fortran), собранных в репозитории CRAN (Comprehensive R Archive Network).</p></div> <div class="para"><div class="title">Типы данных - числа, строки, факторы, векторы, списки и таблицы данных</div><p>Основные типы данных в языке - это числа, строки, факторы, векторы, списки и таблицы данных (data frames). Фактор - это данные, которые могут принимать одно из нескольких значений (пол; сорт дерева; логический тип и др). Векторы являются аналогами массивов - это набор из нескольких значений одного типа, размер вектора меняться не может. Тут же надо заметить, что в R нету <em>скаляров</em>; например, число - это, с точки зрения R, вектор из одного элемента. Списки - это обобщение векторов, они могут содержать объекты разных типов, и длина их может меняться. Кроме того, отдельным элементам списка можно присвоить имена, и обращаться к элементам не по номерам, а по именам. Пример:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>R>> lst <- list(1,2,3)</tt></pre> </div></div> <div class="para"><p>(присваивание в R обозначается обычно знаком <em>←</em>, хотя можно использовать и более привычное <em>=</em>; кроме того, есть форма <em>value → variable</em>). Для обращения к элементам списка по номеру используются двойные квадратные скобки:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>R>> lst[[2]]</tt></pre> </div></div> <div class="literalblock"> <div class="content"> <pre><tt>[1] 2</tt></pre> </div></div> <div class="para"><p>Назначим имена элементам списка:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>R>> names(lst) <- c('first','second','third')</tt></pre> </div></div> <div class="para"><p>(функция <em>c</em> создаёт векторы). Теперь к элементам списка можно обращаться по именам:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>R>> lst$third</tt></pre> </div></div> <div class="literalblock"> <div class="content"> <pre><tt>[1] 3</tt></pre> </div></div> <div class="para"><p>Таблица данных (фрейм данных) в R - это список, состоящий из векторов. Создаются таблицы данных чаще всего загрузкой из внешнего файла.</p></div> <div class="para"><div class="title">Пример</div><p>Скажем, в файле airquality.dat находятся данные замеров качества воздуха:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>"Ozone" "Solar.R" "Wind" "Temp" "Month" "Day"<br />"1" 41 190 7.4 67 5 1<br />"2" 36 118 8 72 5 2<br />"3" 12 149 12.6 74 5 3<br />"4" 18 313 11.5 62 5 4<br />"5" NA NA 14.3 56 5 5<br />"6" 28 NA 14.9 66 5 6<br />"7" 23 299 8.6 65 5 7<br />"8" 19 99 13.8 59 5 8<br />"9" 8 19 20.1 61 5 9<br />"10" NA 194 8.6 69 5 10<br />.......................</tt></pre> </div></div> <div class="para"><p>В первой строке - названия полей, дальше идут сами данные. Пропущенные (неизвестные) данные обозначены как NA. Загрузим эти данные в R:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>R>> air <- read.table('airquality.dat', sep=' ', header=TRUE)</tt></pre> </div></div> <div class="para"><p>Здесь мы указываем имя файла, разделитель (пробел), а также указываем, что в первой строке записаны имена полей. К полям таблицы мы можем теперь обращаться как к элементам списка - например, air$Ozone. Посмотрим, что R знает о структуре наших данных:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>R>> str(air)</tt></pre> </div></div> <div class="literalblock"> <div class="content"> <pre><tt>'data.frame': 153 obs. of 6 variables:<br />$ Ozone : int 41 36 12 18 NA 28 23 19 8 NA ...<br />$ Solar.R: int 190 118 149 313 NA NA 299 99 19 194 ...<br />$ Wind : num 7.4 8 12.6 11.5 14.3 14.9 8.6 13.8 20.1 8.6 ...<br />$ Temp : int 67 72 74 62 56 66 65 59 61 69 ...<br />$ Month : int 5 5 5 5 5 5 5 5 5 5 ...<br />$ Day : int 1 2 3 4 5 6 7 8 9 10 ...</tt></pre> </div></div> <div class="para"><p>Теперь мы можем, например, посмотреть описательную статистику по всем полям таблицы:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>R>> summary(air)</tt></pre> </div></div> <div class="literalblock"> <div class="content"> <pre><tt> Ozone Solar.R Wind Temp<br />Min. : 1.00 Min. : 7.0 Min. : 1.700 Min. :56.00<br />1st Qu.: 18.00 1st Qu.:115.8 1st Qu.: 7.400 1st Qu.:72.00<br />Median : 31.50 Median :205.0 Median : 9.700 Median :79.00<br />Mean : 42.13 Mean :185.9 Mean : 9.958 Mean :77.88<br />3rd Qu.: 63.25 3rd Qu.:258.8 3rd Qu.:11.500 3rd Qu.:85.00<br />Max. :168.00 Max. :334.0 Max. :20.700 Max. :97.00<br />NA's : 37.00 NA's : 7.0<br />Month Day<br />Min. :5.000 Min. : 1.00<br />1st Qu.:6.000 1st Qu.: 8.00<br />Median :7.000 Median :16.00<br />Mean :6.993 Mean :15.80<br />3rd Qu.:8.000 3rd Qu.:23.00<br />Max. :9.000 Max. :31.00</tt></pre> </div></div> <div class="para"><p>Для каждого поля показаны минимум, максимум, медиана и две квартили, среднее значение и количество пропущенных данных. Осталось только среднеквадратичное отклонение:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>R>> sd(air)</tt></pre> </div></div> <div class="literalblock"> <div class="content"> <pre><tt>Ozone Solar.R Wind Temp Month Day<br />NA NA 3.523001 9.465270 1.416522 8.864520</tt></pre> </div></div> <div class="para"><p>Как видим, R считает среднеквадратичное отклонение для полей Ozone и Solar.R неизвестным - из-за того, что в этих полях есть пропущенные данные. Мы можем явно указать, что на пропущенные данные не надо обращать внимание:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>R>> sd(air, na.rm=TRUE)</tt></pre> </div></div> <div class="literalblock"> <div class="content"> <pre><tt> Ozone Solar.R Wind Temp Month Day<br />32.987885 90.058422 3.523001 9.465270 1.416522 8.864520</tt></pre> </div></div> <div class="para"><p>Построим простейшую линейную модель - исследуем зависимость концентрации озона от температуры:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>R>> ot <- lm(Ozone ~ Temp, data=air)</tt></pre> </div></div> <div class="literalblock"> <div class="content"> <pre><tt>R>> ot</tt></pre> </div></div> <div class="literalblock"> <div class="content"> <pre><tt>Call:<br />lm(formula = Ozone ~ Temp, data = air)</tt></pre> </div></div> <div class="literalblock"> <div class="content"> <pre><tt>Coefficients:<br />(Intercept) Temp<br />-146.995 2.429</tt></pre> </div></div> <div class="para"><p>То есть, если приближать зависимость линейной <em>Ozone = k*Temp + b</em>, то <em>k=2.429</em>, а <em>b=-146.995</em>, при увеличении температуры концентрация озона в среднем растёт.</p></div> <div class="para"><p>По GNU R есть довольно много материалов на русском, в частности, методические рекомендации по лабораторным работам для вузов. Также есть хорошее введение в R, содержащееся в цикле статей А.Б. Шипунова и Е.М.Балдина в журнале LinuxFormat, сейчас эти статьи есть в открытом доступе. Продвинутая документация, к сожалению, только на английском, зато её много, включая толстые книги. Официальное руководство к R занимает 2541 страницу.</p></div> </div>Portnovhttp://www.blogger.com/profile/07229550690332111129noreply@blogger.com19tag:blogger.com,1999:blog-3628051270185801752.post-1207283991449977902009-01-01T12:36:00.000-08:002009-01-01T12:39:09.991-08:00Некоторые хитрости в использовании xmonad<div class="para"><p>Некоторое время назад я публиковал здесь статьи по настройке ion3. Всё течёт, всё меняется, и сейчас я использую другой фреймовый оконный менеджер - xmonad. С русской документацией по нему сейчас дело обстоит лучше, чем обстояло с ion3, когда я начал писать о нём. Именно, есть довольно основательная статья <a href="http://ro-che.info/docs/xmonad/">xmonad: функциональный оконный менеджер</a>. Так что с вопросами "что такое xmonad" отсылаю туда. Однако, когда есть одна только вводная документация - этого всё-таки недостаточно. Хочется примеров настройки и всяческих вкусностей. И их есть у меня! ;)</p></div> <div class="para"><p>Во вводных статьях по xmonad обычно рассматриваются три стандартные "компоновки" (способа автоматического расположения окон): Full, Tall и Mirror Tall. "Контрибы" xmonad содержат ещё довольно много компоновок, однако даже базовые могут использоваться более чем одним способом. Например, компоновку "Tall 1 (1/100) (2/3)" (что означает: одно мастер-окно, занимающее по ширине 2/3 экрана, за раз ширина его может меняться на 1/100) я использую для чтения документов и книг: основную часть экрана занимает окно документа, а сбоку может быть что-то ещё. Конечно, такую компоновку можно сделать и "на ходу" из стандартной Tall несколькими нажатиями (по умолчанию) mod-l, но если есть уже сделанная заготовка - проще обратиться к ней. Итак, "хитрость" первая: делайте "заготовки" из настроенных компоновок, чтобы потом быстро к ним обращаться.</p></div> <div class="para"><p>Для того, чтобы удобнее было обращаться к конкретным компоновкам, есть расширение XMonad.Layout.Named. Делаем</p></div> <div class="exampleblock"> <div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.4 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> <pre><tt>import XMonad<span style="color: rgb(153, 0, 0);">.</span>Layout<span style="color: rgb(153, 0, 0);">.</span>Named<br /></tt></pre></div></div> <div class="para"><p>и потом в определении layoutHook описываем компоновки, давая им имена. Например, вместо tiled пишем named "dwmtiled" tiled, где "dwmtiled" - выбираемое вами имя компоновки.</p></div> <div class="para"><p>По умолчанию для переключения компоновок используются сочетания mod-space (следующая компоновка) и mod-shift-space (предыдущая). Однако, когда компоновок больше чем 2-3, это становится неудобно. Удобнее переключаться сразу на нужную компоновку. Я использую для этого сочетания клавиш типа mod+буква. Чтобы такое себе устроить, подправьте в xmonad.hs строку с импортом модуля XMonad: вместо "import XMonad" напишите</p></div> <div class="exampleblock"> <div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.4 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> <pre><tt>import XMonad <span style="font-weight: bold;"><span style="color: rgb(0, 0, 0);">hiding </span></span><span style="color: rgb(153, 0, 0);">(</span> <span style="color: rgb(153, 0, 0);">(|||)</span> <span style="color: rgb(153, 0, 0);">)</span><br /></tt></pre></div></div> <div class="para"><p>Это мы указали, что не хотим использовать оператор ||| (служащий для перечисления компоновок), определённый в модуле XMonad. Зато мы будем использовать одноимённый оператор, определённый в модуле LayoutCombinators. Итак,</p></div> <div class="exampleblock"> <div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.4 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> <pre><tt>import XMonad<span style="color: rgb(153, 0, 0);">.</span>Layout<span style="color: rgb(153, 0, 0);">.</span>LayoutCombinators<br /></tt></pre></div></div> <div class="para"><p>Оператор ||| из LayoutCombinators "умнее", и позволяет переключаться сразу на нужную компоновку. Теперь описываем сочетания клавиш для этого переключения:</p></div> <div class="exampleblock"> <div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.4 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> <pre><tt><span style="color: rgb(153, 0, 0);">...</span><br /><span style="color: rgb(153, 0, 0);">,</span> <span style="color: rgb(153, 0, 0);">((</span>modMask<span style="color: rgb(153, 0, 0);">,</span> xK_d <span style="color: rgb(153, 0, 0);">),</span> sendMessage $ JumpToLayout <span style="color: rgb(255, 0, 0);">"dwmtiled"</span><span style="color: rgb(153, 0, 0);">)</span><br /><span style="color: rgb(153, 0, 0);">,</span> <span style="color: rgb(153, 0, 0);">((</span>modMask<span style="color: rgb(153, 0, 0);">,</span> xK_m <span style="color: rgb(153, 0, 0);">),</span> sendMessage $ JumpToLayout <span style="color: rgb(255, 0, 0);">"mirror"</span><span style="color: rgb(153, 0, 0);">)</span><br /><span style="color: rgb(153, 0, 0);">...</span><br /></tt></pre></div></div> <div class="para"><p>где "dwmtiled", "mirror" - имена соответствующих компоновок.</p></div> <div class="para"><p>Однако переключение на указанную компоновку - только побочная задача модуля LayoutCombinators. Главное его назначение состоит, соответственно названию, в том, чтобы комбинировать компоновки. Этот модуль содержит операторы типа ***||**. Такие операторы разбивают экран на две части, в каждой из которых работает своя компоновка. Количество звёздочек слева и справа показывает, в каком отношении разбивать экран (скажем, упомянутый оператор делит экран в отношении 3:2). Операторы с вертикальными чертами (|) делят экран по вертикали, а с наклонными (например, ***//*) - по горизонтали. Операторы, в которых две черты (вертикальные или наклонные), позволяют во время работы изменять соотношение частей экрана (перетаскивая границу мышкой), а операторы с одной чертой (например, */***) - не позволяют.</p></div> <div class="para"><p>Одна проблема с LayoutCombinators состоит в том, что для перемещения окон между разными частями экрана стандартные действия (swapUp, swapDown) не работают. Для этого приходится использовать модуль WindowNavigation, который определяет модификатор компоновки windowNavigation и действие Move (с аргументом U/D/L/R, указывающим, куда двигать окно).</p></div> <div class="para"><p>Вот пример использования LayoutCombinators:</p></div> <div class="exampleblock"> <div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.4 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> <pre><tt><span style="font-style: italic;"><span style="color: rgb(154, 25, 0);">-- Разделить экран по вертикали в отношении 3:1</span></span><br />onebig <span style="color: rgb(153, 0, 0);">=</span> <span style="font-weight: bold;"><span style="color: rgb(0, 0, 0);">windowNavigation </span></span><span style="color: rgb(153, 0, 0);">(</span>tile <span style="color: rgb(153, 0, 0);">***|*</span> coltile<span style="color: rgb(153, 0, 0);">)</span><br /> where<br /> <span style="font-style: italic;"><span style="color: rgb(154, 25, 0);">-- компоновка для левой части</span></span><br /> <span style="font-style: italic;"><span style="color: rgb(154, 25, 0);">-- master-окно занимает 3/4 по высоте</span></span><br /> tile <span style="color: rgb(153, 0, 0);">=</span> Mirror $ Tall <span style="color: rgb(153, 51, 153);">1</span> <span style="color: rgb(153, 0, 0);">(</span><span style="color: rgb(153, 51, 153);">1</span><span style="color: rgb(153, 0, 0);">/</span><span style="color: rgb(153, 51, 153);">100</span><span style="color: rgb(153, 0, 0);">)</span> <span style="color: rgb(153, 0, 0);">(</span><span style="color: rgb(153, 51, 153);">3</span><span style="color: rgb(153, 0, 0);">/</span><span style="color: rgb(153, 51, 153);">4</span><span style="color: rgb(153, 0, 0);">)</span><br /> <span style="font-style: italic;"><span style="color: rgb(154, 25, 0);">-- компоновка для правой части</span></span><br /> <span style="font-style: italic;"><span style="color: rgb(154, 25, 0);">-- располагает все окна в один столбец</span></span><br /> coltile <span style="color: rgb(153, 0, 0);">=</span> Tall <span style="color: rgb(153, 51, 153);">0</span> <span style="color: rgb(153, 0, 0);">(</span><span style="color: rgb(153, 51, 153);">1</span><span style="color: rgb(153, 0, 0);">/</span><span style="color: rgb(153, 51, 153);">100</span><span style="color: rgb(153, 0, 0);">)</span> <span style="color: rgb(153, 0, 0);">(</span><span style="color: rgb(153, 51, 153);">1</span><span style="color: rgb(153, 0, 0);">/</span><span style="color: rgb(153, 51, 153);">2</span><span style="color: rgb(153, 0, 0);">)</span><br /></tt></pre></div></div> <div class="para"><p>Здесь onebig - это компоновка, дающая одному окну большую часть экрана (3/4 по вертикали и 3/4 по горизонтали), а остальные располагающая снизу и справа от него. Кому легче один раз увидеть, чем десять раз прочитать - вот <a href="http://iportnov.ru/files/xmonad-larswm.png">пример</a> использования этой компоновки (заодно это иллюстрация к предыдущей статье).</p></div> <div class="para"><p>Ещё одна "хитрость" касается автоматического назначения свойств окнам (manageHook). xmonad по умолчанию делает диалоги "плавающими" (float), и это правильно. Только вот по умолчанию распознаются не все диалоги. В частности, по умолчанию xmonad не считает диалогами всплывающие окна Gimp-а (например, диалог кривых и пр). Однако это можно победить. Для таких окон приложения обычно выставляют свойство окна _NET_WM_WINDOW_TYPE в значение _NET_WM_WINDOW_TYPE_DIALOG. Можно заставить xmonad проверять это свойство:</p></div> <div class="exampleblock"> <div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.4 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> <pre><tt><span style="font-style: italic;"><span style="color: rgb(154, 25, 0);">-- подключаем библиотеки X11</span></span><br />import Graphics<span style="color: rgb(153, 0, 0);">.</span>X11<span style="color: rgb(153, 0, 0);">.</span>Xlib<span style="color: rgb(153, 0, 0);">.</span>Extras<br />import Foreign<span style="color: rgb(153, 0, 0);">.</span>C<span style="color: rgb(153, 0, 0);">.</span><span style="font-weight: bold;"><span style="color: rgb(0, 0, 0);">Types </span></span><span style="color: rgb(153, 0, 0);">(</span>CLong<span style="color: rgb(153, 0, 0);">)</span><br /></tt></pre></div></div> <div class="exampleblock"> <div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.4 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> <pre><tt><span style="font-style: italic;"><span style="color: rgb(154, 25, 0);">-- Взять значение свойства окна</span></span><br />getProp <span style="color: rgb(153, 0, 0);">::</span> Atom <span style="color: rgb(153, 0, 0);">-></span> Window <span style="color: rgb(153, 0, 0);">-></span> <span style="font-weight: bold;"><span style="color: rgb(0, 0, 0);">X </span></span><span style="color: rgb(153, 0, 0);">(</span>Maybe <span style="color: rgb(153, 0, 0);">[</span>CLong<span style="color: rgb(153, 0, 0);">])</span><br />getProp a w <span style="color: rgb(153, 0, 0);">=</span> withDisplay $ <span style="color: rgb(153, 0, 0);">\</span>dpy <span style="color: rgb(153, 0, 0);">-></span> io $ getWindowProperty32 dpy a w<br /></tt></pre></div></div> <div class="exampleblock"> <div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.4 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> <pre><tt><span style="font-style: italic;"><span style="color: rgb(154, 25, 0);">-- Эта функция проверяет, выставлено ли свойство окна name в значение value</span></span><br />checkAtom name value <span style="color: rgb(153, 0, 0);">=</span> ask <span style="color: rgb(153, 0, 0);">>>=</span> <span style="color: rgb(153, 0, 0);">\</span>w <span style="color: rgb(153, 0, 0);">-></span> liftX $ <span style="font-weight: bold;"><span style="color: rgb(0, 0, 255);">do</span></span><br /> a <span style="color: rgb(153, 0, 0);"><-</span> getAtom name<br /> val <span style="color: rgb(153, 0, 0);"><-</span> getAtom value<br /> mbr <span style="color: rgb(153, 0, 0);"><-</span> getProp a w<br /> case mbr of<br /> Just <span style="color: rgb(153, 0, 0);">[</span>r<span style="color: rgb(153, 0, 0);">]</span> <span style="color: rgb(153, 0, 0);">-></span> <span style="font-weight: bold;"><span style="color: rgb(0, 0, 255);">return</span></span> $ <span style="font-weight: bold;"><span style="color: rgb(0, 0, 0);">elem </span></span><span style="color: rgb(153, 0, 0);">(</span>fromIntegral r<span style="color: rgb(153, 0, 0);">)</span> <span style="color: rgb(153, 0, 0);">[</span>val<span style="color: rgb(153, 0, 0);">]</span><br /> _ <span style="color: rgb(153, 0, 0);">-></span> <span style="font-weight: bold;"><span style="color: rgb(0, 0, 255);">return</span></span> False<br /><span style="font-style: italic;"><span style="color: rgb(154, 25, 0);">-- Эта функция проверяет, является ли окно диалогом</span></span><br />checkDialog <span style="color: rgb(153, 0, 0);">=</span> checkAtom <span style="color: rgb(255, 0, 0);">"_NET_WM_WINDOW_TYPE"</span> <span style="color: rgb(255, 0, 0);">"_NET_WM_WINDOW_TYPE_DIALOG"</span><br /></tt></pre></div></div> <div class="para"><p>Другой "пунктик" - надо ещё распознавать "отрывающиеся" (tear-off) меню. Это тоже можно сделать проверкой значения атома:</p></div> <div class="exampleblock"> <div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.4 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> <pre><tt>checkMenu <span style="color: rgb(153, 0, 0);">=</span> checkAtom <span style="color: rgb(255, 0, 0);">"_NET_WM_WINDOW_TYPE"</span> <span style="color: rgb(255, 0, 0);">"_NET_WM_WINDOW_TYPE_MENU"</span><br /></tt></pre></div></div> <div class="para"><p>Объявляем соответствующие manageHook-и и добавляем их к остальным:</p></div> <div class="exampleblock"> <div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.4 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> <pre><tt><span style="font-style: italic;"><span style="color: rgb(154, 25, 0);">-- Сделать меню плавающими</span></span><br />manageMenus <span style="color: rgb(153, 0, 0);">=</span> checkMenu <span style="font-style: italic;"><span style="color: rgb(154, 25, 0);">--> doFloat</span></span><br /><span style="font-style: italic;"><span style="color: rgb(154, 25, 0);">-- Сделать диалоги плавающими</span></span><br />manageDialogs <span style="color: rgb(153, 0, 0);">=</span> checkDialog <span style="font-style: italic;"><span style="color: rgb(154, 25, 0);">--> doFloat</span></span><br /></tt></pre></div></div> <div class="exampleblock"> <div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.4 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> <pre><tt><span style="font-style: italic;"><span style="color: rgb(154, 25, 0);">-- Добавляем наши функции к остальным</span></span><br />myManageHook <span style="color: rgb(153, 0, 0);">=</span> <span style="color: rgb(153, 0, 0);">...</span> <span style="color: rgb(153, 0, 0);"><+></span> manageMenus <span style="color: rgb(153, 0, 0);"><+></span> manageDialogs<br /></tt></pre></div></div> <div class="para"><p>Xmonad реализует концепцию виртуальных десктопов (здесь они называются workspaces), как, вобщем, и большинство других оконных менеджеров. Однако известна также другая концепция - теги для окон. Теги используются, например, в dwm и awesome. Их можно использовать и в xmonad. У меня сейчас используются обе концепции параллельно.</p></div> <div class="para"><p>Чтобы использовать теги в xmonad, нужно подключить соответствующий модуль:</p></div> <div class="exampleblock"> <div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.4 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> <pre><tt>import XMonad<span style="color: rgb(153, 0, 0);">.</span>Actions<span style="color: rgb(153, 0, 0);">.</span>TagWindows<br /></tt></pre></div></div> <div class="para"><p>Я объявляю несколько функций, для пущей читабельности:</p></div> <div class="exampleblock"> <div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.4 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> <pre><tt><span style="font-style: italic;"><span style="color: rgb(154, 25, 0);">-- переместить окна, помеченные тегом name, на текущий workspace</span></span><br />showtag name <span style="color: rgb(153, 0, 0);">=</span> withTaggedGlobalP name shiftHere<br /><span style="font-style: italic;"><span style="color: rgb(154, 25, 0);">-- вкл/выкл тег name для текущего окна</span></span><br />toggletag name <span style="color: rgb(153, 0, 0);">=</span> withFocused $ <span style="color: rgb(153, 0, 0);">\</span>w <span style="color: rgb(153, 0, 0);">-></span> hasTag name w <span style="color: rgb(153, 0, 0);">>>=</span><br /> <span style="color: rgb(153, 0, 0);">(\</span>b <span style="color: rgb(153, 0, 0);">-></span> <span style="font-weight: bold;"><span style="color: rgb(0, 0, 255);">if</span></span> b <span style="font-weight: bold;"><span style="color: rgb(0, 0, 255);">then</span></span> delTag name w <span style="font-weight: bold;"><span style="color: rgb(0, 0, 255);">else</span></span> addTag name w<span style="color: rgb(153, 0, 0);">)</span><br /><span style="font-style: italic;"><span style="color: rgb(154, 25, 0);">-- снять тег name</span></span><br />remtag name <span style="color: rgb(153, 0, 0);">=</span> <span style="font-weight: bold;"><span style="color: rgb(0, 0, 0);">withFocused </span></span><span style="color: rgb(153, 0, 0);">(</span>delTag name<span style="color: rgb(153, 0, 0);">)</span><br /><span style="font-style: italic;"><span style="color: rgb(154, 25, 0);">-- перейти к следующему окну, помеченному тегом name</span></span><br />nexttagged name <span style="color: rgb(153, 0, 0);">=</span> focusDownTaggedGlobal name<br /><span style="font-style: italic;"><span style="color: rgb(154, 25, 0);">-- переместить окна с тегом name с текущего workspace на "misc"</span></span><br />shiftoff name <span style="color: rgb(153, 0, 0);">=</span> withTaggedP <span style="font-weight: bold;"><span style="color: rgb(0, 0, 0);">name </span></span><span style="color: rgb(153, 0, 0);">(</span>W<span style="color: rgb(153, 0, 0);">.</span>shiftWin <span style="color: rgb(255, 0, 0);">"misc"</span><span style="color: rgb(153, 0, 0);">)</span><br /></tt></pre></div></div> <div class="para"><p>Т.к. я использую несколько тегов, то для объявления сочетаний клавиш для перечисленных действий я ввожу отдельную функцию:</p></div> <div class="exampleblock"> <div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.4 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> <pre><tt><span style="font-style: italic;"><span style="color: rgb(154, 25, 0);">-- Объявить сочетания клавиш для тега tag с клавишей tag</span></span><br />tagkeys mask key tag <span style="color: rgb(153, 0, 0);">=</span> <span style="color: rgb(153, 0, 0);">[</span><br /><span style="color: rgb(153, 0, 0);">((</span>mod1Mask<span style="color: rgb(153, 0, 0);">,</span> key<span style="color: rgb(153, 0, 0);">),</span> showtag tag<span style="color: rgb(153, 0, 0);">),</span><br /><span style="color: rgb(153, 0, 0);">((</span>mask<span style="color: rgb(153, 0, 0);">,</span> key<span style="color: rgb(153, 0, 0);">),</span> toggletag tag<span style="color: rgb(153, 0, 0);">),</span><br /><span style="color: rgb(153, 0, 0);">((</span>mod3Mask<span style="color: rgb(153, 0, 0);">,</span> key<span style="color: rgb(153, 0, 0);">),</span> nexttagged tag<span style="color: rgb(153, 0, 0);">),</span><br /><span style="color: rgb(153, 0, 0);">((</span>mask <span style="color: rgb(153, 0, 0);">.|.</span> controlMask<span style="color: rgb(153, 0, 0);">,</span> key<span style="color: rgb(153, 0, 0);">),</span> shiftoff tag<span style="color: rgb(153, 0, 0);">)</span><br /><span style="color: rgb(153, 0, 0);">]</span><br /></tt></pre></div></div> <div class="para"><p>(mod3 у меня находится слева от цифрового ряда клавиатуры). Ну и добавляем эти сочетания к остальным:</p></div> <div class="exampleblock"> <div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.4 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> <pre><tt><span style="color: rgb(153, 0, 0);">...</span><br /><span style="font-style: italic;"><span style="color: rgb(154, 25, 0);">-- Пометить текущее окно произвольным тегом</span></span><br /><span style="color: rgb(153, 0, 0);">,</span> <span style="color: rgb(153, 0, 0);">((</span>modMask<span style="color: rgb(153, 0, 0);">,</span> xK_t<span style="color: rgb(153, 0, 0);">),</span> tagPrompt <span style="font-weight: bold;"><span style="color: rgb(0, 0, 0);">defaultXPConfig </span></span><span style="color: rgb(153, 0, 0);">(\</span>s <span style="color: rgb(153, 0, 0);">-></span> <span style="font-weight: bold;"><span style="color: rgb(0, 0, 0);">withFocused </span></span><span style="color: rgb(153, 0, 0);">(</span>addTag s<span style="color: rgb(153, 0, 0);">)))</span><br /><span style="font-style: italic;"><span style="color: rgb(154, 25, 0);">-- Снять произвольный тег</span></span><br /><span style="color: rgb(153, 0, 0);">,</span> <span style="color: rgb(153, 0, 0);">((</span>modMask <span style="color: rgb(153, 0, 0);">.|.</span> controlMask<span style="color: rgb(153, 0, 0);">,</span> xK_t<span style="color: rgb(153, 0, 0);">),</span> tagDelPrompt defaultXPConfig<span style="color: rgb(153, 0, 0);">)</span><br /><span style="font-style: italic;"><span style="color: rgb(154, 25, 0);">-- Переместить окна, помеченные произвольным тегом, на текущий workspace</span></span><br /><span style="color: rgb(153, 0, 0);">,</span> <span style="color: rgb(153, 0, 0);">((</span>mod1Mask<span style="color: rgb(153, 0, 0);">,</span> xK_t<span style="color: rgb(153, 0, 0);">),</span> tagPrompt <span style="font-weight: bold;"><span style="color: rgb(0, 0, 0);">defaultXPConfig </span></span><span style="color: rgb(153, 0, 0);">(\</span>s <span style="color: rgb(153, 0, 0);">-></span> withTaggedGlobalP s shiftHere<span style="color: rgb(153, 0, 0);">))</span><br /><span style="font-style: italic;"><span style="color: rgb(154, 25, 0);">-- Вкл/выкл тег "mark"</span></span><br /><span style="color: rgb(153, 0, 0);">,</span> <span style="color: rgb(153, 0, 0);">((</span>modMask<span style="color: rgb(153, 0, 0);">,</span> xK_grave<span style="color: rgb(153, 0, 0);">),</span> toggletag <span style="color: rgb(255, 0, 0);">"mark"</span><span style="color: rgb(153, 0, 0);">)</span><br /><span style="font-style: italic;"><span style="color: rgb(154, 25, 0);">-- Перейти к следующему окну, помеченному "mark"</span></span><br /><span style="color: rgb(153, 0, 0);">,</span> <span style="color: rgb(153, 0, 0);">((</span>mod3Mask<span style="color: rgb(153, 0, 0);">,</span> xK_grave<span style="color: rgb(153, 0, 0);">),</span> focusDownTaggedGlobal <span style="color: rgb(255, 0, 0);">"mark"</span><span style="color: rgb(153, 0, 0);">)</span><br /><span style="color: rgb(153, 0, 0);">...</span><br /><span style="color: rgb(153, 0, 0);">]</span><br /><span style="color: rgb(153, 0, 0);">++</span> <span style="color: rgb(153, 0, 0);">(</span>tagkeys modMask xK_exclam <span style="color: rgb(255, 0, 0);">"web"</span><span style="color: rgb(153, 0, 0);">)</span><br /><span style="color: rgb(153, 0, 0);">++</span> <span style="color: rgb(153, 0, 0);">(</span>tagkeys modMask xK_numbersign <span style="color: rgb(255, 0, 0);">"text"</span><span style="color: rgb(153, 0, 0);">)</span><br /><span style="color: rgb(153, 0, 0);">++</span> <span style="color: rgb(153, 0, 0);">(</span>tagkeys modMask xK_slash <span style="color: rgb(255, 0, 0);">"gfx"</span><span style="color: rgb(153, 0, 0);">)</span><br /><span style="color: rgb(153, 0, 0);">++</span> <span style="color: rgb(153, 0, 0);">(</span>tagkeys modMask xK_semicolon <span style="color: rgb(255, 0, 0);">"office"</span><span style="color: rgb(153, 0, 0);">)</span><br /><span style="color: rgb(153, 0, 0);">++</span> <span style="color: rgb(153, 0, 0);">(</span>tagkeys modMask xK_colon <span style="color: rgb(255, 0, 0);">"docs"</span><span style="color: rgb(153, 0, 0);">)</span><br /><span style="color: rgb(153, 0, 0);">++</span> <span style="color: rgb(153, 0, 0);">(</span>tagkeys modMask xK_question <span style="color: rgb(255, 0, 0);">"math"</span><span style="color: rgb(153, 0, 0);">)</span><br /><span style="color: rgb(153, 0, 0);">++</span> <span style="color: rgb(153, 0, 0);">(</span>tagkeys modMask xK_asterisk <span style="color: rgb(255, 0, 0);">"files"</span><span style="color: rgb(153, 0, 0);">)</span><br /><span style="color: rgb(153, 0, 0);">++</span> <span style="color: rgb(153, 0, 0);">(</span>tagkeys modMask xK_percent <span style="color: rgb(255, 0, 0);">"im"</span><span style="color: rgb(153, 0, 0);">)</span><br /></tt></pre></div></div> <div class="para"><p>(у меня typewriter-like раскладка клавиатуры, так что при нажатии цифровых клавиш без шифта получаются знаки препинания).</p></div> <div class="para"><p>Для полного счастья надо, чтобы некоторым окнам (отбираемым, например, по классу или заголовку) сразу назначались правильные теги. Стандартного manageHook-а для этого нет, так что приходится изобретать свой. Чтобы было покороче, я просто приведу куски своего xmonad.hs:</p></div> <div class="exampleblock"> <div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.4 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> <pre><tt>import XMonad<span style="color: rgb(153, 0, 0);">.</span>Hooks<span style="color: rgb(153, 0, 0);">.</span>XPropManage<br /><span style="color: rgb(153, 0, 0);">...</span><br /></tt></pre></div></div> <div class="exampleblock"> <div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.4 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> <pre><tt>myManageHook <span style="color: rgb(153, 0, 0);">=</span> ignoresome <span style="color: rgb(153, 0, 0);"><+></span> <span style="color: rgb(153, 0, 0);">(</span>xPropManageHook xPropMatches<span style="color: rgb(153, 0, 0);">)</span> <span style="color: rgb(153, 0, 0);"><+></span> manageMenus <span style="color: rgb(153, 0, 0);"><+></span> manageDialogs<br /></tt></pre></div></div> <div class="exampleblock"> <div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.4 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> <pre><tt>ignoresome <span style="color: rgb(153, 0, 0);">=</span> composeAll<br /> <span style="color: rgb(153, 0, 0);">[</span> className <span style="color: rgb(153, 0, 0);">=?</span> <span style="color: rgb(255, 0, 0);">"trayer"</span> <span style="font-style: italic;"><span style="color: rgb(154, 25, 0);">--> doIgnore</span></span><br /> <span style="color: rgb(153, 0, 0);">,</span> className <span style="color: rgb(153, 0, 0);">=?</span> <span style="color: rgb(255, 0, 0);">"fbpanel"</span> <span style="font-style: italic;"><span style="color: rgb(154, 25, 0);">--> doIgnore</span></span><br /> <span style="color: rgb(153, 0, 0);">,</span> className <span style="color: rgb(153, 0, 0);">=?</span> <span style="color: rgb(255, 0, 0);">"Plasma"</span> <span style="font-style: italic;"><span style="color: rgb(154, 25, 0);">--> doIgnore]</span></span><br /></tt></pre></div></div> <div class="exampleblock"> <div class="exampleblock-content"><!-- Generator: GNU source-highlight 2.4 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> <pre><tt>xPropMatches <span style="color: rgb(153, 0, 0);">::</span> <span style="color: rgb(153, 0, 0);">[</span>XPropMatch<span style="color: rgb(153, 0, 0);">]</span><br />xPropMatches <span style="color: rgb(153, 0, 0);">=</span> tagclasses <span style="color: rgb(153, 0, 0);">[</span><span style="color: rgb(255, 0, 0);">"Epiphany-browser"</span><span style="color: rgb(153, 0, 0);">,</span> <span style="color: rgb(255, 0, 0);">"Kontact"</span><span style="color: rgb(153, 0, 0);">,</span> <span style="color: rgb(255, 0, 0);">"Liferea-bin"</span><span style="color: rgb(153, 0, 0);">]</span> <span style="color: rgb(255, 0, 0);">"web"</span><br /> <span style="color: rgb(153, 0, 0);">++</span> tagclasses <span style="color: rgb(153, 0, 0);">[</span><span style="color: rgb(255, 0, 0);">"gimp"</span><span style="color: rgb(153, 0, 0);">,</span> <span style="color: rgb(255, 0, 0);">"f-spot"</span><span style="color: rgb(153, 0, 0);">,</span> <span style="color: rgb(255, 0, 0);">"Inkscape"</span><span style="color: rgb(153, 0, 0);">,</span> <span style="color: rgb(255, 0, 0);">"Eog"</span><span style="color: rgb(153, 0, 0);">]</span> <span style="color: rgb(255, 0, 0);">"gfx"</span><br /> <span style="color: rgb(153, 0, 0);">++</span> tagclasses <span style="color: rgb(153, 0, 0);">[</span><span style="color: rgb(255, 0, 0);">"gnome-terminal"</span><span style="color: rgb(153, 0, 0);">]</span> <span style="color: rgb(255, 0, 0);">"term"</span><br /> <span style="color: rgb(153, 0, 0);">++</span> tagclasses <span style="color: rgb(153, 0, 0);">[</span><span style="color: rgb(255, 0, 0);">"Gedit"</span><span style="color: rgb(153, 0, 0);">,</span> <span style="color: rgb(255, 0, 0);">"Leafpad"</span><span style="color: rgb(153, 0, 0);">,</span> <span style="color: rgb(255, 0, 0);">"Gvim"</span><span style="color: rgb(153, 0, 0);">]</span> <span style="color: rgb(255, 0, 0);">"text"</span><br /> <span style="color: rgb(153, 0, 0);">++</span> tagclasses <span style="color: rgb(153, 0, 0);">[</span><span style="color: rgb(255, 0, 0);">"Evince"</span><span style="color: rgb(153, 0, 0);">]</span> <span style="color: rgb(255, 0, 0);">"docs"</span><br /> <span style="color: rgb(153, 0, 0);">++</span> tagclasses <span style="color: rgb(153, 0, 0);">[</span><span style="color: rgb(255, 0, 0);">"Nautilus"</span><span style="color: rgb(153, 0, 0);">]</span> <span style="color: rgb(255, 0, 0);">"files"</span><br /> <span style="color: rgb(153, 0, 0);">++</span> tagclasses <span style="color: rgb(153, 0, 0);">[</span><span style="color: rgb(255, 0, 0);">"Amarok"</span><span style="color: rgb(153, 0, 0);">,</span> <span style="color: rgb(255, 0, 0);">"Rhythmbox"</span><span style="color: rgb(153, 0, 0);">,</span> <span style="color: rgb(255, 0, 0);">"Totem"</span><span style="color: rgb(153, 0, 0);">]</span> <span style="color: rgb(255, 0, 0);">"media"</span><br /> <span style="color: rgb(153, 0, 0);">++</span> tagclasses <span style="color: rgb(153, 0, 0);">[</span><span style="color: rgb(255, 0, 0);">"Wxmaxima"</span><span style="color: rgb(153, 0, 0);">]</span> <span style="color: rgb(255, 0, 0);">"math"</span><br /> <span style="color: rgb(153, 0, 0);">++</span> moveclasses <span style="color: rgb(153, 0, 0);">[</span><span style="color: rgb(255, 0, 0);">"Pidgin"</span><span style="color: rgb(153, 0, 0);">]</span> <span style="color: rgb(255, 0, 0);">"im"</span><br /> <span style="color: rgb(153, 0, 0);">++</span> floatclasses <span style="color: rgb(153, 0, 0);">[</span><span style="color: rgb(255, 0, 0);">"Qwerty.py"</span><span style="color: rgb(153, 0, 0);">]</span><br /> where<br /> ckClass cls <span style="color: rgb(153, 0, 0);">=</span> <span style="color: rgb(153, 0, 0);">[(</span>wM_CLASS<span style="color: rgb(153, 0, 0);">,</span> <span style="font-weight: bold;"><span style="color: rgb(0, 0, 0);">any </span></span><span style="color: rgb(153, 0, 0);">(</span>cls<span style="color: rgb(153, 0, 0);">==))]</span><br /> <span style="font-style: italic;"><span style="color: rgb(154, 25, 0);">-- добавить тег окну</span></span><br /> tag name <span style="color: rgb(153, 0, 0);">=</span> <span style="font-weight: bold;"><span style="color: rgb(0, 0, 0);">pmX </span></span><span style="color: rgb(153, 0, 0);">(</span>addTag name<span style="color: rgb(153, 0, 0);">)</span><br /> <span style="font-style: italic;"><span style="color: rgb(154, 25, 0);">-- добавить тег и переместить окно</span></span><br /> moveAndTag name <span style="color: rgb(153, 0, 0);">=</span> <span style="color: rgb(153, 0, 0);">(\</span>w <span style="color: rgb(153, 0, 0);">-></span> addTag name w <span style="color: rgb(153, 0, 0);">>></span> <span style="font-weight: bold;"><span style="color: rgb(0, 0, 255);">return</span></span> <span style="color: rgb(153, 0, 0);">(</span>W<span style="color: rgb(153, 0, 0);">.</span>shift name<span style="color: rgb(153, 0, 0);">))</span><br /> mkfloat <span style="color: rgb(153, 0, 0);">=</span> pmX float<br /> <span style="font-style: italic;"><span style="color: rgb(154, 25, 0);">-- пометить тегом все окна с данным классом</span></span><br /> tagclasses clss name <span style="color: rgb(153, 0, 0);">=</span> <span style="color: rgb(153, 0, 0);">[</span> <span style="color: rgb(153, 0, 0);">(</span>ckClass cls<span style="color: rgb(153, 0, 0);">,</span> tag name<span style="color: rgb(153, 0, 0);">)</span> <span style="color: rgb(153, 0, 0);">|</span> cls <span style="color: rgb(153, 0, 0);"><-</span> clss <span style="color: rgb(153, 0, 0);">]</span><br /> <span style="font-style: italic;"><span style="color: rgb(154, 25, 0);">-- переместить окна с данным классом на воркспейс ws</span></span><br /> moveclasses clss ws <span style="color: rgb(153, 0, 0);">=</span> <span style="color: rgb(153, 0, 0);">[</span> <span style="color: rgb(153, 0, 0);">(</span>ckClass cls<span style="color: rgb(153, 0, 0);">,</span> moveAndTag ws<span style="color: rgb(153, 0, 0);">)</span> <span style="color: rgb(153, 0, 0);">|</span> cls <span style="color: rgb(153, 0, 0);"><-</span> clss <span style="color: rgb(153, 0, 0);">]</span><br /> floatclasses clss <span style="color: rgb(153, 0, 0);">=</span> <span style="color: rgb(153, 0, 0);">[</span> <span style="color: rgb(153, 0, 0);">(</span>ckClass cls<span style="color: rgb(153, 0, 0);">,</span> mkfloat<span style="color: rgb(153, 0, 0);">)</span> <span style="color: rgb(153, 0, 0);">|</span> cls <span style="color: rgb(153, 0, 0);"><-</span> clss <span style="color: rgb(153, 0, 0);">]</span><br /></tt></pre></div></div><br />Это, конечно, далеко не все "фишки" xmonad. Однако, я надеюсь, кому-то это может послужить стартовой площадкой :)Portnovhttp://www.blogger.com/profile/07229550690332111129noreply@blogger.com3tag:blogger.com,1999:blog-3628051270185801752.post-46544195081191553202008-12-23T04:43:00.000-08:002008-12-23T04:49:48.113-08:00Все форматы документа из одного исходника: asciidoc сотоварищи<div class="para"><p>Я уже давно использую asciidoc для написания сколько-нибудь больших текстов. Почти все статьи в этом блоге, включая эту, подготовлены с помощью Asciidoc.</p></div> <div class="para"><p>Asciidoc - это транслятор простейшего языка разметки текста в любой другой язык разметки. Разметка asciidoc очень простая, практически вы пишете plain text, только выделяете заголовки знаками <em>=</em> в начале строки, <strong>полужирный</strong> текст - *звёздочками*, <em>курсив</em> - 'кавычками', итд. Абзацы разделяются пустой строкой. А на выходе может быть всё что угодно, это зависит от так называемого backend-a, поведение которого описывается в конфиге. В поставке доступны бэкенды для xhtml, html4 и docbook. Docbook, в свою очередь, теоретически можно отконвертировать во что угодно.</p></div> <div class="para"><p>На днях я готовил доклад для одного семинара, и мне хотелось получить его сразу в нескольких форматах: html и pdf, как минимум. И ещё бы надо к нему презентацию… И хорошо бы план доклада. И, конечно, не хочется для каждого формата готовить текст.</p></div> <div class="para"><p>HTML (точнее, xhtml 1.1) делается с помощью asciidoc. Все остальные форматы, теоретически, можно получить из docbook, который можно получить с помощью asciidoc. Только вот на практике мне так и не удалось за полдня заставить ни один из конверторов docbook нормально работать с русскими буквами. Также в комплекте asciidoc есть <em>экспериментальный</em> бэкенд latex, но он как-то странно работает с кусками кода, которые мне нужно поместить в tex-файл в неизменном виде (речь идёт о формулах): половина формул куда-то проглатываются.</p></div> <div class="para"><p>Кроме всего прочего, мне нужно в доклад включать фрагменты диалога с консольными программами (в данном случае - с maxima и с R). Так как в ходе подготовки доклада что-то может меняться, неохота каждый раз делать copy-paste из консоли. Надо бы, чтобы в исходник вставлять только запросы к программам - а вызываются программы и вставляется вывод пусть автоматически.</p></div> <div class="para"><p>В общем, в итоге я написал скрипт <a href="http://iportnov.ru/files/lmaxima.py">lmaxima.py</a>, который делает следующее: читает входной файл, и копирует его в выходной. Если встречает строку вида "program>> команды", то по пайпу передаёт эти команды указанной программе, и её ответ вставляет в выходной файл. Если встречает строку вида "program|tex>> команды" - то указанные команды оборачивает в функцию tex(). Таким образом, lmaxima.py работает как препроцессор для asciidoc. Одна из тонкостей состоит в том, как вставлять в документ формулы, которые выдаёт maxima. Если выводить надо в html, то формулы пропускаются через tex, и в выходной файл вставляется картинка (строка image:chtoto.png[]). Если же выводить надо в pdf, то lmaxima указывается ключ -i, и в выходной файл вставляется непосредственно tex-код.</p></div> <div class="para"><p>Т.к. latex-бэкенд к asciidoc работает странно, пришлось писать свой конвертер из подмножества asciidoc-разметки в tex (благо, основная часть разметки asciidoc очень простая). Называется он у меня <a href="http://iportnov.ru/files/vsml.py">vsml.py</a>. <em>Заодно</em> vsml.py умеет следующее:</p></div> <div class="ilist"><ul><li> <p> С ключом -c - добавляет в документ оглавление (latex-овская команда \tableofcontents), </p> </li><li> <p> с ключом -p - "выдирает" из исходника только заголовки, и составляет содержание документа (план доклада, в моём случае), </p> </li><li> <p> с ключом -b - создаёт исходник для презентации (класс beamer); в презентацию попадают заголовки и картинки. </p> </li></ul></div> <div class="para"><p>vsml понимает ещё и некоторые "надстройки" над синтаксисом asciidoc. Так, с помощью строчек "//{" и "//}" (asciidoc их воспринимает как комментарии) можно создавать вложенные куски текста. По умолчанию они выводятся как обычно, однако vsml.py можно задать ключ -l с числовым параметром, и он будет выводить только текст с "уровнем вложенности" не больше заданного; это позволяет оформлять более и менее необязательные части текста, и из одного исходника создавать документы разной степени подробности. А с помощью строчки вида "//.Тут заголовок" можно создавать (под)заголовки, которые не будут видны нигде, кроме презентации.</p></div> <div class="para"><p>Конечно, вручную писать все эти команды с ключами каждый раз долго, поэтому я написал небольшой Makefile:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>all: report.pdf report.html presentation.pdf plan.pdf</tt></pre> </div></div> <div class="literalblock"> <div class="content"> <pre><tt>clean:<br />rm report.pdf report.html presentation.pdf<br />rm presentation.tex report.asciidoc report.vsml<br />rm plan.tex plan.pdf</tt></pre> </div></div> <div class="literalblock"> <div class="content"> <pre><tt>plan.pdf: plan.tex<br />pdflatex $<</tt></pre> </div></div> <div class="literalblock"> <div class="content"> <pre><tt>plan.tex: report.vsml<br />vsml.py -p < $< > $@</tt></pre> </div></div> <div class="literalblock"> <div class="content"> <pre><tt>report.pdf: report.tex<br />pdflatex $<</tt></pre> </div></div> <div class="literalblock"> <div class="content"> <pre><tt>presentation.pdf: presentation.tex<br />pdflatex $<</tt></pre> </div></div> <div class="literalblock"> <div class="content"> <pre><tt>report.html: report.asciidoc<br />asciidoc $<</tt></pre> </div></div> <div class="literalblock"> <div class="content"> <pre><tt>report.asciidoc: math-report<br />lmaxima.py $< $@</tt></pre> </div></div> <div class="literalblock"> <div class="content"> <pre><tt>presentation.tex: report.vsml<br />vsml.py -b < $< > $@</tt></pre> </div></div> <div class="literalblock"> <div class="content"> <pre><tt>report.tex: report.vsml<br />vsml.py < $< > $@</tt></pre> </div></div> <div class="literalblock"> <div class="content"> <pre><tt>report.vsml: math-report<br />lmaxima.py -i $< $@ </tt></pre> </div></div><br /><br />PS: мне тут подсказывают: добавь ещё festival, оно за тебя и доклад прочитает :)Portnovhttp://www.blogger.com/profile/07229550690332111129noreply@blogger.com7tag:blogger.com,1999:blog-3628051270185801752.post-60729848330744530862008-11-23T08:32:00.000-08:002008-11-23T12:19:39.453-08:00Создание собственных виджетов в PyGTK с помощью cairo<div class="para"><p>Свободная библиотека Gtk, как известно, не отличается очень большим выбором виджетов. Но никто не мешает создавать свои собственные.</p></div> <div class="para"><p>Gtk, как известно, построена на принципах ООП, что хорошо ложится на объектную модель Python. В данном случае это означает, что наследование виджетов естественным образом соответствует наследованию классов в Питоне. Так, создав класс-потомок gtk.VBox, мы получим виджет со всеми свойствами VBox, и сможем добавлять в него нужную функциональность.</p></div> <div class="para"><p>Покажу простейший пример. Пусть мы хотим создать виджет, выглядящий как комбинация gtk.Label и gtk.Entry, т.е. поле для ввода сразу с подписью. Чтобы сделать такое непосредственно средствами gtk, нужно создать gtk.HBox, а в него поместить Label и Entry. Т.е. HBox окажется родительским виджетом для всей конструкции. Вот от него и будем <em>наследоваться</em>:</p></div> <div class="literalblock"> <div class="content"> <code></code><pre>class LabeledEntry(gtk.HBox):</pre> </div></div> <div class="para"><p>Но наш виджет довольно сильно отличается от простого HBox, поэтому нужно переопределить инициализатор:</p></div> <div class="literalblock"> <div class="content"><br /><code></code><pre> def __init__(self,label=None):<br /> gtk.HBox.__init__(self) # Вызываем инициализатор родительского класса<br /> self.label = gtk.Label(label) # Создаём текстовую метку с нужной подписью<br /> self.entry = gtk.Entry() # И поле для ввода текста<br /> self.pack_start(self.label, expand=False) # Добавляем label в создаваемый виджет<br /> self.pack_start(self.entry, expand=True) # Поле для ввода - туда же</pre> </div></div> <div class="para"><p>Теперь можно дописывать методы по собственному усмотрению. Например, логично было бы видеть методы set_text и get_text:</p></div> <div class="literalblock"> <div class="content"> <code></code><pre> def get_text(self):<br /> return self.entry.get_text()</pre> </div></div> <div class="literalblock"> <div class="content"> <code></code><pre> def set_text(self,text):<br /> self.entry.set_text(text)</pre> </div></div> <div class="para"><p>При желании можно добавить, например, get_label и set_label. Пример использования нашего виджета:</p></div> <div class="literalblock"> <div class="content"> <code></code><pre>...<br />entry = LabeledEntry("Enter some text")<br />...</pre> </div></div> <div class="para"><p>Таким образом, наследуясь от HBox или VBox, можно создавать виджеты, состоящие из нескольких готовых. Но иногда нужны виджеты, внешне не похожие ни на один из стандартных. И вот тогда выручает то, что все виджеты gtk отрисовываются с помощью Cairo, который имеет весьма простой API.</p></div> <div class="para"><p>API этот имеет много общего со многими другими <em>рисовальными</em> API. Прежде всего, нужно получить <em>контекст Cairo</em> - объект, содержащий состояние изображения. Далее для собственно рисования вызываются методы этого объекта. Наиболее часто используемые:</p></div> <div class="ilist"><ul><li> <p> cr.move_to(x,y) - переместить <em>графический указатель</em> в нужную точку холста, </p> </li><li> <p> cr.line_to(x,y) - провести линию от положения указателя до данной точки (указатель сдвинется в указанную точку), </p> </li><li> <p> cr.path_close() - делает текущую линию замкнутой, </p> </li><li> <p> cr.rectangle(x,y,w,h) - рисует прямоугольник; задаются координаты левого верхнего угла и размеры, </p> </li><li> <p> cr.set_source_rgb(r,g,b) - выбрать цвет для рисования; компоненты r,g,b измеряются от 0 до 1, </p> </li><li> <p> cr.stroke() - нарисовать контур текущей линии (выбранным цветом), </p> </li><li> <p> cr.fill() - закрасить текущую линию. </p> </li></ul></div> <div class="para"><p>Координаты измеряются <em>как обычно</em> - от левого верхнего угла вправо и вниз, в пикселах.</p></div> <div class="para"><p>Пусть нам, скажем, нужен виджет, который будет отображать простейшие линейные диаграммы. Должна быть возможность добавлять в него данные, а он должен соответственно перерисовывать диаграмму. Такие виджеты удобнее всего наследовать от gtk.DrawingArea:</p></div> <div class="literalblock"> <div class="content"> <code></code><pre> class Diagram(gtk.DrawingArea):</pre> </div></div> <div class="para"><p>Сам виджет DrawingArea выглядит как белый прямоугольник. И на нём, в соответствии с названием, можно рисовать. Пока сделаем инициализацию нашего виджета:</p></div> <div class="literalblock"> <div class="content"> <code></code><pre> def __init__(self,max=10,color=(0.8,0.8,0.6)):<br /> gtk.DrawingArea.__init__(self)<br /> self.data = [1] # Это будут данные, отображаемые виджетом<br /> self.max = max # Сколько максимум данных будет рисовать виджет<br /> self.color = color # Цвет диаграммы<br /> # Вот это, можно сказать, самое главное: привязываем рисующую процедуру к событию перерисовки виджета<br /> self.connect('expose-event', self.on_expose)</pre> </div></div> <div class="para"><p>Определяем собственно метод, который будет отрисовывать виджет:</p></div> <div class="literalblock"> <div class="content"> <code></code><pre> def on_expose(self, widget, event):</pre> </div></div> <div class="para"><p>В аргументе widget передаётся сам виджет. Первое, что нам от него нужно - это размеры и положение:</p></div> <div class="literalblock"> <div class="content"> <code></code><pre> x,y, width,height,_ = widget.window.get_geometry()</pre> </div></div> <div class="para"><p>Кроме того, нам понадобится контекст Cairo:</p></div> <div class="literalblock"> <div class="content"> <code></code><pre> cr = widget.window.cairo_create()</pre> </div></div> <div class="para"><p>Вычислим некоторые размеры:</p></div> <div class="literalblock"> <div class="content"> <code></code><pre> xpad = 0.03*self.width # Поля по горизонтали<br /> ypad = 0.07*self.height # И по вертикали<br /> w = float(self.width-2*xpad) # Ширина 'рабочей' части виджета<br /> h = float(self.height-2*ypad) # и высота<br /> M = max(self.data) # Максимум данных - он нужен, чтобы выставить масштаб по оси Y<br /> n = len(self.data) # Количество данных</pre> </div></div> <div class="literalblock"> <div class="content"> <code></code><pre> cr.rectangle(0,0,self.width,self.height) # Обозначаем прямоугольник, закрывающий весь виджет<br /> cr.set_source_rgb(1,1,1) # Выбираем белый цвет<br /> cr.fill() # Закрашиваем наш прямоугольник - это будет фон</pre> </div></div> <div class="literalblock"> <div class="content"> <code></code><pre> cr.move_to(xpad, ypad+h-h*float(self.data[0])/M) # Ставим указатель в верхний левый угол будущей диаграммы<br /> for x,y in enumerate(self.data[1:]): # Пробегаемся по всем данным<br /> cr.line_to(xpad+w*float(x+1)/(n-1), ypad+h-h*float(y)/M) # Проводим очередной отрезок ломанной<br /> cr.line_to(xpad+w, ypad+h) # Проводим правую границу диаграммы<br /> cr.line_to(xpad,ypad+h) # Теперь нижнюю границу<br /> cr.close_path() # Замыкаем ломанную - это проведёт левую границу диаграммы<br /> cr.set_source_rgb(*self.color) # Выбираем цвет<br /> cr.fill() # Закрашиваем ломанную</pre> </div></div> <div class="para"><p>Этот метод будет вызываться каждый раз, когда нужно перерисовать виджет. Конечно, стоит иметь ввиду, что если он будет выполняться долго - перерисовка виджета будет тормозить. Так что вычислений и циклов в нём должно быть минимум. Всё, что можно, следует вычислять заранее, или кэшировать.</p></div> <div class="para"><p>Ну и допишем метод для добавления данных в диаграмму:</p></div> <div class="literalblock"> <div class="content"> <code></code><pre>def accept(self,n):<br />if len(self.data) == self.max:<br /> del self.data[0] # Если данных слишком много - забываем самое старое значение<br />self.data.append(float(n)) # Добавляем число в список<br />self.queue_draw() # Этот вызов заставит виджет перерисоваться, т.е. лишний раз вызовет on_expose().</pre> </div></div> <div class="para"><p>Пример использования:</p></div> <div class="literalblock"> <div class="content"> <code></code><pre>...<br />dg = Diagram(max=20)<br />...<br />dg.accept(10)<br />dg.accept(20)<br />...</pre> </div></div>Portnovhttp://www.blogger.com/profile/07229550690332111129noreply@blogger.com2tag:blogger.com,1999:blog-3628051270185801752.post-1229860231977057742008-11-10T22:36:00.000-08:002008-11-10T22:45:17.655-08:00Typewriter-like раскладкиПо совету http://vonderer.blogspot.com/, решил попробовать использовать для русского языка раскладку пишущей машинки. Главное преимущество (для меня) - в том, что знаки препинания обычно в тексте встречаются гораздо чаще, чем цифры, а в typewriter набирать их становится проще. Заодно точка и запятая получают по отдельной клавише, и буква Ё - более удобное место. Вобщем, действительно, удобно. Правда, на привыкание ушло около недели.<br /><br />Но кроме русской раскладки есть ещё и английская. Для неё в X-ах не предусмотрено tyewriter-варианта, а хочется, потому что на переключение режима в мозгах требуется слишком много времени (цифры набирать то с шифтом, то без, и знаки препинания скачут по всей клавиатуре). Раскладка Дворака (у которой есть вариант с цифрами на верхнем уровне) - слишком другая, а я не так много набираю англоязычных текстов, чтобы изучать совсем новую раскладку (да ещё и надписи на клавишах будут мешать). Вобщем, сделал я себе typewriter-вариант английской раскладки. Выглядит это так:<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_phevwOrVXs8/SRkp7-I8RGI/AAAAAAAAAA4/Jm7896HXb1Y/s1600-h/keyboard-ustw.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 83px;" src="http://3.bp.blogspot.com/_phevwOrVXs8/SRkp7-I8RGI/AAAAAAAAAA4/Jm7896HXb1Y/s320/keyboard-ustw.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5267287349456028770" /></a><br /><br />Соответствующий код (/usr/share/X11/xkb/symbols/ustw):<br /><pre><code><br /> partial alphanumeric_keys<br /> xkb_symbols "typewriter" {<br /> include "us(basic)"<br /> name[Group1]= "US - Typewriter";<br /> key <AE01> { [exclam, 1 ] };<br /> key <AE02> { [numbersign,2 ] };<br /> key <AE03> { [slash, 3 ] };<br /> key <AE04> { [semicolon, 4 ] };<br /> key <AE05> { [colon, 5 ] };<br /> key <AE06> { [comma, 6 ] };<br /> key <AE07> { [period, 7 ] };<br /> key <AE08> { [asterisk, 8 ] };<br /> key <AE09> { [question, 9 ] };<br /> key <AE10> { [percent, 0 ] };<br /> key <BKSL> { [parenleft, parenright ] };<br /><br /> key <AC10> { [at, ampersand ] };<br /> key <AB08> { [asciicircum, less ] };<br /> key <AB09> { [dollar, greater ] };<br /> key <AB10> { [bar, backslash ] };<br /> };<br /></code></pre><br /><br />Кроме того, ещё с давних пор я использую CapsLock как специальный модификатор, превращающий некоторые буквенные клавиши в стрелки итп. Сейчас ещё захотелось на Shift-Caps повесить переключение такого режима (чтоб в браузере тексты читать, листая кнопками j/k, итп). И ещё захотелось временный переключатель из русской раскладки в английскую - иногда >/< или ещё чего набрать быстро. И, раз уж пошла такая пьянка, чтоб можно было греческие буквы побыстрее набирать (временный переключатель в греческую раскладку) (правда, я не верю, что греки пользуются фонетической раскладкой, которая в иксах под именем gr, ну да это их проблемы).<br /><br />Итак, текущие мои настройки, если кому интересно.<br /><br />/usr/share/X11/xkb/symbols/addkeys - мои раскладки:<br /><br /><pre><code><br /> partial alphanumeric_keys<br /> xkb_symbols "en" {<br /> include "ustw"<br /> name[Group1]= "US - Additional";<br /> key.type[group1]="FOUR_LEVEL";<br /> key <AC01> { [ a, A, Home, Home ] };<br /> key <AD03> { [ e, E, End, End ] };<br /> key <AC05> { [ g, G, Home, End ] };<br /> key <AC06> { [ h, H, Left, Left ] };<br /> key <AC07> { [ j, J, Down, Down ] };<br /> key <AC08> { [ k, K, Up, Up ] };<br /> key <AC09> { [ l, L, Right, Right ] };<br /> key <AC03> { [ d, D, Delete, Delete ] };<br /> key <AD10> { [ p, P, XF86ScrollUp, XF86ScrollUp ] };<br /> key <AB06> { [ n, N, XF86ScrollDown, XF86ScrollDown ] };<br /> include "addkeys(caps_switch)"<br /> };<br /><br /> partial alphanumeric_keys<br /> xkb_symbols "ru" {<br /> include "ru(typewriter)"<br /> name[Group1]= "Russia - Additional";<br /> key.type[group1]="FOUR_LEVEL";<br /> key <AC01> { [ Cyrillic_ef, Cyrillic_EF, Home, Home ] };<br /> key <AD03> { [ Cyrillic_u, Cyrillic_U, End, End ] };<br /> key <AC05> { [ Cyrillic_pe, Cyrillic_PE, Home, End ] };<br /> key <AC06> { [ Cyrillic_er, Cyrillic_ER, Left, Left ] };<br /> key <AC07> { [ Cyrillic_o, Cyrillic_O, Down, Down ] };<br /> key <AC08> { [ Cyrillic_el, Cyrillic_EL, Up, Up ] };<br /> key <AC09> { [ Cyrillic_de, Cyrillic_DE, Right, Right ] };<br /> key <AE11> { [ minus, underscore, emdash, hyphen ] };<br /> key <AE12> { [ equal, plus, notequal, plusminus ] };<br /> key <AC03> { [ Cyrillic_ve, Cyrillic_VE, Delete, Delete ] };<br /> key <AD11> { [ Cyrillic_ha, Cyrillic_HA, bracketleft, braceleft ] };<br /> key <AD12> { [Cyrillic_hardsign,Cyrillic_HARDSIGN, bracketright, braceright ] };<br /> key <AD10> { [ Cyrillic_ze, Cyrillic_ZE, XF86ScrollUp, XF86ScrollUp ] };<br /> key <AB06> { [ Cyrillic_te, Cyrillic_TE, XF86ScrollDown, XF86ScrollDown ] };<br /> include "addkeys(caps_switch)"<br /> };<br /><br /> partial alphanumeric_keys<br /> xkb_symbols "gr" {<br /> include "gr"<br /> name[Group1]= "Greek - Additional";<br /> key.type[group1]="FOUR_LEVEL";<br /> key <AC01> { [ Greek_alpha, Greek_ALPHA, Home, Home ] };<br /> key <AD03> { [ Greek_epsilon, Greek_EPSILON, End, End ] };<br /> key <AC05> { [ Greek_gamma, Greek_GAMMA, Home, End ] };<br /> key <AC06> { [ Greek_eta, Greek_ETA, Left, Left ] };<br /> key <AC07> { [ Greek_xi, Greek_XI, Down, Down ] };<br /> key <AC08> { [ Greek_kappa, Greek_KAPPA, Up, Up ] };<br /> key <AC09> { [ Greek_lambda, Greek_LAMBDA, Right, Right ] };<br /> key <AC03> { [ Greek_delta, Greek_DELTA, Delete, Delete ] };<br /> include "addkeys(caps_switch)"<br /> };<br /><br /> xkb_symbols "caps_switch" {<br /> key <CAPS> {<br /> type[Group1]="ONE_LEVEL",<br /> symbols[Group1] = [ ISO_Level3_Shift ]<br /> };<br /> modifier_map Mod5 { ISO_Level3_Shift };<br /><br /> replace key <II65> {<br /> type[Group1]="ONE_LEVEL",<br /> actions[Group1] = [ SetGroup(group=3) ],<br /> actions[Group2] = [ SetGroup(group=3) ],<br /> actions[Group3] = [ ],<br /> actions[Group4] = [ SetGroup(group=3) ]<br /> };<br /><br /> replace key <I21> {<br /> type[Group1]="ONE_LEVEL",<br /> type[Group2]="ONE_LEVEL",<br /> actions[Group1] = [ SetGroup(group=1) ],<br /> actions[Group2] = [ SetGroup(group=1) ],<br /> actions[Group3] = [ SetGroup(group=1) ],<br /> actions[Group4] = [ ]<br /> };<br /><br /> replace key <RCTL> {<br /> actions[Group1] = [ SetGroup(group=2) ],<br /> actions[Group2] = [ SetGroup(group=1) ],<br /> actions[Group3] = [ SetGroup(group=1) ],<br /> actions[Group4] = [ ],<br /> locks = yes<br /> };<br /> };<br /></code></pre><br /><br />Ну и в /etc/X11/xorg.conf:<br /><br /><pre><code><br /> Option "XkbLayout" "addkeys(en),addkeys(ru),gr"<br /> Option "XkbOptions" "grp_led:caps,compose:ralt"<br /></code></pre><br /><br /><br />Рус/лат переключается правым Ctrl, индикация лампочкой Caps. Временный переключатель в английскую раскладку на клавише <I21> (у меня она рядом с левым Ctrl). На клавише <II65> (у меня над <I21>) - временный переключатель в третью раскладку (греческие буквы иногда набрать). По Caps+буква - некоторые спецклавиши: Caps-hjkl - стрелки, Caps-a - Home, Caps-e - End, Caps-g - Home, Caps-G - End. На правом Alt - Compose.Portnovhttp://www.blogger.com/profile/07229550690332111129noreply@blogger.com8tag:blogger.com,1999:blog-3628051270185801752.post-45776950668698869392008-06-19T13:00:00.000-07:002008-06-19T13:02:51.344-07:00Deployment и Git<div id="preamble"> <div class="sectionbody"> <div class="para"><p>Сперва - что такое deployment?</p></div> <div class="para"><p>Буквальный перевод - развертывание. Речь идет о том, чтобы заставить код, написанный разработчиком в своей песочнице, заставить работать в реальных условиях на "боевых серверах". И вот во что это выливается даже в случае с одним разработчиком:</p></div> <div class="para"><p>Мой нынешний проект я разрабатываю/тестирую на своем домашнем сервере (dev-сервер, это называется). А недавно выложил на хостинг - это, так сказать, production-сервер (ну, на самом деле, это пока что разновидность тестирования). И тут появляются некоторые ньюансы:</p></div> <div class="ilist"><ul><li> <p> на dev- и production- серверах нужны разные настройки кода (например, разные пароли для коннекта к БД); </p> </li><li> <p> на dev-сервере я продолжаю разработку, добавляю новые фичи и пр. Хотелось бы, чтобы новые фичи и в production-варианте появлялись; </p> </li><li> <p>на production-сервере вылезают некоторые баги, которые по разным причинам на dev не вылезали, их приходится фиксить. Хотелось бы, чтобы эти же баги были пофиксены и в dev-версии; </p> </li><li> <p>Т.к. код еще не доведен "до кондиции", при переносе выясняется, что кое-где он написан неуниверсально (зависит от специфики того места, где работает). Для работы production-сервере его приходится после выкладывания обобщать. Конечно, нужно, чтобы и в dev-версии он был не менее общим; </p> </li><li> <p>Некоторые изменения, сделанные в коде production-версии, всё-таки специфичны именно для данного конкретного хостинга. Они в версии на dev-сервере мне совсем не нужны; </p> </li><li> <p> Кроме всего этого, я хочу держать актуальную версию кода в публичном месте, чтобы любой желающий мог его скачать. </p> </li></ul></div> <div class="para"><p>Несложно догадаться, что при попытке выполнять все эти требования "вручную" - очень быстро запутаешься в трех соснах (то бишь, версиях). Для упрощения deployment-а существует довольно много всяких разных решений. Одно из возможных - использовать Git.</p></div> <div class="para"><p>Предположим, код на dev-сервере уже под контролем git. Тогда для разворачивания проекта делаем:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>user@production.server:/project$ git init</tt></pre> </div></div> <div class="literalblock"> <div class="content"> <pre><tt>user@dev.server:~/project$ git push ssh://production.server/project master</tt></pre> </div></div> <div class="para"><p>Собственно, теперь на production.server в директории /project/ имеем копию кода с dev-сервера. Далее:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>user@production.server:/project$ git branch production<br />user@production.server:/project$ git checkout production</tt></pre> </div></div> <div class="para"><p>Это мы создали новую ветвь репозитория и переключились в нее. Теперь изменяем настройки, тестируем, фиксим баги… не забываем после каждого логически завершенного изменения делать "git commit -a" - фиксировать изменения в репозитории.</p></div> <div class="para"><p>При внесении изменений в код на dev-сервере их, конечно, тоже фиксируем. Когда захотим обновить версию на production, делаем:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>user@dev.server:~/project$ git push</tt></pre> </div></div> <div class="literalblock"> <div class="content"> <pre><tt>user@production.server:/project$ git merge master</tt></pre> </div></div> <div class="para"><p>— вливаем изменения в ветви master в production. Если нужно применить только последнее изменение в master (последний коммит), вместо git merge делаем git cherry-pick.</p></div> <div class="para"><p>Чтобы применять нужные изменения, сделанные на production, к dev-версии, делаем:</p></div> <div class="literalblock"> <div class="content"> <pre><tt>user@dev.server:~/project$ git remote add prod ssh://production.server/project</tt></pre> </div></div> <div class="para"><p>— создаем ссылку на удаленный репозиторий,</p></div> <div class="literalblock"> <div class="content"> <pre><tt>user@dev.server:~/project$ git fetch prod/production</tt></pre> </div></div> <div class="para"><p>— получаем код из ветви production,</p></div> <div class="literalblock"> <div class="content"> <pre><tt>user@dev.server:~/project$ git cherry-pick prod/production</tt></pre> </div></div> <div class="para"><p>— это если нужно применить последнее изменение в production, или</p></div> <div class="literalblock"> <div class="content"> <pre><tt>user@dev.server:~/project$ git cherry-pick идентификатор-коммита</tt></pre> </div></div> <div class="para"><p>— если нужно применить произвольный коммит.</p></div> </div> </div>Portnovhttp://www.blogger.com/profile/07229550690332111129noreply@blogger.com7