<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2russianfull.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:creativeCommons="http://backend.userland.com/creativeCommonsRssModule" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-37628976</atom:id><lastBuildDate>Tue, 10 Nov 2009 10:05:15 +0000</lastBuildDate><title>советы.блогспот.ком</title><description>Линукс, Vim, LaTeX, полезные скрипты, визуализация данных, численные расчёты, немного ФП</description><link>http://sovety.blogspot.com/</link><managingEditor>noreply@blogger.com (Сергей)</managingEditor><generator>Blogger</generator><openSearch:totalResults>178</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><creativeCommons:license>http://creativecommons.org/licenses/by-nc-sa/3.0/</creativeCommons:license><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/sovety" type="application/rss+xml" /><feedburner:emailServiceId xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">sovety</feedburner:emailServiceId><feedburner:feedburnerHostname xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">http://feedburner.google.com</feedburner:feedburnerHostname><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://add.my.yahoo.com/rss?url=http%3A%2F%2Ffeeds.feedburner.com%2Fsovety" src="http://us.i1.yimg.com/us.yimg.com/i/us/my/addtomyyahoo4.gif">Subscribe with My Yahoo!</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.newsgator.com/ngs/subscriber/subext.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2Fsovety" src="http://www.newsgator.com/images/ngsub1.gif">Subscribe with NewsGator</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://feeds.my.aol.com/add.jsp?url=http%3A%2F%2Ffeeds.feedburner.com%2Fsovety" src="http://o.aolcdn.com/favorites.my.aol.com/webmaster/ffclient/webroot/locale/en-US/images/myAOLButtonSmall.gif">Subscribe with My AOL</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.bloglines.com/sub/http://feeds.feedburner.com/sovety" src="http://www.bloglines.com/images/sub_modern11.gif">Subscribe with Bloglines</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.netvibes.com/subscribe.php?url=http%3A%2F%2Ffeeds.feedburner.com%2Fsovety" src="http://www.netvibes.com/img/add2netvibes.gif">Subscribe with Netvibes</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://fusion.google.com/add?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2Fsovety" src="http://buttons.googlesyndication.com/fusion/add.gif">Subscribe with Google</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.pageflakes.com/subscribe.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2Fsovety" src="http://www.pageflakes.com/ImageFile.ashx?instanceId=Static_4&amp;fileName=ATP_blu_91x17.gif">Subscribe with Pageflakes</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://lenta.yandex.ru/settings.xml?name=feed&amp;url=http%3A%2F%2Ffeeds.feedburner.com%2Fsovety" src="http://lenta.yandex.ru/i/addfeed.gif">?????? ? ??????.?????</feedburner:feedFlare><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37628976.post-9121910365713737468</guid><pubDate>Wed, 28 Oct 2009 14:23:00 +0000</pubDate><atom:updated>2009-10-29T14:22:32.711+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">программирование</category><category domain="http://www.blogger.com/atom/ns#">vcs</category><title>Переименование переменных и слияние изменений в Darcs</title><description>Ныне к традиционным холиварам, вроде &lt;code&gt;vi&lt;/code&gt; против &lt;code&gt;emacs&lt;/code&gt;, прибавился ещё &lt;a href="http://mercurial.selenic.com/"&gt;&lt;code&gt;hg&lt;/code&gt; (Mercurial)&lt;/a&gt; против &lt;a href="http://git-scm.com/"&gt;&lt;code&gt;git&lt;/code&gt;&lt;/a&gt;. И то, и другое — распределённые системы управления версиями (&lt;abbr title="распределённая система управления версиями"&gt;DVCS&lt;/abbr&gt;). В чём их преимущество перед старыми централизованными системами и как пользоваться новыми давно &lt;a href="http://www.developers.org.ua/archives/piranha/2008/07/09/mercurial-step-by-step-dvcs-intro/"&gt;уже&lt;/a&gt; &lt;a href="http://www.developers.org.ua/archives/piranha/2008/08/06/mercurial-basics/"&gt;написано&lt;/a&gt;. Впрочем, выбор этими двумя системами не ограничивается, &lt;a href="http://softwaremaniacs.org/blog/2009/10/11/svn-from-bzr/"&gt;отдельные маньяки&lt;/a&gt; успешно пользуются и другими системами. А среди альтернативных систем совершенно особняком стоит &lt;a href="http://www.darcs.net/"&gt;&lt;code&gt;darcs&lt;/code&gt;&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Почитал я тут руководство по &lt;code&gt;darcs&lt;/code&gt;, и обнаружил что есть у него одна удивительная возможность, которой, насколько мне известно, у его более популярных собратьев нет. А именно, поддержка замен в управляемых файлах. Например, можно переименовать в одной ветке функцию или переменную, в другой ветке делать другие изменения, затрагивающие эти же строки, а потом совершенно волшебным способом автоматически объединить изменения обеих веток. И ручное слияние изменений не потребуется. Возможность настолько необычная, что захотелось поделиться.&lt;br /&gt;&lt;br /&gt;Основное отличие &lt;code&gt;darcs&lt;/code&gt; от собратьев: он отслеживает не состояние каталога с файлами (и историю его изменений), а хранит сами изменения — патчи. А уж состояние рабочего каталога определяется просто как результат применения всех накопленных изменений-патчей. Всякое такое изменение обратимо, а некоторые можно безболезненно переставлять местами (и это очень облегчает слияния).&lt;br /&gt;&lt;br /&gt;В случае обычных DVCS, каждое изменение определяется разницей двух состояний каталога. Чтобы объединить такие изменения, нужен их общий «предок», к которому изменения можно применить. В &lt;code&gt;darcs&lt;/code&gt; изменение не обязательно должно определяться разницей между двумя состояниями каталога. Это позвляет создавать разные типы изменений, и автор может определить что именно изменение делает (семантически). В том числе есть и такой вид изменений: &lt;em&gt;замена слов&lt;/em&gt; в файле (token replace patch).&lt;br /&gt;&lt;br /&gt;Покажу, как это работает, а вы уж сами судите, насколько это круто :-)&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Завязка&lt;/h4&gt;&lt;br /&gt;&lt;br /&gt;Итак, создадим вначале исходный репозиторий и поместим в него простую программку. Тут отличия между &lt;code&gt;darcs&lt;/code&gt; и &lt;code&gt;hg&lt;/code&gt; или &lt;code&gt;git&lt;/code&gt; минимальны:&lt;br /&gt;&lt;pre class="sh_sourceCode"&gt;$ &lt;b&gt;mkdir repo-0&lt;/b&gt;&lt;br /&gt;$ &lt;b&gt;cd repo-0&lt;/b&gt;&lt;br /&gt;repo-0$ &lt;b&gt;darcs init&lt;/b&gt;&lt;br /&gt;repo-0$ &lt;b&gt;cat &amp;gt; hello.py&lt;br /&gt;def hello(what):&lt;br /&gt;    print "Hello %s" % what&lt;br /&gt;&lt;br /&gt;hello("World")&lt;br /&gt;^D&lt;/b&gt;&lt;br /&gt;repo-0$ &lt;b&gt;darcs add hello.py&lt;/b&gt;&lt;br /&gt;repo-0$ &lt;b&gt;darcs record -m 'initial commit' hello.py&lt;/b&gt;&lt;br /&gt;Recording changes in "hello.py":&lt;br /&gt;&lt;br /&gt;addfile ./hello.py&lt;br /&gt;Shall I record this change? (1/2)  [ynWsfvplxdaqjk], or ? for help: &lt;b&gt;y&lt;/b&gt;&lt;br /&gt;hunk ./hello.py 1&lt;br /&gt;+def hello(what):&lt;br /&gt;+    print "Hello %s" % what&lt;br /&gt;+&lt;br /&gt;+hello("World")&lt;br /&gt;Shall I record this change? (2/2)  [ynWsfvplxdaqjk], or ? for help: &lt;b&gt;y&lt;/b&gt;&lt;br /&gt;Finished recording patch 'initial commit'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;А теперь создадим две ветки. В каждой ветке сделаем свои изменения. В одной (A) изменим название переменной &lt;code&gt;what&lt;/code&gt; на &lt;code&gt;name&lt;/code&gt;, а в другой (B) переименуем и перепишем функцию &lt;code&gt;hello()&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Внезапно!&lt;/h4&gt;&lt;br /&gt;&lt;br /&gt;Клонируем исходный репозиторий:&lt;br /&gt;&lt;pre class="sh_sourceCode"&gt;repo-0$ &lt;b&gt;cd ..&lt;/b&gt;&lt;br /&gt;$ &lt;b&gt;darcs get repo-0 repo-A&lt;/b&gt;&lt;br /&gt;Copying patches, to get lazy repository hit ctrl-C...&lt;br /&gt;Finished getting.&lt;/pre&gt;&lt;br /&gt;И переименовываем в этой ветке переменную. Только хитрость, мы хотим не просто сделать замену слов в файле, а мы хотим явно указать &lt;code&gt;darcs&lt;/code&gt;-у, что это именно замена слов. Поэтому вместо текстового редактора выполняем такую вот команду:&lt;br /&gt;&lt;pre class="sh_sourceCode"&gt;$ &lt;b&gt;cd repo-A&lt;/b&gt;&lt;br /&gt;repo-A$ &lt;b&gt;darcs replace what name hello.py&lt;/b&gt;&lt;/pre&gt;&lt;br /&gt;Убеждаемся, что программка изменилась:&lt;br /&gt;&lt;pre class="sh_sourceCode"&gt;repo-A$ &lt;b&gt;cat hello.py&lt;/b&gt;&lt;br /&gt;def hello(name):&lt;br /&gt;    print "Hello %s" % name&lt;br /&gt;&lt;br /&gt;hello("World")&lt;/pre&gt;&lt;br /&gt;И записываем изменения в репозиторий:&lt;br /&gt;&lt;pre class="sh_sourceCode"&gt;repo-A$ &lt;b&gt;darcs record -m 'renamed: what to name' hello.py&lt;/b&gt;&lt;br /&gt;Recording changes in "hello.py":&lt;br /&gt;&lt;br /&gt;replace ./hello.py [A-Za-z_0-9] what name&lt;br /&gt;Shall I record this change? (1/1)  [ynWsfvplxdaqjk], or ? for help: &lt;b&gt;y&lt;/b&gt;&lt;br /&gt;Finished recording patch 'renamed: what to name'&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Тем временем...&lt;/h4&gt;&lt;br /&gt;&lt;br /&gt;Параллельно создаём другую ветку и как-нибудь меняем функцию &lt;code&gt;hello&lt;/code&gt;:&lt;br /&gt;&lt;pre class="sh_sourceCode"&gt;repo-A$ &lt;b&gt;cd ..&lt;/b&gt;&lt;br /&gt;$ &lt;b&gt;darcs get repo-0 repo-B&lt;/b&gt;&lt;br /&gt;Copying patches, to get lazy repository hit ctrl-C...&lt;br /&gt;Finished getting.&lt;br /&gt;$ &lt;b&gt;cd repo-B&lt;/b&gt;&lt;br /&gt;repo-B$ &lt;b&gt;cat &amp;gt; hello.py &lt;br /&gt;def hello(what):&lt;br /&gt;    if len(what) &amp;gt; 6:&lt;br /&gt;        print "Hello %s" % what&lt;br /&gt;    else:&lt;br /&gt;        print "Hi %s" % what&lt;br /&gt;&lt;br /&gt;hello("World")&lt;br /&gt;^D&lt;/b&gt;&lt;/pre&gt;&lt;br /&gt;Изменения настолько серьёзны, что старое имя функции уже не подходит. Переименовываем её с помощью &lt;code&gt;darcs replace&lt;/code&gt;:&lt;br /&gt;&lt;pre class="sh_sourceCode"&gt;repo-B$ &lt;b&gt;darcs replace hello greet hello.py&lt;/b&gt;&lt;/pre&gt;&lt;br /&gt;И записываем изменения:&lt;br /&gt;&lt;pre class="sh_sourceCode"&gt;repo-B$ &lt;b&gt;darcs record -m 'changed hello and renamed to greet' hello.py&lt;/b&gt;&lt;br /&gt;Recording changes in "hello.py":&lt;br /&gt;&lt;br /&gt;hunk ./hello.py 2&lt;br /&gt;-    print "Hello %s" % what&lt;br /&gt;+    if len(what) &amp;gt; 6:&lt;br /&gt;+        print "Hello %s" % what&lt;br /&gt;+    else:&lt;br /&gt;+        print "Hi %s" % what&lt;br /&gt;Shall I record this change? (1/2)  [ynWsfvplxdaqjk], or ? for help: &lt;b&gt;y&lt;/b&gt;&lt;br /&gt;replace ./hello.py [A-Za-z_0-9] hello greet&lt;br /&gt;Shall I record this change? (2/2)  [ynWsfvplxdaqjk], or ? for help: &lt;b&gt;y&lt;/b&gt;&lt;br /&gt;Finished recording patch 'changed hello and renamed to greet'&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Кровавый финал&lt;/h4&gt;&lt;br /&gt;&lt;br /&gt;А теперь возвращаемся в исходный репозиторий и объединяем изменения:&lt;br /&gt;&lt;pre class="sh_sourceCode"&gt;repo-B$ &lt;b&gt;cd ../repo-0&lt;/b&gt;&lt;br /&gt;repo-0$ &lt;b&gt;darcs pull ../repo-A ../repo-B&lt;/b&gt;&lt;br /&gt;Wed Oct 28 17:21:41 CET 2009  me@example.com&lt;br /&gt;  * changed hello and renamed to greet&lt;br /&gt;Shall I pull this patch? (1/2)  [ynWsfvplxdaqjk], or ? for help: &lt;b&gt;y&lt;/b&gt;&lt;br /&gt;Wed Oct 28 17:12:12 CET 2009  me@example.com&lt;br /&gt;  * renamed: what to name&lt;br /&gt;Shall I pull this patch? (2/2)  [ynWsfvplxdaqjk], or ? for help: &lt;b&gt;y&lt;/b&gt;&lt;br /&gt;Finished pulling and applying.&lt;/pre&gt;&lt;br /&gt;И что же мы видим?&lt;br /&gt;&lt;pre class="sh_sourceCode"&gt;repo-0$ &lt;b&gt;cat hello.py&lt;/b&gt;&lt;br /&gt;def greet(name):&lt;br /&gt;    if len(name) &amp;gt; 6:&lt;br /&gt;        print "Hello %s" % name&lt;br /&gt;    else:&lt;br /&gt;        print "Hi %s" % name&lt;br /&gt;&lt;br /&gt;greet("World")&lt;/pre&gt;&lt;br /&gt;Изменения &lt;em&gt;объединились правильно&lt;/em&gt;. Система управления версиями оказалась достаточно умной, чтобы применить изменения в нужном порядке (вначале переписать функцию, а уж потом переименовать все случаи использования переменной).&lt;br /&gt;&lt;br /&gt;Я впечатлён.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37628976-9121910365713737468?l=sovety.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/sovety/~4/V85I9g2LOnw" height="1" width="1"/&gt;</description><link>http://sovety.blogspot.com/2009/10/dvcs-which-can-rename-variables.html</link><author>noreply@blogger.com (Сергей)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">9</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37628976.post-407072897408407682</guid><pubDate>Fri, 16 Oct 2009 17:40:00 +0000</pubDate><atom:updated>2009-10-17T16:15:16.843+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">видео</category><category domain="http://www.blogger.com/atom/ns#">latex</category><category domain="http://www.blogger.com/atom/ns#">программирование</category><category domain="http://www.blogger.com/atom/ns#">haskell</category><category domain="http://www.blogger.com/atom/ns#">ссылки</category><category domain="http://www.blogger.com/atom/ns#">командная строка</category><category domain="http://www.blogger.com/atom/ns#">python</category><category domain="http://www.blogger.com/atom/ns#">графика</category><category domain="http://www.blogger.com/atom/ns#">openoffice</category><title>Микросоветы</title><description>&lt;blockquote style="font-style: italic; margin-left: 50%;"&gt;Всё чаще в &lt;a href="http://twitter.com/jetxee"&gt;твиттер&lt;/a&gt;&lt;br /&gt;одной строкой пост целый&lt;br /&gt;пишу на память.&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;Не растекаясь мыслею по древу и без лишних аннотаций, предлагаю вам список коротких советов и ссылок, настоящих жемчужин, накопившихся &lt;a href="http://twitter.com/jetxee"&gt;в моём твиттере&lt;/a&gt;, записанных мной самим, тщательно упорядоченных ныне по темам и ранжированных с точки зрения общечеловеческих ценностей.&lt;br /&gt;&lt;br /&gt;&lt;a href="#microsovety-tricks"&gt;Приёмы работы&lt;/a&gt;&lt;br /&gt;&lt;a href="#microsovety-latex"&gt;LaTeX и вёрстка&lt;/a&gt;&lt;br /&gt;&lt;a href="#microsovety-hacking"&gt;Программирование&lt;/a&gt;&lt;br /&gt;&lt;a href="#microsovety-soft"&gt;Находки (всякие программки)&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a name="microsovety-tricks"&gt;1.&lt;/a&gt; Приёмы работы: &lt;br /&gt;&lt;ul&gt;&lt;li&gt;Чтобы не закрывать Firefox, когда закрывается последняя вкладка по &lt;kbd&gt;Ctrl&lt;/kbd&gt;-&lt;kbd&gt;W&lt;/kbd&gt;: идём в &lt;code&gt;about:config&lt;/code&gt;, находим &lt;code&gt;browser.tabs.closeWindowWithLastTab&lt;/code&gt;, ставим &lt;code&gt;false&lt;/code&gt;. Проверено на FF&amp;nbsp;3.5.&lt;/li&gt;&lt;li&gt;OpenOffice: чтобы запретить разрыв слова (т.е. запретить перенос), вставляем нечитаемый символ U+2060 (Zero-width WORD JOINER). Символ можно найти, запустив &lt;code&gt;gucharmap&lt;/code&gt;. Надо &lt;a href="http://sovety.blogspot.com/2008/02/compose.html"&gt;в .XCompose добавить...&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Чтобы использовать новый, сжимающий &lt;a href="http://hacks.mozilla.org/2009/09/theora-1-1-released/"&gt;раза в два лучше&lt;/a&gt;, видео-кодировщик Theora&amp;nbsp;1.1, нужно взять саму новую библиотечку (уже есть в Debian unstable), и, главное, &lt;code&gt;ffmpeg2theora&lt;/code&gt; версии &lt;i&gt;не ниже 0.25&lt;/i&gt;. На сайте разработчиков есть &lt;a href="http://v2v.cc/%7Ej/ffmpeg2theora/download.html"&gt;и бинарные сборки&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Принудительное отключение подсветки ЖК-дисплея: &lt;code&gt;xset dpms force off&lt;/code&gt;. &lt;a href="http://www.shallowsky.com/linux/x-screen-blanking.html"&gt;Отсюда&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Банальность. Удаление пустых строк &lt;code&gt;sed&lt;/code&gt;-ом: &lt;code&gt;sed '/^\s*$/d'&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;Редактируя диаграммы Graphviz в Vim, быстрый просмотр по &lt;code&gt;:make&lt;/code&gt; можно сделать так: &lt;code&gt;:set makeprg=dot\ -Tpng\ %\ \\\|display\ png:- errorformat='' autowrite&lt;/code&gt;. Подставить название используемой программы (&lt;code&gt;dot&lt;/code&gt;, &lt;code&gt;neato&lt;/code&gt;, &lt;code&gt;fdp&lt;/code&gt;, ...).&lt;/li&gt;&lt;li&gt;Создание паролей (если нет KeePassX): &lt;code&gt;cat /dev/urandom | tr -d -c 'a-zA-Z0-9' | fold -w 8 | head -1&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Поиск и удаление дубликатов файлов: &lt;code&gt;fdupes&lt;/code&gt; в командной строке, &lt;code&gt;fslint&lt;/code&gt; — утилита с графическим интерфейсом.&lt;/li&gt;&lt;li&gt;В Debian можно заменить файл пакета, не пересобирая пакет. Поможет &lt;code&gt;&lt;a href="http://www.debian-administration.org/articles/118"&gt;dpkg-divert&lt;/a&gt;&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;sudo -i&lt;/code&gt; имитирует логин под рутом (даёт &lt;code&gt;#&lt;/code&gt;). Бывает полезно (раньше &lt;code&gt;sudo su -&lt;/code&gt; иногда пользовался).&lt;/li&gt;&lt;li&gt;Как создавать картинки предварительного просмотра видеофайлов: &lt;br /&gt;&lt;pre class="sh_sh"&gt;ffmpeg -itsoffset -1 -i видеофайл.avi -vcodec mjpeg -vframes 1 -an -y -f rawvideo -s 320x240 картинка.jpg ; done&lt;/pre&gt;Как создавать картинки из PDF:&lt;br /&gt;&lt;pre class="sh_sh"&gt;convert -thumbnail 300x300 документ.pdf[0] -gravity center -extent 300x300 картинка.png&lt;/pre&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a name="microsovety-latex"&gt;2.&lt;/a&gt; LaTeX и вёрстка: &lt;br /&gt;&lt;ul&gt;&lt;li&gt;Рекомендуемая минимальная ширина полей, чтобы документ можно было печатать и на A4, и на Letter — А4, слева и справа 20&amp;nbsp;мм, сверху и снизу 33&amp;nbsp;мм. &lt;a href="http://www.faqs.org/rfcs/rfc2346.html"&gt;RFC&amp;nbsp;2346&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Чтобы избежать разрыва страницы в LaTeX, можно поместить фрагмент текста в окружение &lt;code&gt;samepage&lt;/code&gt;. &lt;a href="http://www.tex.ac.uk/cgi-bin/texfaq2html?label=nopagebrk"&gt;Это частый вопрос&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Отступы элементов списка в LaTeX можно настроить, если использовать окружение &lt;code&gt;list&lt;/code&gt; вместо &lt;code&gt;itemize&lt;/code&gt;. &lt;a href="http://dikkie.net/2007/03/08/latex-indentation-itemize/"&gt;Пример&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Чтобы добавить межабзацный пробел, &lt;code&gt;\setlength{\parskip}{10pt plus 1pt minus 1pt}&lt;/code&gt;. Особенно полезно в наборе без абзацного отступа. &lt;a href="http://www.mackichan.com/techtalk/564.htm"&gt;Отсюда&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Чтобы выравнять картинку и текст справа от неё по вертикали, по середине, повозившись, сделал себе макрос &lt;code&gt;\sidebyside{}{}&lt;/code&gt;:&lt;br /&gt;&lt;pre class="sh_latex"&gt;\newsavebox{\leftbox}\newlength{\leftboxheight}\newcommand{\sidebyside}[2]{\sbox{\leftbox}{#1}\settoheight{\leftboxheight}{\usebox{\leftbox}}\usebox{\leftbox}\raisebox{0.5\leftboxheight}{#2}}&lt;/pre&gt;Смотрите &lt;a href="http://sovety.org.ru/files/sidebyside.pdf"&gt;пример использования&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Чтобы автоматически закрывать окружения LaTeX, пользователи Vim могут поставить &lt;a href="http://www.vim.org/scripts/script.php?script_id=920"&gt;плагин tex_autoclose&lt;/a&gt;. Использование: в режиме вставки &lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;\&lt;/kbd&gt;, затем &lt;kbd&gt;c&lt;/kbd&gt;.&lt;/li&gt;&lt;li&gt;Разрезать на страницы и «склеивать» PDF-документы можно с помощью &lt;a href="http://www.linux.com/archive/feature/53701"&gt;pdftk&lt;/a&gt;. Объединить два файла в один:&lt;pre class="sh_sh"&gt;pdftk первый.pdf второй.pdf cat output новый.pdf&lt;/pre&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a name="microsovety-hacking"&gt;3.&lt;/a&gt; Программирование:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;В Python, примитивное транспонирование списка пар в пару списков: &lt;br /&gt;&lt;pre class="sh_python"&gt;unzip = lambda pairs: zip(*pairs)&lt;/pre&gt;&lt;a href="http://twitter.com/vlasovskikh"&gt;@vlasovskikh&lt;/a&gt; подсказал, что для больших списков &lt;code&gt;izip&lt;/code&gt; будет быстрее (проверили, так). &lt;br /&gt;&lt;/li&gt;&lt;li&gt;Занятное и доходчивое объяснение «что такое &lt;i&gt;продолжения&lt;/i&gt;» на 11-й минуте &lt;a href="http://vimeo.com/6614042"&gt;видеопрезентации Swarm-dpl&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Быстро создавать графический интерфейс для научных программок позволяет библиотечка TraitsUI (Python). Пока не пробовал, но прочитал &lt;a href="http://gael-varoquaux.info/computers/traits_tutorial/traits_tutorial.html"&gt;урок по TraitsUI&lt;/a&gt;.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://softtalkblog.wordpress.com/2009/09/29/more-about-haskell-and-intel-concurrent-collections/"&gt;Говорят&lt;/a&gt;, Intel готовит Concurrent Collections и для Хаскеля.&lt;/li&gt;&lt;li&gt;Я же пока проснулся и прочитал про &lt;a href="http://www.chiark.greenend.org.uk/%7Esgtatham/coroutines.html"&gt;со-процедуры на Си&lt;/a&gt; и &lt;a href="http://www.lysator.liu.se/c/duffs-device.html"&gt;устройство Даффа&lt;/a&gt;. Впечатлился.&lt;/li&gt;&lt;li&gt;Хотите полюбоваться,  как можно добавлять побочные вычисления «наследованием» типов? Вот, пожалуйста, в &lt;a href="http://cdsmith.wordpress.com/2009/09/20/side-computations-via-type-classes/"&gt;этом примере&lt;/a&gt; (на Хаскеле). Хотя это, конечно &lt;a href="http://harmful.cat-v.org/Blog/2009/08/05/0-java-factory/"&gt;не Java&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Учился использвать монадные трансформеры (бррр!) — оказалось несложно. В результате получился такой &lt;a href="http://hpaste.org/fastcgi/hpaste.fcgi/view?id=5991"&gt;пример использования StateT поверх IO&lt;/a&gt;. Может кому пригодится.&lt;/li&gt;&lt;li&gt;Мелкое копирование словарей в Python — грабли.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a name="microsovety-soft"&gt;4.&lt;/a&gt; Находки (всякие программки): &lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://repo.cat-v.org/atrack/"&gt;Atrack&lt;/a&gt; — анонимный открытый битторент трекер для Google App Engine. Всего 246 строк кода.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.sweethome3d.eu/ru/index.jsp"&gt;Sweet Home 3D&lt;/a&gt; — программа для планирования интерьера. Можно рисовать планы комнат, расставлять мебель, крутить по всякому. Сделана красиво.&lt;/li&gt;&lt;li&gt;&lt;a href="http://code.google.com/p/fuse-zip/"&gt;fuse-zip&lt;/a&gt; — файловая система FUSE для монтирования zip-архивов. Быстрая, легко собирается по &lt;code&gt;make&lt;/code&gt;, умеет писать в архив. Использование: &lt;pre class="sh_sh"&gt;fuse-zip архив.zip /точка/монтирования&lt;/pre&gt;Есть также &lt;a href="http://avf.sourceforge.net/"&gt;avfs&lt;/a&gt;, которая монтирует любые архивы, но не пишет и не такая удобная. Её использовать так: &lt;br /&gt;&lt;pre class="sh_sh"&gt;mountavfs ; ls ~/.avfs/полный/путь/к/архиву.zip#/файл/в/архиве&lt;/pre&gt;В Debian нужно предварительно добавить пользователя в группу fuse.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.pythonxy.com/foreword.php"&gt;Python(x,y)&lt;/a&gt; — дистрибутив Python для научных работников, для Windows и Ubuntu. Все инструменты и библиотечки «из коробки».&lt;/li&gt;&lt;li&gt;В дополнение к своему &lt;a href="http://sovety.blogspot.com/2008/06/antiodt-openoffice.html"&gt;однострочнику antiodt&lt;/a&gt; нашёл ещё хороший конвертер ODT  в Markdown &lt;a href="http://www.freewisdom.org/projects/python-markdown/odt2txt_py.raw_content"&gt;odt2txt.py&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Дружественный к Гному вариант Xmonad — &lt;a href="http://projects.haskell.org/bluetile/"&gt;Bluetile&lt;/a&gt;. Раз попробовал, и две недели им пользовался.&lt;/li&gt;&lt;li&gt;Попробовал &lt;code&gt;gitit&lt;/code&gt;. Самая простая вики для совместной работы над математическими текстами (вместе с &lt;code&gt;jsMath&lt;/code&gt; из коробки). Хранилище — git или darcs.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.keithsheppard.name/txt-sushi/"&gt;TxtSushi&lt;/a&gt; — утилитки, позволяющие выполнять SQL-запросы по простому текстовому (CSV, TSV) файлу.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Ух-ты, а немало получилось.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37628976-407072897408407682?l=sovety.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/sovety/~4/L440Bk2akWE" height="1" width="1"/&gt;</description><link>http://sovety.blogspot.com/2009/10/micro-digest.html</link><author>noreply@blogger.com (Сергей)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">6</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37628976.post-3810767488678051494</guid><pubDate>Tue, 22 Sep 2009 00:03:00 +0000</pubDate><atom:updated>2009-09-29T00:06:30.307+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">видеоурок</category><category domain="http://www.blogger.com/atom/ns#">inkscape</category><category domain="http://www.blogger.com/atom/ns#">начинающим</category><category domain="http://www.blogger.com/atom/ns#">графика</category><title>Как нарисовать стрелку в Inkscape</title><description>Очень люблю Inkscape, и часто в нём рисую разные схемы. А для того, чтобы рисовать схемы, нужны стрелки. Готового инструмента «стрелка» в Инкскейпе нет&lt;sup&gt;*&lt;/sup&gt;, поэтому творю из подручных материалов сообразно вкусу и потребностям. В общем-то, минутное дело, умеючи...&lt;br /&gt;
&lt;br /&gt;
Я подумал, что кому-то, возможно, будет интересно увидеть, как можно самому нарисовать практически любую стрелку. Предлагаю небольшой видеоурок, где показываю как нарисовать стрелку попроще и стрелку позатейливей:&lt;br /&gt;
&lt;br /&gt;
&lt;video width="512" controls="" src="http://sovety.org.ru/video/inkscape-arrows-tutorial.ogg" poster="http://sovety.org.ru/video/inkscape-arrows-tutorial.jpg" tabindex="0"&gt;&lt;br /&gt;
&lt;div class="error"&gt;Ой! Ваш браузер не может показать это видео (не поддерживает тэг &amp;lt;video&amp;gt;). Установите &lt;a href="http://getfirefox.com/"&gt;Firefox 3.5&lt;/a&gt; или &lt;a href="http://www.google.com/chrome"&gt;Google Chrome 3&lt;/a&gt;. Щёлкните по картинке, чтобы скачать видеофайл.&lt;/div&gt;&lt;br /&gt;
&lt;a href="http://sovety.org.ru/video/inkscape-arrows-tutorial.ogg"&gt;&lt;img src="http://sovety.org.ru/video/inkscape-arrows-tutorial.jpg" alt="Скачать видеофайл"&gt;&lt;br /&gt;
Скачать видеофайл&lt;/a&gt;&lt;/video&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;noindex&gt;Это мой первый скринкаст. Прошу строго не судить. Кино немое, просто с титрами.&lt;/noindex&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;sup&gt;*&lt;/sup&gt; В инкскейпе есть маркеры конца и начала линии. Красить их нельзя, они всегда чёрные. Впрочем, подсказывают из зала, можно — создав стрелку, надо «оконтурить обводку», получить два контура (древко и острие), вручную их подправить-подравнять, и получится полноценная контурная стрелка. Я пока остаюсь приверженцем своего способа создания контурных стрелок. Впрочем, смотрите на &lt;a href="http://www.youtube.com/v/w_NYuBml9ek&amp;fs=1"&gt;другой способ&lt;/a&gt; сами (подал идею и сделал видео &lt;a href="http://www.youtube.com/user/freedomfidaj"&gt;freedomfidaj&lt;/a&gt;).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37628976-3810767488678051494?l=sovety.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/sovety/~4/E8kisB6Poiw" height="1" width="1"/&gt;</description><link>http://sovety.blogspot.com/2009/09/inkscape-tutorial-how-to-draw-arrows.html</link><author>noreply@blogger.com (Сергей)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">19</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37628976.post-1881501667381363807</guid><pubDate>Thu, 17 Sep 2009 21:04:00 +0000</pubDate><atom:updated>2009-10-04T11:17:42.569+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">программирование</category><category domain="http://www.blogger.com/atom/ns#">haskell</category><title>(Новичковые) ужасы Хаскеля</title><description>Я — начинающий программист на Хаскеле, и пока я ещё помню всё, чем он страшен. И это хочу записать. Сразу скажу, когда я приступал к Хаскелю, я ещё не знал практически ничего о функциональном программировании, поэтому одновременно с языком, нужно было осваивать новые идеи и образ мысли. И вообще-то это было здорово. А у страха, как известно, глаза велики. В общем, я думаю, эта заметка может быть полезна и другим начинающим. В ней я укажу на пять непонятных мне вначале, но относительно несложных вещей, поняв которые хотя бы приблизительно, освоить язык мне было гораздо легче.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;1. Ламбда-функции&lt;/h3&gt;&lt;br /&gt;
&lt;div style="padding-left: 50%; font-style: italic;"&gt;— Но мы называем его лембас или путевой хлеб, он подкрепляет лучше, чем любая пища людей, и он гораздо вкуснее.&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
Вообще-то, именно поэтому я и выучил Хаскель. Мне было просто любопытно, что означают все эти лямбды. Очень помогла в самом начале статья Пола Худака &lt;a href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.83.6505"&gt;Conception, evolution, and application of functional programming languages&lt;/a&gt; (&lt;a href="freenet:CHK@7V55cv5604SmgsFEfqm8~iJmMHmzohlLf7J6H9-E8hA,4hsE5sD-guPDm3aHMOd2xtmOY7SKKJWSr3rayM8Sbmk,AAIC--8/hudak89functional.pdf"&gt;PDF также здесь&lt;/a&gt;). Возможно, есть введения и получше, но я начинал с него.&lt;br /&gt;
&lt;br /&gt;
Ламбда-выражение — самая суть «функций» — это выражение вида&lt;br /&gt;
&lt;blockquote&gt;&lt;img style="border: 0pt none ; padding: 1ex 2ex;" alt="\lambda x \;.\; \text{expression with $x$}" src="http://mathurl.com/krqmc7.png"/&gt;&lt;/blockquote&gt;Значением этого выражения является пока ещё безымянная &lt;em&gt;функция&lt;/em&gt; одного аргумента (&lt;em&gt;x&lt;/em&gt;), что-то с ним вычисляющая (а именно, выражение справа от точки). С лямбда-функциями связана серьёзная математическая теория, но с точки зрения программирования можно считать &lt;img alt="\lambda" src="http://mathurl.com/c8c2x3.png"/&gt; &lt;em&gt;ключевым словом&lt;/em&gt; для определения функций. Действительно, когда я и до этого уже пользовался лямбдами в Питоне (и почти во всех других современных языках они тоже есть). В Питоне они выглядят вот так:&lt;br /&gt;
&lt;pre class="sh_python"&gt;lambda x: expression with x&lt;/pre&gt;Ими было очень удобно пользоваться в &lt;code&gt;filter()&lt;/code&gt; и &lt;code&gt;reduce()&lt;/code&gt;. И вообще, почти везде, где в качестве аргумента требуется имя функции. Однако у лямбда-функций нет имён, и именно поэтому их ещё называют &lt;a href="http://ru.wikipedia.org/wiki/%D0%90%D0%BD%D0%BE%D0%BD%D0%B8%D0%BC%D0%BD%D0%B0%D1%8F_%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D1%8F"&gt;анонимными (безымянными) функциями&lt;/a&gt;. В пайтоне иногда я давал им имена прямо на лету:&lt;br /&gt;
&lt;pre class="sh_python"&gt;add_42 = lambda x: x + 42&lt;/pre&gt;Теперь имя &lt;code&gt;add_42&lt;/code&gt; указывает на функцию. Точно такой же результат можно было получить, записав определение функции как обычно:&lt;br /&gt;
&lt;pre class="sh_python"&gt;def add_42(x):
return x+42&lt;/pre&gt;А что же насчёт Хаскеля? Да почти то же самое. Символ &lt;code&gt;\&lt;/code&gt; заменяет  &lt;img alt="\lambda" src="http://mathurl.com/c8c2x3.png"/&gt;, &lt;code&gt;-&amp;gt;&lt;/code&gt;  служит вместо точки. Всё вместе записывается так:&lt;br /&gt;
&lt;pre class="sh_haskell"&gt;\x -&amp;gt; выражение с x&lt;/pre&gt;И мы даже можем давать имена таким безымянным функциям, так же как и в Питоне:&lt;br /&gt;
&lt;pre class="sh_haskell"&gt;add_42 = \x -&amp;gt; x + 42&lt;/pre&gt;Согласитесь, очень похоже.&lt;br /&gt;
&lt;br /&gt;
Однако тут есть одна тонкость. Как только я начал читать о Хаскеле, я увидел лямбда-выражения, которые поначалу казались немного странными:&lt;br /&gt;
&lt;pre class="sh_haskell"&gt;\x -&amp;gt; \y -&amp;gt; выражение с x и y&lt;/pre&gt;Что означают все эти «стрелочки»? Ответ оказался очень прост и очень полезен в дальнейшем освоении языка.&lt;br /&gt;
&lt;br /&gt;
В Хаскеле все функции являются функциями одного аргумента. Поначалу это может показаться ограничением, но на деле это очень удобная и практичная идея. Любую функцию &lt;em&gt;n&lt;/em&gt; аргументов можно представить как функцию одного аргумента, возвращающую другую функцию &lt;em&gt;n–1&lt;/em&gt; аргементов. И по науке это азывается &lt;a href="http://ru.wikipedia.org/wiki/%D0%9A%D0%B0%D1%80%D1%80%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5"&gt;каррированием&lt;/a&gt;. Эта идея, в частности, позволяет передавать функции только часть аргументов.&lt;br /&gt;
&lt;br /&gt;
Узнав об этом, мы теперь можем читать любые выражение с множеством «стрелок»:&lt;br /&gt;
&lt;pre class="sh_haskell"&gt;\x -&amp;gt; (\y -&amp;gt; выражение с x и y)&lt;/pre&gt;Значением такого выражения будет фукнция, берущая один аргумент и производящая другую функцию, которая берёт ещё один аргумент. Такое выражение в целом ведёт себя как функция двух аргументов. Например, мы можем вычислить такую функцию двух аргументов в интерпретаторе Хаскеля &lt;code&gt;ghci&lt;/code&gt;:&lt;br /&gt;
&lt;pre&gt;ghci&amp;gt; (\x -&amp;gt; \y -&amp;gt; x + y ) 3 4
7&lt;/pre&gt;Конечно, есть более краткий способ записи функций двух аргументов (обратите внимание, что список аргументов брать в скобки совсем не нужно):&lt;br /&gt;
&lt;pre&gt;ghci&amp;gt; (\x y -&amp;gt; x + y) 3 4
7&lt;/pre&gt;Однако знать, что на самом деле все функции нескольких аргументов являются функциями одного аргумента очень полезно. Например, это помогает читать описания типов функций. Например, тип функции &lt;code&gt;map&lt;/code&gt; выглядит так:&lt;br /&gt;
&lt;pre class="sh_haskell"&gt;map :: (a -&amp;gt; b) -&amp;gt; [a] -&amp;gt; [b]&lt;/pre&gt;Я обычно читаю это следующим образом: «функция &lt;code&gt;map&lt;/code&gt; принимает два аргумента, первый — функцию преобразующую &lt;code&gt;a&lt;/code&gt; в &lt;code&gt;b&lt;/code&gt;, второй — список элементов типа &lt;code&gt;a&lt;/code&gt;, а возвращает список элементов типа &lt;code&gt;b&lt;/code&gt;». Но иногда гораздо естественнее записать тот же самый тип так:&lt;br /&gt;
&lt;pre class="sh_haskell"&gt;map :: (a -&amp;gt; b) -&amp;gt; ([a] -&amp;gt; [b])&lt;/pre&gt;«Функция, которая берёт функцию, преобразующую &lt;code&gt;a&lt;/code&gt; в &lt;code&gt;b&lt;/code&gt;, и возвращает функцию, преобразующую список &lt;code&gt;a&lt;/code&gt; в список &lt;code&gt;b&lt;/code&gt;».&lt;br /&gt;
&lt;br /&gt;
Даже эти простые понятия о лямбда-функиях были уже достаточны, чтобы начать пользоваться Хаскелем и понять большинство примеров и объяснений.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;2. Знак равенства&lt;/h3&gt;&lt;br /&gt;
&lt;div style="padding-left: 50%; font-style: italic;"&gt;— Ну что, если тут нет смысла, — сказал Король, — тогда у нас гора с плеч: нам незачем пытаться его найти! Сэкономим кучу работы! И все же...&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
По-моему, знак равенства (&lt;code&gt;=&lt;/code&gt;) — самый важный символ в Хаскеле. Понять его важно для понимания языка. И мне кажется, смысл равенства недостаточно подчёркивается во всевозможных учебниках. Например, это единственное ключевое слово, отсутствующее в &lt;a href="http://www.haskell.org/haskellwiki/Keywords"&gt;списке ключевых слов Хаскеля&lt;/a&gt; в его вики.&lt;br /&gt;
&lt;br /&gt;
В отличии от большинства императивных языков, где &lt;code&gt;=&lt;/code&gt; означает &lt;a href="http://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B8%D1%81%D0%B2%D0%B0%D0%B8%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5"&gt;присваивание&lt;/a&gt; (то есть &lt;em&gt;действие&lt;/em&gt;), в Хаскеле он означает, что левая часть &lt;em&gt;равна&lt;/em&gt; правой (то есть описывает &lt;em&gt;свойство&lt;/em&gt;).&lt;br /&gt;
&lt;br /&gt;
«Равна» — не значит «становится». Это означает, что нечто равно чему-то ещё. Всегда. Как в математике. &lt;code&gt;a = b&lt;/code&gt; в Хаскеле означает, что &lt;code&gt;a&lt;/code&gt; равно &lt;code&gt;b&lt;/code&gt; &lt;a href="http://mathworld.wolfram.com/Defined.html"&gt;по определению&lt;/a&gt;, &lt;code&gt;a&lt;/code&gt; эквивалентно &lt;code&gt;b&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом, &lt;code&gt;=&lt;/code&gt; в Хаскеле служит для записи определений. «Равно» может определять самые разные вещи, но определяет их статично. Оно не зависит от порядка выполнения операций. На него можно положиться.&lt;br /&gt;
&lt;br /&gt;
Пользователям функциональных языком это покажется слишком уж очевидным, но именно в смысле знака равенства самое важное изменение для тех, кто раньше пользовался императивными языками. Теперь, кстати, мы можем давать имена нашим безымянным функциям:&lt;br /&gt;
&lt;pre class="sh_haskell"&gt;add = \x -&amp;gt; \y -&amp;gt; x + y&lt;/pre&gt;Признаю, что читается это плохо, поэтому в большинстве случаев функции в Хаскеле определяются так:&lt;br /&gt;
&lt;pre class="sh_haskell"&gt;add x y = x + y&lt;/pre&gt;Но и это по-прежнему &lt;em&gt;определение&lt;/em&gt; функции &lt;code&gt;add&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;3. Классы типов&lt;/h3&gt;&lt;br /&gt;
&lt;div style="padding-left: 50%; font-style: italic;"&gt;Significant benefits arise from sharing a common type system, a common toolset, and so forth. These technical advantages translate into important practical benefits such as enabling groups with moderately differing needs to share a language rather than having to apply a number of specialized languages. — приписывается Б. Страуструпу&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
Система типов в Хаскеле просто прекрасна. В ней очень легко и естественно выражаются многие идеи. И возможно, именно классы типов — это наименее чуждая  концепция для тех, кто приходит в Хаскель из процедурного и объектно-ориентированного мира. Во всяком случае, мне так показалось. Однако классы типов — это совсем не то же самое, что классы в Си++ или в Джаве. Гораздо больше они похожи на абстрактные шаблоны классов в Си++, потому что классы типов&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;определяют только абстрактный интерфейс (не имеют реализации по-умолчанию)&lt;/li&gt;
&lt;li&gt;позволяют создавать несколько независимых реализаций интерфейса (таким образом, любой класс может стать экземпляром класса типов, если предоставит реализацию его методов)&lt;/li&gt;
&lt;li&gt;полиморфны по своей природе и поддерживают наследование&lt;/li&gt;
&lt;li&gt;не могут иметь переменных состояния&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
Как только мы привыкнем, что типы классов — это не классы Си++, а абстрактные интерфейсы, и экземпляры классов это не «объекты», а конкретные реализации абстрактных интерфейсов, Хаскель сразу станет привычным и уютным.&lt;br /&gt;
&lt;br /&gt;
Я очень советую почитать вики-статью &lt;a href="http://www.haskell.org/haskellwiki/OOP_vs_type_classes"&gt;OOP vs type classes&lt;/a&gt;, которая гораздо более детально сравнивает объектно-ориентированный подход и классово-типовой.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;4. Монады&lt;/h3&gt;&lt;br /&gt;
&lt;div style="padding-left: 50%; font-style: italic;"&gt;И так как всякое настоящее состояние простой субстанции, естественно, есть следствие ее предыдущего состояния, то настоящее ее чревато будущим, — Лейбниц, «Монадология»&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
Не важно, насколько &lt;a href="http://rsdn.ru/article/haskell/haskell_part1.xml"&gt;мягкое введение в Хаскель&lt;/a&gt;, рано или поздно его читатель упрётся лбом в крепкую стеную из &lt;a href="http://ru.wikipedia.org/wiki/%D0%9C%D0%BE%D0%BD%D0%B0%D0%B4%D0%B0_%28%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%29"&gt;монад&lt;/a&gt;. Да, это вам не плюшки тырить, это вам серьёзная математика. Где-то за этой стеной.&lt;br /&gt;
&lt;br /&gt;
Но вот что я понял: изучать абстрактную математику совсем не обязательно, чтобы монады использовать, а они и правда очень изящная программистская техника. Вначале они казались мне немного странными, но понять раз и навсегда монады гораздо легче, чем запоминать (и правильно применять!) бесчисленные &lt;a href="http://ru.wikipedia.org/wiki/%D0%A8%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD%D1%8B_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F"&gt;шаблоны ОО-проектирования&lt;/a&gt;. Монады логичней.&lt;br /&gt;
&lt;br /&gt;
Поскольку тьюториалов по монадом огромное множество, я не буду их здесь повторять и ожидаю, что вы их уже прочитал &lt;a href="http://www.haskell.org/haskellwiki/Tutorials#Using_monads"&gt;парочку&lt;/a&gt;. Что же не так с монадами? Для человека, привыкшему к императивным языка, испорченному годами объектно-ориентированного мышления монады кажутся странными. Они выглядят как абстрактный класс-контейнер с загадочным методом &lt;code&gt;&amp;gt;&amp;gt;=&lt;/code&gt;:&lt;br /&gt;
&lt;pre class="sh_haskell"&gt;class Monad m where
return :: a -&amp;gt; m a
(&amp;gt;&amp;gt;=) :: m a -&amp;gt; (a -&amp;gt; m b) -&amp;gt; m b
fail :: String -&amp;gt; m a&lt;/pre&gt;Хорошо, если &lt;code&gt;return&lt;/code&gt; — конструктор, то почему такое чудное имя? Если это класс-контейнер, то как из него что-либо извлечь? И какой смысл применять функцию внутри контейнера (а именно это делает метод &lt;code&gt;&amp;gt;&amp;gt;=&lt;/code&gt;, называемый также операцией &lt;em&gt;связывания&lt;/em&gt;), если мы не можем вытащить результат из этого контейнера?&lt;br /&gt;
&lt;br /&gt;
Отвечу вначале на последний вопрос. &lt;b&gt;Зачем нужно связывание (&lt;code&gt;&amp;gt;&amp;gt;=&lt;/code&gt;)?&lt;/b&gt; Монады являются и одновременно не являются контейнерами. Они — обёртки, упаковки для &lt;em&gt;вычислений&lt;/em&gt;, а не для значений (&lt;code&gt;return&lt;/code&gt;). Однако они обёртывают вычисления не для того, чтобы их было удобнее хранить в монадных коробочках, а чтобы их можно было удобнее соединять друг с другом. Вместо «коробочек» представьте обыкновенные кирпичи, которые ровно кладутся друг к другу. Это, кстати, похоже на шаблон Adapter в ОО-проектировании. Каждая монада определяет какой-то способ передавать результат от одного вычисления к другому и реализует стандартный интерфейс, чтобы этот способ использовать (&lt;code&gt;&amp;gt;&amp;gt;=&lt;/code&gt;). И что бы ни случилось, результат всегда останется в той же монаде (даже, если произойдёт сбой, &lt;code&gt;fail&lt;/code&gt;).&lt;br /&gt;
&lt;br /&gt;
Самая простая программистская аналогия монадам, которую я придумал, это конвееры (pipes) в командной оболочке Unix. Монады обеспечивают однонаправленный конвеер для вычислений. То же самое делают и конвееры в Unix. Например:&lt;br /&gt;
&lt;pre&gt;$ seq 1 5 | awk '{print $1 " " $1*$1*$1 ;}'
1 1
2 8
3 27
4 64
5 125&lt;/pre&gt;&lt;code&gt;seq&lt;/code&gt; создаёт список целых чисел. &lt;code&gt;awk&lt;/code&gt; вычислят куб каждого из них. Что здесь замечательного? У нас есть две слабо связанные друг с другом программы, которым мы можем легко указать работать вместе. Поток текста создаваемый программой слева попадает по конвееру в программу справа, которая может читать этот поток, что-то с ним делать и создавать уже новый текстовый поток. Текстовый поток — общий формат для результата вычислений, &lt;code&gt;|&lt;/code&gt; — операция, связывающая их воедино.&lt;br /&gt;
&lt;br /&gt;
Монады очень похожи. &lt;code&gt;&amp;gt;&amp;gt;=&lt;/code&gt; берёт внутреннее вычисление из монады слева и подставляет его в вычисление справа, которое всегда должно создавать ещё одну монаду того же типа.&lt;br /&gt;
&lt;br /&gt;
Как вы уже, наверное, знаете, списки и тип &lt;code&gt;Maybe&lt;/code&gt; в Хаскеле — монады. Например, пусть у нас есть простое вычисление, которое возвращает пару из числа и его куба обратно в монаду (&lt;code&gt;return&lt;/code&gt;):&lt;br /&gt;
&lt;pre class="sh_haskell"&gt;\x -&amp;gt; return (x, x^3)&lt;/pre&gt;тогда мы можем взять список и направить его «по конвееру» в это вычисление:&lt;br /&gt;
&lt;pre class="sh_haskell"&gt;ghci&amp;gt; [1,2,3,4,5] &amp;gt;&amp;gt;= \x -&amp;gt; return (x,x^3)
[(1,1),(2,8),(3,27),(4,64),(5,125)]&lt;/pre&gt;Заметьте, что мы получили список пар. Это та же самая исходная монада (то есть список). Однако если мы возмьём значение &lt;code&gt;Maybe&lt;/code&gt; и направим его в то же вычисление, на выходе у нас будет та же сама монада &lt;code&gt;Maybe&lt;/code&gt;:&lt;br /&gt;
&lt;pre class="sh_haskell"&gt;ghci&amp;gt; Just 5 &amp;gt;&amp;gt;= \x -&amp;gt; return (x,x^3)
Just (5,125)&lt;/pre&gt;Таким образом, мы можем создать конвеер из двух вычислений, и поведение этого конвеера зависит от контекста (т.е. от того, какая монада используется), а не от самих вычислений. В отличии от юниксовых конвееров, монады строго типизованы, и сама система типов заботится о том, чтобы выход одной монады был совместим с входом другой. И в отличии от юниксовых конвееров, мы можем задавать наши собственные правила связывания (&lt;code&gt;&amp;gt;&amp;gt;=&lt;/code&gt;). Например, такие: «не делать более 42 вычислений подряд» или «посмотреть на входное значение, сделать то или это». Классы монад содержат в себе подобные правила, как соединять вычисления.&lt;blockquote&gt;&lt;b&gt;Дополнение:&lt;/b&gt; в комментариях подсказывают, что гораздо более подробно и более строго аналогия между юниксовым конвеером и монадами разобрана в статье &lt;a href="http://okmij.org/ftp/Computation/monadic-shell.html"&gt;Monadic i/o and UNIX shell programming&lt;/a&gt;.&lt;/blockquote&gt;Теперь, я надеюсь, вы понимаете монады не хуже меня (не обязательно полностью). Хочу обсудить несколько мнемонических правил. &lt;b&gt;Почему &lt;code&gt;return&lt;/code&gt; называется &lt;code&gt;return&lt;/code&gt;?&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
В большинстве языков &lt;code&gt;return&lt;/code&gt; возвращает результат вычисления из функции. В Хаскеле же он конструктор для монад. Это очень странно. Однако посмотрим как работает &lt;code&gt;&amp;gt;&amp;gt;=&lt;/code&gt;: эта операция &lt;em&gt;извлекает&lt;/em&gt; значение из монады слева, а затем связывает его с аргументом функции справа (отсюда, кстати, и другое название метода — bind). А функция справа должна &lt;em&gt;вернуть&lt;/em&gt; значение обратно в монаду, чтобы можно было передать эстафетную палочку дальше следующей операции &lt;code&gt;&amp;gt;&amp;gt;=&lt;/code&gt;. Это первое мнемоническое правило: &lt;code&gt;return&lt;/code&gt; — возвращает вычисленное значения обратно в монаду.&lt;br /&gt;
&lt;br /&gt;
Вторая мнемоника. Функция верхнего уровня любой программы на Хаскеле выполняется в монаде &lt;code&gt;IO&lt;/code&gt; (тип функции &lt;code&gt;main&lt;/code&gt; — &lt;code&gt;IO ()&lt;/code&gt;). Эта монада позволяет выполнять ввод-вывод и вообще любые последовательные действия. Таким образом, монадный код выполняется на самом верхнем уровне программы, и именно он вызывает любой «чистый» код по мере необходимости, а не наоборот. Таким образом, любое «чистое» значение, если не отбрасывается, то рано или поздно &lt;em&gt;возвращается&lt;/em&gt; в монаду её вызвавшую.&lt;br /&gt;
&lt;br /&gt;
Надеюсь, что после этих объяснений имя &lt;code&gt;return&lt;/code&gt; для монадного конструктора больше не кажется таким уж странным. Я, однако, не утверждаю, что мои объяснения 100% технически верны.&lt;br /&gt;
&lt;br /&gt;
Следующий вопрос бывшего ОО-программиста, &lt;b&gt;как вытащить значение вычисления из монады?&lt;/b&gt;. Хорошо, монады преднамеренно спроектированы именно так, что это не всегда возможно. Например, нельзя извлечь чистое значение из монады &lt;code&gt;IO&lt;/code&gt;. Если дана такая «односторонняя» монада, то всё, что можно с ней делать — передавать внутреннее значение по монадному конвееру дальше. К счастью, в Хаскеле разработан специальный синтаксис с ключевым словом &lt;code&gt;do&lt;/code&gt;, которые делает такую многократную передачу монады по конвееру очень похожей на последовательную императивную программу. Следующие две программы делают одно и то же. Первая записана с &lt;code&gt;do&lt;/code&gt;-нотацией:&lt;br /&gt;
&lt;pre class="sh_haskell"&gt;main :: IO ()
main = do
name &amp;lt;- getLine
putStrLn ("Hi, " ++ name)&lt;/pre&gt;а вторая явно использует &lt;code&gt;&amp;gt;&amp;gt;=&lt;/code&gt;:&lt;br /&gt;
&lt;pre class="sh_haskell"&gt;main :: IO ()
main = getLine &amp;gt;&amp;gt;= \name -&amp;gt; putStrLn ("Hi, " ++ name)&lt;/pre&gt;Эквивалентная программа на Питоне:&lt;br /&gt;
&lt;pre class="sh_python"&gt;from sys import stdin, stdout
if __name__ == "__main__":
name = stdin.readline()
stdout.write("Hi, " + name)&lt;/pre&gt;Однако иногда вытащить чистое значение из монадного вычисления можно. Это не предусмотрено общим монадным интерфейсом, поэтому разработчик монады должен специально предусмотреть возможность извлекать значения наружу. Например, можно извлекать значения из монады &lt;code&gt;Maybe&lt;/code&gt;, используя функцию &lt;code&gt;fromMaybe&lt;/code&gt;:&lt;br /&gt;
&lt;pre class="sh_haskell"&gt;ghci&amp;gt; fromMaybe 0 $ Just 3
3
ghci&amp;gt; fromMaybe 0 $ Nothing
0&lt;/pre&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Заключение по монадам&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Итак, связывание (&lt;code&gt;&amp;gt;&amp;gt;=&lt;/code&gt;) позволяет объединять различные монадные вычисления вместе. Почти везде, где есть цепь вычислений, монады очень подходят. Конкретные реализации монад могут содержать разные правила комбинирования вычислений. Имя метода &lt;code&gt;return&lt;/code&gt; сбивает с толку начинающих, это метод возвращает результа вычисления обратно в монаду, а не из монады. В общем, когда я понял эти простые идеи, это сильно помогло.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;5. Страшные слова&lt;/h3&gt;&lt;br /&gt;
&lt;div style="padding-left: 50%; font-style: italic;"&gt;Я знаю только то, что ничего не знаю.&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
Даже спустя месяцы после того, как я начал учить Хаскель, умея написать какие-то полезные программы на нём, я вижу вокруг, в мире Хаскеля, ещё много понятий, о которых не знаю ничего или имею только очень смутное представление. Я называю такие понятия «страшными словами». И я вижу, что есть люди, которые создают и используют библиотеки, воплощающие эти понятия в жизнь.&lt;br /&gt;
&lt;br /&gt;
Надо принзнать, Хаскель остаётся испытательной площадкой для исследователей. И это одновременно и хорошо, и плохо. Это хорошо, потому что даёт чувство, что передний край науки и технологии очень близок, и можно при желании пользоваться преимуществами новых подходов. При желании. И одновременно это плохо, потому что иногда, когда хочется использовать новую изящную библиотеку, оказывается, что она активно использует незнакомые и не совсем понятные понятия и идеи, и нужно быть готовым такие идеи осваивать.&lt;br /&gt;
&lt;br /&gt;
Например, есть современная XML-библиотека HXT. Она очень много использует &lt;em&gt;стрелки&lt;/em&gt;. Стрелки — более универсальные комбинаторы вычислений, чем монады, но мне потребовалось гораздо больше, чем один день, чтобы их более-менее понять. Строго говоря, стрелки не являются частью языка, но они — понятие, которое применяют пользователи этого языка. И таких примеров немало. Хотя тем, кому стрелки осваивать не хочется, можно пользоваться более традиционной и активно поддерживаемой XML-библиотекой HaXml.&lt;br /&gt;
&lt;br /&gt;
Я думаю, важно не бояться «страшных слов». К счастью, основополагающие идеи хорошо описаны. Как правило, есть статьи их очень детально объясняющие. Я сам решил осваивать такие идеи по мере необходимости. Это обещает быть и увлекательным, и одновременно посильным.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Заключение&lt;/h3&gt;&lt;br /&gt;
&lt;br /&gt;
Я перечислил пять простых идей, осознав которые, мне стало легче привыкнуть к Хаскелю. Лямбды — это просто способ записи функций, и функции нескольких аргументов можно всегда записать как функцию одного, возвращающую другую функцию. Типы классов очень похожи на абстрактные полиморфные интерфейсы в объектно-ориентированном подходе. Монады — стандартизованный способ соединять вычисления вместе. А страшные слова — просто страшные слова. Без них можно жить, но скучно.&lt;br /&gt;
&lt;br /&gt;
Надеюсь, мои заметки будут полезны и ещё кому-нибудь.&lt;br /&gt;
&lt;br /&gt;
&lt;em&gt;Эта статья есть также &lt;a href="http://nix-tips.blogspot.com/2009/06/haskell-horrors.html" title="Haskell horrors"&gt;по-английски&lt;/a&gt;.  &lt;a href="http://nix-tips.blogspot.com/2009/06/haskell-horrors.html" title="Haskell horrors"&gt;This post is also available in English.&lt;/a&gt;&lt;/em&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37628976-1881501667381363807?l=sovety.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/sovety/~4/eQrfXfEWm6w" height="1" width="1"/&gt;</description><link>http://sovety.blogspot.com/2009/09/haskell-horrors.html</link><author>noreply@blogger.com (Сергей)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">8</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37628976.post-1126319472123305951</guid><pubDate>Tue, 08 Sep 2009 07:07:00 +0000</pubDate><atom:updated>2009-09-22T13:03:49.687+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">программирование</category><category domain="http://www.blogger.com/atom/ns#">python</category><title>Ещё одна библиотека комбинаторного парсинга</title><description>Не так давно я &lt;a href="http://sovety.blogspot.com/2009/07/easy-parsing-in-python.html" title="Необыкновенно лёгкий парсинг в Python"&gt;писал&lt;/a&gt; о библиотеке &lt;code&gt;pyparsing&lt;/code&gt; для комбинаторного парсинга в Python. В комментариях появилась ссылка на ещё одну библиотеку, о которой я вначале не знал, а именно на &lt;a href="http://code.google.com/p/funcparserlib/"&gt;&lt;code&gt;funcparserlib&lt;/code&gt;&lt;/a&gt;, написанную Андреем Власовских.&lt;br /&gt;
&lt;br /&gt;
В общем-то, я посмотрел на новую библитечку, и она мне тоже понравилась. Подкупает сравнительная простота самой библиотеки, ясные исходники и подробно написанные руководства — понять как работает библиотека нетрудно. Правда, при чтении документации нужно быть знакомым с нотацией типов, принятой в Haskell (&lt;code&gt;ArgType -&amp;gt; ResultType&lt;/code&gt;). Общее впечатление: имеющиеся в &lt;code&gt;funcparserlib&lt;/code&gt;  комбинаторы практически полностью ортогональны друг другу, записываются кратко, места на экране занимают мало, называются понятно.&lt;br /&gt;
&lt;br /&gt;
Комбинаторов в библиотеке всего-то: &lt;code&gt;some&lt;/code&gt;, &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;many&lt;/code&gt;, &lt;code&gt;finished&lt;/code&gt;, &lt;code&gt;maybe&lt;/code&gt;, &lt;code&gt;skip&lt;/code&gt;, &lt;code&gt;oneplus&lt;/code&gt;, &lt;code&gt;forward_decl&lt;/code&gt;, &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;|&lt;/code&gt; и &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;. Действие большинства комбинаторов угадывается из названия. Рабочие лошадки: &lt;code&gt;some(функция-предикат)&lt;/code&gt; берёт токен, удовлетворяющий условию, &lt;code&gt;a(токен)&lt;/code&gt; берёт токен, указанный в аргументе, &lt;code&gt;many(парсер)&lt;/code&gt;, &lt;code&gt;maybe(парсер)&lt;/code&gt;, &lt;code&gt;oneplus(парсер)&lt;/code&gt; — множественное, возможное или хотя бы однократное срабатывание парсера, &lt;code&gt;skip(парсер)&lt;/code&gt; отбрасывает всё найденное парсером. Плюс (&lt;code&gt;+&lt;/code&gt;) последовательно применяет два парсера, «или» (&lt;code&gt;|&lt;/code&gt;) пробует альтернативные варианты, &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; подставляет результат парсера в функцию и создаёт новый парсер (очень полезный комбинатор!). Ещё есть &lt;code&gt;tuple&lt;/code&gt; для группировки.&lt;br /&gt;
&lt;br /&gt;
Для пробы я решил написать пример разбора того же файлового формата, что и в &lt;a href="http://sovety.blogspot.com/2009/07/easy-parsing-in-python.html" title="Необыкновенно лёгкий парсинг в Python"&gt;заметке о pyparsing&lt;/a&gt;. Мне кажется, что наиболее эффективно применять &lt;code&gt;funcparserlib&lt;/code&gt; с предварительным лексическим анализом (разбиением текста на токены-лексемы). В принципе, готовый токенизатор — часть стандартной библиотеки Python, поэтому это не проблема. Удобная обёртка приведена в руководстве к &lt;code&gt;funcparserlib&lt;/code&gt;. Однако мне хотелось написать пример в том же стиле, что и для &lt;code&gt;pyparsing&lt;/code&gt;, поэтому в примере ниже я разбираю текст на уровне отдельных символов.&lt;br /&gt;
&lt;br /&gt;
Полный текст примера с тестами кладу &lt;a href="http://pastebin.com/f64902ff2" title="Learning to use funcparserlib"&gt;в pastebin&lt;/a&gt;. Далее пояснения.&lt;br /&gt;
&lt;br /&gt;
Половина кода пришлась на разбор чисел. Вообще, по-моему, в такого рода библиотеках было бы хорошо класть готовые парсеры для чисел, дат, e-mail и тому подобных обыденных вещей где-нибудь в разделе contrib... Впрочем, написать разбор чисел было даже занятно.&lt;br /&gt;
&lt;br /&gt;
Чтение знака числа. Если ни плюса, ни минуса нет, подразумеваю плюс. Волшебный комбинатор &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; позволяет обработать результат и сразу подготовить ответ, или &lt;code&gt;-1&lt;/code&gt;, или &lt;code&gt;+1&lt;/code&gt;:&lt;pre class="sh_python"&gt;sign = maybe(a("-")|a("+")) &amp;gt;&amp;gt; (lambda c: c == "-" and -1 or +1)&lt;/pre&gt;&lt;br /&gt;
Целые числа состоят из знака и последовательности цифр. Так и запишем: &lt;pre class="sh_python"&gt;digits = many(some(lambda c: c.isdigit()))
int_num = sign + (digits &amp;gt;&amp;gt; to_int) &amp;gt;&amp;gt; mk_int&lt;/pre&gt;&lt;br /&gt;
Здесь я пользуюсь опять комбинатором &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; и двумя вспомогательными функциями. Одна превращает последовательность символов-цифр в число (&lt;code&gt;to_int&lt;/code&gt;), другая умножает результат на &lt;code&gt;-1&lt;/code&gt;, если необходимо (&lt;code&gt;mk_int&lt;/code&gt;). Эти функции вспомогательные, можно пропустить: &lt;pre class="sh_python"&gt;powers = lambda digs: zip(digs,xrange(len(digs)-1,-1,-1))
add_digit = lambda acc, dp: acc+int(dp[0])*10**dp[1]
to_int = lambda digs: reduce(add_digit, powers(digs), 0)
mk_int = lambda (s,i): s*i&lt;/pre&gt;&lt;br /&gt;
Аналогично строю и парсер для рациональных чисел, только прибавляю ещё и дробь, и, соответственно, ещё две вспомогательных функции: &lt;pre class="sh_python"&gt;to_frac = lambda digs: to_int(digs)*1.0/10**len(digs)
mk_frac = lambda (s,i,f): s*(i+f)
frac_num = sign + (digits &amp;gt;&amp;gt; to_int) + (skip(maybe(a("."))) + (digits &amp;gt;&amp;gt; to_frac)) &gt;&gt; mk_frac&lt;/pre&gt;&lt;br /&gt;
Вот и всё. Использовать примерно так: &lt;pre class="sh_python"&gt;&amp;gt;&amp;gt;&amp;gt; frac_num.parse("-1.25")
-1.25&lt;/pre&gt;&lt;br /&gt;
Второй половиной кода оказалось написание парсеров для аналогов &lt;code&gt;Literal&lt;/code&gt; и &lt;code&gt;Word&lt;/code&gt; из &lt;code&gt;pyparsing&lt;/code&gt;. Это необходимо, потому что без токенизации приходится распознавать цепочки токенов. Дополнительно создал парсер &lt;code&gt;ws&lt;/code&gt; для пропуска пробелов: &lt;pre class="sh_python"&gt;pack = lambda cs: ''.join(cs)
literal = lambda s: reduce(lambda a,b: a+b, map(a,s)) &amp;gt;&amp;gt; pack
word = lambda p: oneplus(some(p)) &amp;gt;&amp;gt; pack
ws = skip(many(some(lambda c: c.isspace())))&lt;/pre&gt;&lt;br /&gt;
После этих определений собственно парсер выбранного формата укладывается в три выражения: &lt;pre class="sh_python"&gt;varname = word(lambda c: c.isalpha())
var = (ws + varname + ws + frac_num + ws)
custom_format = skip(literal("Inspection")) + \
ws + skip(literal("#")) + \
ws + int_num + \
ws + skip(literal("SHOULD")) + \
ws + skip(literal("Ref. Sys")) + \
ws + int_num + \
many(var)&lt;/pre&gt;&lt;br /&gt;
Смотрим на результат: &lt;pre class="sh_sh"&gt;$ python test.py &amp;lt; input.txt 
(2, 1, [('X', 28.749300000000002), ('Y', 78.995999999999995), ('Z', -1.0014000000000001)])&lt;/pre&gt;&lt;br /&gt;
&lt;br /&gt;
В общем, впечатления хорошие. Документация у библиотеки приличная. Использовать приятно. Только одно досадное неудобство было связано с тем, что &lt;code&gt;setup.py install --prefix=...&lt;/code&gt; из дистрибутивного тарбола не сработал как надо. Впрочем, библиотека такая маленькая, что можно положить все три её файла прямо в свой проект, без общесистемной установки.&lt;br /&gt;
&lt;br /&gt;
Тонкости: нужно быть осторожным, не помещая универсально успешный парсер внутрь &lt;code&gt;many&lt;/code&gt;, чтобы избежать вечного цикла. Короче, нельзя внутрь &lt;code&gt;many&lt;/code&gt; помещать &lt;code&gt;many&lt;/code&gt;, &lt;code&gt;maybe&lt;/code&gt; и &lt;code&gt;pure&lt;/code&gt;. Подробнее — см. &lt;a href="http://archlinux.folding-maps.org/2009/funcparserlib/FAQ"&gt;FAQ&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
См. также &lt;a href="http://sovety.blogspot.com/2009/07/easy-parsing-in-python.html" title="Пример использования pyparsing"&gt;заметку про pyparsing&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37628976-1126319472123305951?l=sovety.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/sovety/~4/qK0Rn8bktVQ" height="1" width="1"/&gt;</description><link>http://sovety.blogspot.com/2009/09/yet-another-parser-combinator-library.html</link><author>noreply@blogger.com (Сергей)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">6</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37628976.post-6775858460252318878</guid><pubDate>Fri, 04 Sep 2009 15:08:00 +0000</pubDate><atom:updated>2009-09-04T17:17:46.516+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">видео</category><category domain="http://www.blogger.com/atom/ns#">командная строка</category><category domain="http://www.blogger.com/atom/ns#">графика</category><title>Как сделать видеофайл из анимированного GIF-а</title><description>Для того, чтобы из анимированного GIFа сделать видеофайл, я недавно использовал &lt;code&gt;gifsicle&lt;/code&gt; (чтобы разоптимизировать GIF и разбить на кадры) и &lt;code&gt;ffmpeg&lt;/code&gt; (чтобы сделать из кадров видео):&lt;br /&gt;&lt;pre class="sh_sh"&gt;gifsicle -U --explode "input.gif"&lt;br /&gt;for f in *.gif ; do mv "$f" "$f.gif" ; done&lt;br /&gt;ffmpeg -r 25 -i "input.gif.%03d.gif" -sameq -s 320x240 output.flv&lt;/pre&gt;&lt;br /&gt;Если нужно добавить чёрных полей (до нужного размера), действую примерно так (в данном случае, хочу получить 320×240):&lt;br /&gt;&lt;pre class="sh_sh"&gt;ffmpeg -i input.file -s 320x180 -padtop 30 -padbottom 30 output.file&lt;/pre&gt;&lt;br /&gt;Я не использую для разделения на кадры ImageMagick (convert), потому что мне кажется, что &lt;code&gt;gifsicle&lt;/code&gt; работает быстрее и требует меньше памяти.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;&lt;a href="http://nix-tips.blogspot.com/2009/05/animated-gif-to-aviflv.html" title="Animated gif to avi/flv"&gt;(in English)&lt;/a&gt;&lt;/em&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37628976-6775858460252318878?l=sovety.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/sovety/~4/PsDts1rTKK4" height="1" width="1"/&gt;</description><link>http://sovety.blogspot.com/2009/09/animated-gif-to-avi-or-flv.html</link><author>noreply@blogger.com (Сергей)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37628976.post-8432976823388885184</guid><pubDate>Wed, 02 Sep 2009 20:58:00 +0000</pubDate><atom:updated>2009-09-04T17:04:47.929+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">debian</category><category domain="http://www.blogger.com/atom/ns#">ubuntu</category><title>Как пометить пакеты в Aptitude, чтобы потом удалить</title><description>Очень полезная возможность в &lt;code&gt;aptitude&lt;/code&gt; — пользовательские метки для выбранных пакетов.&lt;br /&gt;&lt;br /&gt;Например, нужно поставить какой-то набор пакетов, чтобы собрать программу X из исходников, а потом нужно эти пакеты удалить. При установке помечаем выбранные пакеты какой-то своей меткой (&lt;code&gt;builddeps&lt;/code&gt; в моём примере):&lt;br /&gt;&lt;pre&gt;$ sudo aptitude install &lt;b&gt;--add-user-tag builddeps&lt;/b&gt; libчто-то-dev libчто-то-ещё-dev ...&lt;/pre&gt;&lt;br /&gt;А потом, когда эти пакеты больше не требуются, их удаляем, выбрав по той же метке:&lt;br /&gt;&lt;pre&gt;$ sudo aptitude purge &lt;b&gt;'?user-tag(builddeps)'&lt;/b&gt;&lt;/pre&gt;&lt;br /&gt;Поисковый шаблон &lt;code&gt;?user-tag(метка)&lt;/code&gt; можно использовать совместно со всеми другими &lt;a href=" http://sovety.blogspot.com/2008/01/aptitude.html" title="Поисковые шаблоны Aptitude"&gt;поисковыми шаблонами&lt;/a&gt;. Присваивать метки можно не только при установке (&lt;code&gt;install&lt;/code&gt;), но и во многих других операциях.&lt;br /&gt;&lt;br /&gt;Как видно из примера, особенно эта возможность полезна для самостоятельной &lt;a href="http://sovety.blogspot.com/2008/01/deb.html"&gt;сборки пакетов&lt;/a&gt; и &lt;a href="http://sovety.blogspot.com/2006/11/blog-post_16.html"&gt;программ из исходников&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;P.S. Не помню, есть ли &lt;code&gt;--add-user-tag&lt;/code&gt; в Ubuntu, но в Debian Lenny (&lt;code&gt;aptitude-0.4.11&lt;/code&gt;) точно есть.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;&lt;a href="http://nix-tips.blogspot.com/2009/09/tag-installed-packages-to-delete-later.html" title="Tag installed packages to delete later"&gt;This post in English&lt;/a&gt;&lt;/em&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37628976-8432976823388885184?l=sovety.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/sovety/~4/qSC9S0VHt04" height="1" width="1"/&gt;</description><link>http://sovety.blogspot.com/2009/09/aptitude-tag-packages-to-remove-later.html</link><author>noreply@blogger.com (Сергей)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">4</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37628976.post-2349874800792776503</guid><pubDate>Fri, 28 Aug 2009 00:59:00 +0000</pubDate><atom:updated>2009-08-28T13:05:51.898+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">интернет</category><category domain="http://www.blogger.com/atom/ns#">блоги</category><title>Скорое обновление RuNIX.org</title><description>Наконец собрался и подготовил обновление планеты русских блогов о *NIX &lt;a href="http://runix.org/"&gt;RuNIX.org&lt;/a&gt;. Дело в том, что скрипт Planet Planet, на котором аггрегатор работает сейчас уже давно не обновляется, а на смену ему пришёл &lt;a href="http://intertwingly.net/code/venus/"&gt;Planet Venus&lt;/a&gt;. Вот на него и переходим.&lt;br /&gt;&lt;br /&gt;Грядущие изменения, большие и маленькие: &lt;ul&gt;&lt;li&gt;Другой, на мой вкус более аккуратный и человечный, шаблон.&lt;br /&gt;&lt;li&gt;Список блогов теперь можно сворачивать (будет такой маааленький треугольник в заголовке списка). По наведению на название блога из списка, открывается меню недавних из этого блога.&lt;br /&gt;&lt;li&gt;Появилась возможность навигации с клавиатуры (для тех, кто читает планету на сайте). Переход между записями по клавишам &lt;kbd&gt;j&lt;/kbd&gt; и &lt;kbd&gt;k&lt;/kbd&gt; можно включить в боковой врезке.&lt;br /&gt;&lt;li&gt;Если в фиде определена картинка пользователя (Channel image или Icon), планета может её использовать. RSS&amp;nbsp;2.0 из ЖЖ, например, такие картинки отдаёт (соответственно, у некоторых авторов автоматически появятся портреты). Естественно, присылать нам свои фотки 64×64 по-прежнему можно.&lt;br /&gt;&lt;li&gt;После перехода на Planet Venus, мы теперь можем пропускать заметки с будущими датами и изменения &lt;code&gt;atom:updated&lt;/code&gt; (Помните, когда порой вдруг валились кучей старые посты какого-нибудь блога, как правило на blogspot? Вот чтобы так не было, и нужно). Должно стать лучше.&lt;br /&gt;&lt;li&gt;Шаблон теперь на XSLT, поэтому появилась определённая свобода в обработке записей (я в XSLT не силён, но думаю, справимся). Можно будет что-то исправлять или фильтровать. Дополнительно теперь есть возможность подключать фильтры-плагины.&lt;br /&gt;&lt;/ul&gt;На сайт runix.org изменения попадут после того, как &lt;a href="http://gq.net.ru/"&gt;GQ&lt;/a&gt; установит на сервере новый скрипт. Посмотреть одним глазком на новую планету уже можно &lt;a href="http://sovety.org.ru/new-runix-demo/"&gt;здесь&lt;/a&gt; — для сравнения сохранил &lt;a href="http://sovety.org.ru/old-runix-demo/"&gt;как было&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Кто не заметил ссылку — &lt;a href="http://sovety.org.ru/new-runix-demo/"&gt;новая планет будет выглядеть так&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Приветствуются замечания по делу (если по вёрстске — лучше сразу патч к CSS, если считаете, что что-то нужно фильтровать-менять — поделюсь XSLT, обсудим). Если что-то из ряда вон и лучше ничего не трогать — кричите!&lt;br /&gt;&lt;br /&gt;Есть некоторые задумки (не факт, что скоро сделаю):&lt;ul&gt;&lt;li&gt;добавить какой-нибудь ticket-tracker, чтобы заявки и жалобы быстрее собирать и все могли их видеть и комментировать;&lt;br /&gt;&lt;li&gt;сделать javascript-овый флажок «пожаловаться» рядом с каждой записью, чтобы вовремя замечать неподходящий или неинтересный материал (я не всегда успеваю такой заметить, а написать письмо читателям обычно более лениво, чем ткнуть в кнопку);&lt;br /&gt;&lt;/ul&gt;Помощь и пожелания принимаются.&lt;br /&gt;&lt;br /&gt;P.S. Да, забыл предупредить. Допускаю, что при обновлении скрипта некоторые записи пройдут повторно. Прошу извинить за неудобство.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37628976-2349874800792776503?l=sovety.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/sovety/~4/rYJR84MyfmE" height="1" width="1"/&gt;</description><link>http://sovety.blogspot.com/2009/08/runixorg-upgrade.html</link><author>noreply@blogger.com (Сергей)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">17</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37628976.post-6781377271917150624</guid><pubDate>Thu, 27 Aug 2009 15:31:00 +0000</pubDate><atom:updated>2009-08-27T17:49:12.596+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">latex</category><category domain="http://www.blogger.com/atom/ns#">ссылки</category><category domain="http://www.blogger.com/atom/ns#">графика</category><title>Декоративная табличка в LaTeX</title><description>В TeXblog появилась заметка, как делать декоративные таблички в LaTeX. Вот такие:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://texblog.net/latex-archive/graphics/tikz-table/"&gt;&lt;img src="http://lh4.ggpht.com/_PiUWFeprZSw/Spao7fZs4qI/AAAAAAAALYw/OSTCL3_HKws/s800/tikz-table-big.png" alt="Fancy tables with LateX and Tikz"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Табличка набрана как обычно, но внутри «узла» окружения &lt;code&gt;tikzpicture&lt;/code&gt;, а фон и раскрашенные шапки — средствами Tikz на фоне. Исходник примера — в &lt;a href="http://texblog.net/latex-archive/graphics/tikz-table/"&gt;TeX blog&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Кстати, давно хочу написать про PGF/Tikz. Что-нибудь интересно?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37628976-6781377271917150624?l=sovety.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/sovety/~4/xR0yLrbErAI" height="1" width="1"/&gt;</description><link>http://sovety.blogspot.com/2009/08/tables-with-rounded-corners-in-latex.html</link><author>noreply@blogger.com (Сергей)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh4.ggpht.com/_PiUWFeprZSw/Spao7fZs4qI/AAAAAAAALYw/OSTCL3_HKws/s72-c/tikz-table-big.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">3</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37628976.post-1404472487712866818</guid><pubDate>Wed, 26 Aug 2009 12:53:00 +0000</pubDate><atom:updated>2009-08-26T15:11:16.127+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">PDF</category><category domain="http://www.blogger.com/atom/ns#">debian</category><category domain="http://www.blogger.com/atom/ns#">ubuntu</category><title>Не видно букв в японском PDF?</title><description>Бывают такие PDF, родом из Японии, в которых, если попытаться открыть их в Evince или XPDF — букв вообще не видно, а в Adobe Reader-е вместо букв видны только точки. В свойствах документа список встроенных шрифтов вообще выглядит пустым. Google Docs же такие PDF открывает, что интересно. И открыв такие PDF в Google Docs, можно увидеть, что в них всё таки есть кое-что и латиницей. Только латиница эта — из японских шрифтов (квадратная и широкая).&lt;br /&gt;&lt;br /&gt;Оказывается, для отображения этих PDF нужно поставить кое-какие дополнительные пакеты. Для Evince — нужно поставить рекомендуемый пакет &lt;code&gt;poppler-data&lt;/code&gt;&lt;sup&gt;*&lt;/sup&gt;. Для XPDF — нужно поставить пакет &lt;code&gt;xpdf-japanese&lt;/code&gt;&lt;sup&gt;*&lt;/sup&gt;. И только после этого мы действительно сможем нормально смотреть такие PDF-файлы.&lt;br /&gt;&lt;br /&gt;&lt;sup&gt;*&lt;/sup&gt; названия пакетов даны для Debian/Ubuntu.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37628976-1404472487712866818?l=sovety.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/sovety/~4/fhoPlfsDXyk" height="1" width="1"/&gt;</description><link>http://sovety.blogspot.com/2009/08/cannot-see-letters-in-japanese-pdf.html</link><author>noreply@blogger.com (Сергей)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37628976.post-757306306577678679</guid><pubDate>Wed, 26 Aug 2009 11:37:00 +0000</pubDate><atom:updated>2009-08-27T15:12:53.899+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">gnome</category><title>Старая флэшка монтируется только для чтения?</title><description>Уже не раз столкнулся: пользуясь ГНОМом, вставляешь какую-нибудь старую флэшку или карточку памяти в кард-ридер, она вроде как обычно автоматом подключается, но права доступа какие-то чудные (скажем, только для чтения). А другие карточки и флэшки вставляешь — всё нормально.&lt;br /&gt;&lt;br /&gt;Догадался, что дело в том, что когда-то ещё на старой машине я настраивал параметры монтирования для каждого носителя отдельно. С тех пор домашний каталог благополучно переезжал из системы в систему, и настройки ГНОМа переезжали вместе с ним. А вот новые группы и пользователи не всегда &lt;em&gt;точно&lt;/em&gt; соответствовали тому, что было раньше.&lt;br /&gt;&lt;br /&gt;В результате, при попытке вставить какой-нибудь старый внешний носитель, ГНОМ использует настройки рассчитанные на совсем другую машину (другие группы и другую принадлежность пользователя к ним). Естественно, такие настройки &lt;code&gt;gnome-mount&lt;/code&gt; надо просто удалить (ну или поправить), вот только найти их в гномовских закромах не так-то просто.&lt;br /&gt;&lt;br /&gt;Мой способ: вставляю проблемный носитель и выполняю &lt;code&gt;blkid&lt;/code&gt;. Например,&lt;br /&gt;&lt;pre class="sh_sh"&gt;$ blkid &lt;br /&gt;/dev/sdb1: SEC_TYPE="msdos" LABEL="PALM-CARD" UUID="15F4-492C" TYPE="vfat"&lt;/pre&gt;&lt;br /&gt;Запоминаю UUID и запускаю &lt;strike&gt;редактор реестра&lt;/strike&gt; &lt;code&gt;gconf-editor&lt;/code&gt;. Нахожу &lt;code&gt;/system/storage/volumes/_org_freedesktop_Hal_devices_volume_uuid_15F4_492C&lt;/code&gt; и там сбрасываю установленный ключ &lt;code&gt;mount_options&lt;/code&gt;. Отсоединяю носитель и подключаю опять. Пока что все проблемы с монтирование старых внешних носителей разрешались именно таким образом.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37628976-757306306577678679?l=sovety.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/sovety/~4/d1glU8my1TA" height="1" width="1"/&gt;</description><link>http://sovety.blogspot.com/2009/08/old-pendrive-or-memory-card-is-readonly.html</link><author>noreply@blogger.com (Сергей)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37628976.post-6819542087198840357</guid><pubDate>Tue, 25 Aug 2009 10:13:00 +0000</pubDate><atom:updated>2009-08-26T00:48:41.761+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">latex</category><category domain="http://www.blogger.com/atom/ns#">linux</category><category domain="http://www.blogger.com/atom/ns#">командная строка</category><category domain="http://www.blogger.com/atom/ns#">скрипт</category><title>Как отслеживать изменения файлов в скриптах</title><description>Задача: отслеживать в скриптах изменения файла или файлов и выполнять какие-нибудь команды, когда файлы изменяются.&lt;br /&gt;&lt;br /&gt;Пример: пересобирать документ LaTeX при изменении (сохранении) одного из исходных файлов.&lt;br /&gt;&lt;br /&gt;Решение: остлеживать изменения можно с помощью утилит &lt;a href="http://inotify-tools.sourceforge.net/"&gt;inotify-tools&lt;/a&gt;. Одна утилита, &lt;code&gt;inotifywait&lt;/code&gt; ждёт указанных изменений и после этого завершается с тем или иным кодом возврата. Если произошло ожидаемое событие, код возврата &lt;code&gt;0&lt;/code&gt; (успех). Именно &lt;code&gt;inotifywait&lt;/code&gt; и используется в моём примере ниже. Другая утилита, &lt;code&gt;inotifywatch&lt;/code&gt;, наблюдает за файлами и &lt;em&gt;собирает&lt;/em&gt; информацию об изменениях, на выходе выводит табличку того, что заметила. Примеры применения этой утилиты смотрите на сайте &lt;a href="http://inotify-tools.sourceforge.net/"&gt;inotify-tools&lt;/a&gt;, там же есть и дополнительные примеры использования &lt;code&gt;inotifywait&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Пример использования: в данном случае я предполагаю, что все исходные файлы документа LaTeX лежат в текущем каталоге, а для сборки достаточно использовать &lt;code&gt;pdflatex&lt;/code&gt; и &lt;code&gt;bibtex&lt;/code&gt;. Вечный цикл: ждём любых изменений файлов текста или библиографии (первая команда цикла), в случае успеха (обнаруженных изменений) исполняем все нужные команды сборки документа (вторая команда цикла).&lt;br /&gt;&lt;pre class="sh_sh"&gt;while true ; do \&lt;br /&gt;  inotifywait *.tex *.bib \&lt;br /&gt;  &amp;&amp; ( pdflatex -interaction=nonstopmode mypaper &amp;&amp; \&lt;br /&gt;       bibtex mypaper &amp;&amp; \&lt;br /&gt;       pdflatex -interaction=nonstopmode mypaper ) \&lt;br /&gt;done&lt;/pre&gt;&lt;br /&gt;Естественно, применять можно для чего угодно, не только для LaTeX.&lt;br /&gt;&lt;br /&gt;P.S. Вариант запуска LaTeX с опцией &lt;code&gt;-interaction=nonstopmode&lt;/code&gt; позволяет с одной стороны избежать запроса интерактивного ввода в случае ошибки компиляции, а с другой стороны, позволяет эти ошибки компиляции всё же увидеть.&lt;br /&gt;&lt;br /&gt;P.P.S. Рецепт работает только в линуксе. Для *BSD есть библиотечка &lt;a href="http://mark.heily.com/pnotify" title="inotify alternative for BSD"&gt;pnotify&lt;/a&gt; и &lt;a href="http://wiki.netbsd.se/kqueue_tutorial"&gt;kqueue&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37628976-6819542087198840357?l=sovety.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/sovety/~4/DenxwtSIokk" height="1" width="1"/&gt;</description><link>http://sovety.blogspot.com/2009/08/monitor-file-changes-in-shell-script.html</link><author>noreply@blogger.com (Сергей)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">9</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37628976.post-6130601797554846566</guid><pubDate>Thu, 20 Aug 2009 10:50:00 +0000</pubDate><atom:updated>2009-08-21T17:03:25.756+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">командная строка</category><category domain="http://www.blogger.com/atom/ns#">xml</category><category domain="http://www.blogger.com/atom/ns#">скрипт</category><title>Автоматические отступы в XML</title><description>Для просмотра какого-нибудь XML часто нужно автоматически его отформатировать (чтобы отступы слева соответствовали вложенности элементов). Особенно это полезно, когда весь исходный XML записан в одну большую строку. Такие файлы — это нечитаемая каша, которую, однако, легко привести в порядок.&lt;br /&gt;&lt;br /&gt;&lt;h5&gt;Первый способ — используем XSLT&lt;/h5&gt;Есть у меня файл с вот таким XSL-преобразованием:&lt;pre class="sh_xml"&gt;&amp;lt;xsl:stylesheet version=&amp;quot;1.0&amp;quot; &lt;br /&gt;   xmlns:xsl=&amp;quot;http://www.w3.org/1999/XSL/Transform&amp;quot;&amp;gt;&lt;br /&gt;&amp;lt;xsl:output method=&amp;quot;xml&amp;quot;/&amp;gt;&lt;br /&gt;&amp;lt;xsl:param name=&amp;quot;indent-increment&amp;quot; select=&amp;quot;&amp;apos;   &amp;apos;&amp;quot; /&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;xsl:template match=&amp;quot;*&amp;quot;&amp;gt;&lt;br /&gt;   &amp;lt;xsl:param name=&amp;quot;indent&amp;quot; select=&amp;quot;&amp;apos;&amp;amp;#xA;&amp;apos;&amp;quot;/&amp;gt;&lt;br /&gt;&lt;br /&gt;   &amp;lt;xsl:value-of select=&amp;quot;$indent&amp;quot;/&amp;gt;&lt;br /&gt;   &amp;lt;xsl:copy&amp;gt;&lt;br /&gt;     &amp;lt;xsl:copy-of select=&amp;quot;@*&amp;quot; /&amp;gt;&lt;br /&gt;     &amp;lt;xsl:apply-templates&amp;gt;&lt;br /&gt;       &amp;lt;xsl:with-param name=&amp;quot;indent&amp;quot;&lt;br /&gt;            select=&amp;quot;concat($indent, $indent-increment)&amp;quot;/&amp;gt;&lt;br /&gt;     &amp;lt;/xsl:apply-templates&amp;gt;&lt;br /&gt;     &amp;lt;xsl:value-of select=&amp;quot;$indent&amp;quot;/&amp;gt;&lt;br /&gt;   &amp;lt;/xsl:copy&amp;gt;&lt;br /&gt;&amp;lt;/xsl:template&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;xsl:template match=&amp;quot;comment()|processing-instruction()&amp;quot;&amp;gt;&lt;br /&gt;   &amp;lt;xsl:copy /&amp;gt;&lt;br /&gt;&amp;lt;/xsl:template&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;!-- WARNING: this is dangerous. Handle with care --&amp;gt;&lt;br /&gt;&amp;lt;xsl:template match=&amp;quot;text()[normalize-space(.)=&amp;apos;&amp;apos;]&amp;quot;/&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/xsl:stylesheet&amp;gt;&lt;/pre&gt;&lt;br /&gt;Код взял &lt;a href="http://www.dpawson.co.uk/xsl/sect2/pretty.html" title="Pretty Printing XML"&gt;здесь&lt;/a&gt; (предложил Николай Григорьев). Там ещё несколько вариантов есть.&lt;br /&gt;&lt;br /&gt;В дополнение к XSL-файлу есть у меня ещё и скрипт-однострочник, который это преобразование применяет. Я выполняю XSL с помощью &lt;a href="http://sovety.blogspot.com/search?q=xmlstarlet"&gt;любимого мной&lt;/a&gt; &lt;code&gt;&lt;a href="http://xmlstar.sourceforge.net/"&gt;xmlstarlet&lt;/a&gt;&lt;/code&gt;. Это программка с интерфейсом командной строки для работы с XML.&lt;pre class="sh_sh"&gt;#!/bin/sh&lt;br /&gt;# указать правильный путь к файлу с преобразованием!&lt;br /&gt;xmlstarlet tr ~/bin/indent-xml.xsl&lt;/pre&gt;&lt;br /&gt;Пользуюсь этим скриптом так:&lt;pre class="sh_sh"&gt;$ xmlindent &amp;lt; исходный.xml | view -&lt;/pre&gt;&lt;br /&gt;И всё, можно читать любой XML с правильными отступами. И подсветкой синтаксиса (&lt;code&gt;view&lt;/code&gt; — это Vim!). Кроме &lt;code&gt;xmlstarlet&lt;/code&gt; есть и другие XSLT-процессоры. На память приходит &lt;code&gt;xsltproc&lt;/code&gt; и библиотечки для разных языков программирования. Вот, например, &lt;a href="http://xmlhack.ru/books/xslt/ch_09_07.html" title="Выполнение XSLT-преобразований в Python"&gt;однострочник на Python&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;h5&gt;Второй способ — используем &lt;code&gt;xmllint&lt;/code&gt;&lt;/h5&gt;В пакете &lt;code&gt;libxml2-utils&lt;/code&gt; есть программка для проверки и форматирования XML — &lt;code&gt;xmllint&lt;/code&gt;. Для форматирования использовать так:&lt;br /&gt;&lt;pre class="sh_sh"&gt;$ xmllint --format исходный.xml&lt;/pre&gt;&lt;br /&gt;Так даже проще.&lt;br /&gt;&lt;br /&gt;&lt;h5&gt;Третий способ — &lt;code&gt;xmlindent&lt;/code&gt;&lt;/h5&gt;&lt;code&gt;xmlindent&lt;/code&gt; — отдельная утилита, написанная на чистом Си. Говорят, работает и с задачей справляется.&lt;br /&gt;&lt;br /&gt;По теме:&lt;br /&gt;&lt;a href="http://sovety.blogspot.com/2009/04/html-and-xml-in-vim.html"&gt;Редактирование HTML и XML в Vim&lt;/a&gt; (&lt;strong&gt;добавил про HTML Entities&lt;/strong&gt;)&lt;br /&gt;&lt;a href="http://sovety.blogspot.com/2009/01/selecting-text-objects-in-vim.html"&gt;Выделение HTML-тегов, строк и блоков кода в Vim&lt;/a&gt;&lt;br /&gt;&lt;a href="http://sovety.blogspot.com/2007/11/vim_20.html"&gt;Vim в терминале: сохранение отступов вставленного текста&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37628976-6130601797554846566?l=sovety.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/sovety/~4/URBc4cSVPH8" height="1" width="1"/&gt;</description><link>http://sovety.blogspot.com/2009/08/autoindent-xml.html</link><author>noreply@blogger.com (Сергей)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">7</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37628976.post-1627155037845239858</guid><pubDate>Mon, 20 Jul 2009 19:59:00 +0000</pubDate><atom:updated>2009-08-20T14:30:33.942+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">видео</category><category domain="http://www.blogger.com/atom/ns#">командная строка</category><category domain="http://www.blogger.com/atom/ns#">графика</category><title>Как ускорить или замедлить видеоролик</title><description>Иногда нужно замедлить (растянуть) видеоролик, чтобы он игрался как будто в режиме замедленного воспроизведения, а иногда нужно наоборот, показать «на перемотке» слишком длинный ролик, выбросить часть кадров и ускорить воспроизведение. О том как это сделать — сия заметка.&lt;br /&gt;&lt;br /&gt;Изменить частоту кадров в видеопотоке позволяет программа &lt;code&gt;yuvfps&lt;/code&gt; из пакета &lt;code&gt;mjpegtools&lt;/code&gt;. Как и большинство утилит пакета она принимает и отдаёт видеопоток в формате YUV4MPEG. И &lt;code&gt;ffmpeg&lt;/code&gt;, и &lt;code&gt;mencoder&lt;/code&gt; тоже умеют работать с YUV4MPEG (и умеют читать и писать всякие другие форматы). Я приведу пример использования &lt;code&gt;ffmpeg&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Итак, чтобы ускорить видео, нужно взять исходный файл и посмотреть, какая в нём частота кадров&lt;br /&gt;&lt;pre&gt;$ ffmpeg -i normal.ogg&lt;br /&gt;FFmpeg version SVN-r13582, Copyright (c) 2000-2008 Fabrice Bellard, et al.&lt;br /&gt;...&lt;br /&gt;Input #0, ogg, from 'normal.ogg':&lt;br /&gt;  Duration: 00:00:10.49, start: 0.000000, bitrate: 150 kb/s&lt;br /&gt;    Stream #0.0: Video: theora, yuv420p, 320x240 [PAR 1:1 DAR 4:3], 30.00 tb(r)&lt;br /&gt;Must supply at least one output file&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;В данном случае исходный файл — 30 кадров в секунду. Затем нужно решить, во сколько раз уменьшить число кадров (исходя из желаемой длительности ролика). Потом берём исходный файл (&lt;code&gt;normal.ogg&lt;/code&gt; в примере) и преобразовываем его в YUV4MPEG-поток (первый вызов &lt;code&gt;ffmpeg&lt;/code&gt;), после нужно дважды вызвать &lt;code&gt;yuvfps&lt;/code&gt;, первый раз, чтобы изменить число кадров в потоке (&lt;code&gt;yuvfps -s 5:2 -r 1:1&lt;/code&gt; сокращает исходные 2.5 кадра до одного), второй раз, чтобы перезаписать заголовок потока, указав скорость воспроизведения (&lt;code&gt;yuvfps -r 30:1 -c&lt;/code&gt; устанавливает скорость 30 кадров в секунду). В конце опять вызываем &lt;code&gt;ffmpeg&lt;/code&gt; и кодируем в нужный формат (я сохранил в формате Ogg/Theora, чтобы можно было вставлять в веб-странички тэгом &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt;). Всё вместе:&lt;br /&gt;&lt;pre&gt;$ ffmpeg -i normal.ogg -sameq -f yuv4mpegpipe - | \&lt;br /&gt;  yuvfps -s 5:2 -r 1:1 | yuvfps -r 30:1 -c | \&lt;br /&gt;  ffmpeg -f yuv4mpegpipe -i - -sameq -y fast.ogg&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Аналогично можно увеличить число кадров. Дополнительные кадры интерполируются:&lt;br /&gt;&lt;pre&gt;$ ffmpeg -i normal.ogg -sameq -f yuv4mpegpipe - | \&lt;br /&gt;  yuvfps -s 1:3 -r 1:1 | yuvfps -r 30:1 -c | \&lt;br /&gt;  ffmpeg -f yuv4mpegpipe -i - -sameq -y slow.ogg&lt;/pre&gt;&lt;br /&gt;В этот раз видео замедляется в 3 раза: на каждую «треть» исходного кадра (&lt;code&gt;-s&amp;nbsp;1:3&lt;/code&gt;) создаётся целый кадр (&lt;code&gt;-r&amp;nbsp;1:1&lt;/code&gt;). Вообще, как легко заметить, в качестве частоты кадров для &lt;code&gt;yuvfps&lt;/code&gt; можно указывать любые дроби в виде числитель:знаменатель.&lt;br /&gt;&lt;br /&gt;Немного более гладкой интерполяции при увеличении числа кадров вроде бы можно добиться, если использовать &lt;a href="http://jcornet.free.fr/linux/yuvmotionfps.html"&gt;yuvmotionfps&lt;/a&gt;, но чудес он не делает.&lt;br /&gt;&lt;br /&gt;Теперь можно сравнить все три видео. Для просмотра нужен современный браузер с поддержкой тэга &lt;code&gt;&amp;lt;video/&amp;gt;&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Исходное видео:&lt;br /&gt;&lt;br /&gt;&lt;video src='http://sovety.org.ru/video/normal.ogg' controls&gt;&lt;br /&gt;&lt;div class="error"&gt;Ой! А ваш браузер устарел и не умеет нормально играть видео (не поддерживает тэг &amp;lt;video&amp;gt;). Установите &lt;a href="http://getfirefox.com/"&gt;Firefox 3.5&lt;/a&gt; или &lt;a href="http://www.google.com/chrome"&gt;Google Chrome 3&lt;/a&gt;.&lt;/div&gt;&lt;br /&gt;&lt;a href="http://sovety.org.ru/video/normal.ogg"&gt;Скачать видео-файл&lt;/a&gt;.&lt;br /&gt;&lt;/video&gt;&lt;br /&gt;&lt;br /&gt;Ускоренное в 2.5 раза:&lt;br /&gt;&lt;br /&gt;&lt;video src='http://sovety.org.ru/video/fast.ogg' controls&gt;&lt;br /&gt;&lt;div class="error"&gt;Ой! А ваш браузер устарел и не умеет нормально играть видео (не поддерживает тэг &amp;lt;video&amp;gt;). Установите &lt;a href="http://getfirefox.com/"&gt;Firefox 3.5&lt;/a&gt; или &lt;a href="http://www.google.com/chrome"&gt;Google Chrome 3&lt;/a&gt;.&lt;/div&gt;&lt;br /&gt;&lt;a href="http://sovety.org.ru/video/fast.ogg"&gt;Скачать видео-файл&lt;/a&gt;.&lt;br /&gt;&lt;/video&gt;&lt;br /&gt;&lt;br /&gt;И замедленное в 3 раза:&lt;br /&gt;&lt;br /&gt;&lt;video src='http://sovety.org.ru/video/slow.ogg' controls&gt;&lt;br /&gt;&lt;div class="error"&gt;Ой! А ваш браузер устарел и не умеет нормально играть видео (не поддерживает тэг &amp;lt;video&amp;gt;). Установите &lt;a href="http://getfirefox.com/"&gt;Firefox 3.5&lt;/a&gt; или &lt;a href="http://www.google.com/chrome"&gt;Google Chrome 3&lt;/a&gt;.&lt;/div&gt;&lt;br /&gt;&lt;a href="http://sovety.org.ru/video/slow.ogg"&gt;Скачать видео-файл&lt;/a&gt;.&lt;br /&gt;&lt;/video&gt;&lt;br /&gt;&lt;br /&gt;P.S. В качестве иллюстрации использовал ролик &lt;a href="http://commons.wikimedia.org/wiki/File:Breitenlee-VESTAS-V-52_.ogg"&gt;Breitenlee-VESTAS-V-52&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37628976-1627155037845239858?l=sovety.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/sovety/~4/cQWebkxDQzU" height="1" width="1"/&gt;</description><link>http://sovety.blogspot.com/2009/07/slow-motion-and-fast-forward-video.html</link><author>noreply@blogger.com (Сергей)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">4</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37628976.post-7835783489572360161</guid><pubDate>Tue, 14 Jul 2009 09:03:00 +0000</pubDate><atom:updated>2009-07-14T11:32:44.345+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">новость</category><title>Вы не поверите — Ubuntu Cola!</title><description>Эта заметка не про линукс. Эта заметка про газированный напиток Ubuntu Cola. Вот он:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh3.ggpht.com/_PiUWFeprZSw/SlxQe-xvQUI/AAAAAAAALWY/6KeXSETIMJA/s800/Ubuntu_cola_500px.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;Купил вчера в автомате в итальянском университете за 1,80&amp;nbsp;€. На этикетке спереди значок &lt;a href="http://www.fairtrade.org.uk/"&gt;Fairtrade&lt;/a&gt;. Сзади надпись:&lt;blockquote&gt;Ubuntu. “&lt;em&gt;Я есть, потому что есть мы&lt;/em&gt;”. Благодаря программе Fairtrade производители тростникового сахара в Малави и Замбии могут заключать более выгодные контракты и вкладывать средства в социальные, экономические и природоохранные проекты. К тому же, мы возвращаем 15% нашей прибыли в эти страны через программу Ubuntu Africa. Посетите: &lt;a href="http://www.ubuntu-trading.com/"&gt;www.ubuntu-trading.com&lt;/a&gt;&lt;/blockquote&gt;Вот такая кола. Вот такая убунту. Кстати, на вкус оказалась довольно хороша.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37628976-7835783489572360161?l=sovety.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/sovety/~4/v1nQsZSWEM8" height="1" width="1"/&gt;</description><link>http://sovety.blogspot.com/2009/07/ubuntu-cola.html</link><author>noreply@blogger.com (Сергей)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh3.ggpht.com/_PiUWFeprZSw/SlxQe-xvQUI/AAAAAAAALWY/6KeXSETIMJA/s72-c/Ubuntu_cola_500px.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">6</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37628976.post-2198141703083235856</guid><pubDate>Sun, 12 Jul 2009 17:51:00 +0000</pubDate><atom:updated>2009-09-08T11:36:47.947+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">программирование</category><category domain="http://www.blogger.com/atom/ns#">python</category><title>Необыкновенно лёгкий парсинг в Python</title><description>Нашёл просто волшебную библиотечку для парсинга в Python (хм, правильно говорить &lt;a href="http://ru.wikipedia.org/wiki/%D0%A1%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7"&gt;синтаксического анализа&lt;/a&gt;), &lt;code&gt;pyparsing&lt;/code&gt;. Ниже на простом примере я покажу, как её можно использовать для разбора пользовательских форматов данных.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Нашёл так: читая Real World Haskell, узнал про &lt;em&gt;комбинаторную&lt;/em&gt; библиотеку для синтаксического анализа &lt;a href="http://book.realworldhaskell.org/read/using-parsec.html"&gt;Parsec&lt;/a&gt;. Примеры в книжке впечатлили. В отличие от &lt;a href="https://www.ibm.com/developerworks/ru/edu/au-lexyacc/" title="Создание анализаторов текста при помощи yacc и lex"&gt;традиционного подхода&lt;/a&gt;, при этом нет разделения на лексический анализ (выделение «слов»-лексем) и синтаксический анализ (преобразование потока «слов» в упорядоченную структуру данных) — в  комбинаторном парсинге эти два этапа объединяются. Берутся небольшие функции, распознающие элементы текста, и затем они комбинируются в соответветствии с синтаксисом текста. Таким образом, сама комбинация функций непосредственно отражает грамматику, и она же, естественно, является и программой для разбора текста. Как у всякой удачной идеи, у Parsec есть множество подражаний. Для Python комбинаторных парсеров нашлось &lt;s&gt;целых два&lt;/s&gt; &lt;s&gt;уже три&lt;/s&gt; уже четыре — &lt;a href="http://www.valuedlessons.com/2008/02/pysec-monadic-combinatoric-parsing-in.html"&gt;Pysec&lt;/a&gt;, &lt;a href="http://pyparsing.wikispaces.com/"&gt;Pyparsing&lt;/a&gt;, &lt;a href="http://www.acooke.org/lepl/"&gt;LEPL (для Python 2.6/3.0)&lt;/a&gt; и &lt;a href="http://code.google.com/p/funcparserlib/"&gt;funcparselib&lt;/a&gt;. Я буду говорить о &lt;code&gt;pyparsing&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;В следующей заметке — &lt;a href="http://sovety.blogspot.com/2009/09/yet-another-parser-combinator-library.html" title="Пример использования funcparserlib"&gt;Ещё одна библиотека для комбинаторного парсинга&lt;/a&gt; — смотрите аналогичный пример для библиотечки &lt;code&gt;funcparserlib&lt;/code&gt;.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Итак, перейдём к делу. Предположим нужно читать файлы состоящие из записей следующего вида:&lt;br /&gt;&lt;pre&gt;Inspection&lt;br /&gt;#   2  SHOULD      Ref. Sys    1&lt;br /&gt;X            28.7493 &lt;br /&gt;Y            78.9960 &lt;br /&gt;Z            -1.0014 &lt;/pre&gt;&lt;br /&gt;Всё необходимое импортируем из модуля &lt;code&gt;pyparsing&lt;/code&gt;. При работе поглядываем в &lt;a href="http://crpppc19.epfl.ch/doc/python-pyparsing/htmldoc/frames.html"&gt;документацию к модулю&lt;/a&gt;. Для простоты примера импортируем всё:&lt;br /&gt;&lt;pre class="sh_python"&gt;from pyparsing import *&lt;/pre&gt;&lt;br /&gt;Теперь начинаем описывать грамматику. Например, определим &lt;em&gt;числа&lt;/em&gt; как слова, состоящие из цифр, знака точки и дефиса (минуса)&lt;br /&gt;&lt;pre class="sh_python"&gt;number = Word(nums + ".-")&lt;/pre&gt;&lt;br /&gt;а &lt;em&gt;значения переменных&lt;/em&gt; определим как пару заглавной латинской буквы и числа:&lt;br /&gt;&lt;pre class="sh_python"&gt;var = Regex("[A-Z]") + number&lt;/pre&gt;&lt;br /&gt;Обратим внимание, что плюс между двумя простыми парсерами (регулярное выражение и слово) создаёт новый парсер, который распознаёт уже последовательность выражений. По-умолчанию &lt;code&gt;pyparsing&lt;/code&gt; игнорирует все лишние пробелы и переводы строк между элементами разбираемого текста (обычно именно это и нужно), поэтому указывать в грамматике наличие пробелов между элементами необязательно.&lt;br /&gt;&lt;br /&gt;Уже на этом этапе мы можем попробовать наш парсер переменных. Запускаем интерпретатор и выполняем:&lt;br /&gt;&lt;pre class="sh_python"&gt;&amp;gt;&amp;gt;&amp;gt; var.parseString("X   42.0")&lt;br /&gt;(['X', '42.0'], {})&lt;/pre&gt;&lt;br /&gt;— на выходе получили структуру данных в соответствии с нашей грамматикой (имя переменной и число за ним).&lt;br /&gt;&lt;br /&gt;Допишем всё остальное. Для простоты будем считать комментарием всё после знака «#» до конца строки (комбинатор &lt;code&gt;restOfLine&lt;/code&gt;):&lt;br /&gt;&lt;pre class="sh_python"&gt;comment = "#" + restOfLine&lt;/pre&gt;&lt;br /&gt;Теперь мы можем описать грамматику всей записи в целом.&lt;br /&gt;&lt;pre class="sh_python"&gt;record = Suppress("Inspection" + comment) + OneOrMore(var)&lt;/pre&gt;&lt;br /&gt;Запись опознаём по слову «Inspection» в начале (здесь строковой литерал Python автоматически конвертируется в &lt;code&gt;Literal&lt;/code&gt;-парсер, проверяющий буквальное соответствие слову). Далее, обнаружив начало записи, состоящие из слова «Inspection» и следующий за ней комментарий, мы можем их просто пропустить (комбинатор &lt;code&gt;Suppress&lt;/code&gt;), а вот то, что следует дальше — нам интересно. Мы ожидаем, что дальше могут идти значения для одной или нескольких переменных (применяем комбинатор &lt;code&gt;OneOrMore&lt;/code&gt;).&lt;br /&gt;&lt;br /&gt;Последний штрих. Нужно указать, что в файле таких записей может быть несколько.  Для удобства работы с полученной структурой переменные каждой из записей группируем вместе (комбинатор &lt;code&gt;Group&lt;/code&gt;):&lt;br /&gt;&lt;pre class="sh_python"&gt;datafile = OneOrMore(Group(record))&lt;/pre&gt;&lt;br /&gt;Всё! Синтаксический анализатор для нашего формата данных готов. Использовать можно, например, так:&lt;br /&gt;&lt;pre class="sh_python"&gt;import sys&lt;br /&gt;print datafile.parseString(sys.stdin.read())&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Проверяем:&lt;br /&gt;&lt;pre&gt;$ python example.py &lt;&lt; END&lt;br /&gt;&gt; Inspection&lt;br /&gt;&gt; #   2  SHOULD      Ref. Sys    1&lt;br /&gt;&gt; X            28.7493 &lt;br /&gt;&gt; Y            78.9960 &lt;br /&gt;&gt; Z            -1.0014 &lt;br /&gt;&gt; &lt;br /&gt;&gt; Inspection&lt;br /&gt;&gt; #   3  SHOULD      Ref. Sys    1&lt;br /&gt;&gt; X            54.0394 &lt;br /&gt;&gt; Y            64.3977 &lt;br /&gt;&gt; Z            -0.9950 &lt;br /&gt;&gt; &lt;br /&gt;&gt; END&lt;br /&gt;[['X', '28.7493', 'Y', '78.9960', 'Z', '-1.0014'],&lt;br /&gt; ['X', '54.0394', 'Y', '64.3977', 'Z', '-0.9950']]&lt;/pre&gt;&lt;br /&gt;Получили вполне пригодную к использованию в программе структуру данных. Вся грамматика — на пять строк. В общем-то, поняв идею и поглядывая в &lt;a href="http://crpppc19.epfl.ch/doc/python-pyparsing/htmldoc/frames.html"&gt;справку&lt;/a&gt;, несложно описать и более сложную грамматику.&lt;br /&gt;&lt;br /&gt;Например, чтобы разбирать также и строчку с «#» в моём примере, программку можно изменить так:&lt;br /&gt;&lt;pre class="sh_python"&gt;from pyparsing import *&lt;br /&gt;number   = Word(nums + ".-")&lt;br /&gt;var      = Regex("[A-Z]") + number&lt;br /&gt;desc     = Suppress("#") + Word(nums) + Word(alphas) \&lt;br /&gt;           + Suppress("Ref. Sys") + Word(nums)&lt;br /&gt;record   = Suppress("Inspection") + desc + Group(OneOrMore(Group(var)))&lt;br /&gt;datafile = OneOrMore(Group(record))&lt;/pre&gt;&lt;br /&gt;На выходе этот парсер даст:&lt;br /&gt;&lt;pre&gt;[['2', 'SHOULD', '1', [['X', '28.7493'], ['Y', '78.9960'], ['Z', '-1.0014']]],&lt;br /&gt; ['3', 'SHOULD', '1', [['X', '54.0394'], ['Y', '64.3977'], ['Z', '-0.9950']]]]&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;P.S. Нормального тьюториала по &lt;code&gt;pyparsing&lt;/code&gt; в сети я не нашёл, но автор библиотеки написал и продаёт на O’Reilly учебное электропособие за 10 долларов. Справочная же документация и разные примеры в интернете — вполне толковы.&lt;br /&gt;&lt;br /&gt;См. также &lt;a href="http://sovety.blogspot.com/2009/09/yet-another-parser-combinator-library.html" title="Пример использования funcparserlib"&gt;заметку про funcparserlib&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37628976-2198141703083235856?l=sovety.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/sovety/~4/KGRpcEgSozE" height="1" width="1"/&gt;</description><link>http://sovety.blogspot.com/2009/07/easy-parsing-in-python.html</link><author>noreply@blogger.com (Сергей)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">9</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37628976.post-4112176524324571978</guid><pubDate>Wed, 01 Jul 2009 09:48:00 +0000</pubDate><atom:updated>2009-08-20T13:30:26.078+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">командная строка</category><category domain="http://www.blogger.com/atom/ns#">скрипт</category><category domain="http://www.blogger.com/atom/ns#">фото</category><title>Сортировка фото по дате EXIF</title><description>На днях и я, и virens почти синхронно написали две заметки об EXIF: &lt;a href="http://sovety.blogspot.com/2009/06/fix-exif-date-and-time-in-jpegs.html"&gt;как исправить дату EXIF в фото&lt;/a&gt; и &lt;a href="http://mydebianblog.blogspot.com/2009/06/exif-linux-exif.html"&gt;как добавлять пользовательские теги в raw-файлы&lt;/a&gt;. Раз уж тема начата, то поделюсь ещё одним приёмом.&lt;br /&gt;&lt;br /&gt;Фотографии я храню, группируя каталоги по годам и по датам съёмки (указывая дату в формате ISO), то есть в архиве путь к альбому у меня примерно такой: &lt;code&gt;photos/2009/20090628 - название альбома/&lt;/code&gt;. Очень удобно, потому что обычно один день — одна тема, и даже при алфавитной сортировке каталога альбомы упорядочены хронологически. И такая организация не зависит ни от операционной системы, ни от конкретной программы-каталогизатора.&lt;br /&gt;&lt;br /&gt;Однако если на карточке фотографии разных дней — раскидывать их по альбомам вручную утомительно. Поэтому у меня есть ещё и скрипт-сортировщик для внесения фото в архив. Он смотрит на дату в EXIF, создаёт нужные каталоги и помещает в них фото:&lt;br /&gt;&lt;pre class="sh_sh"&gt;#!/bin/sh&lt;br /&gt;&lt;br /&gt;ARCHIVE=$HOME/photos&lt;br /&gt;&lt;br /&gt;for f in "$@"; do&lt;br /&gt; DT=$(exiftool -s -DateTimeOriginal "$f")&lt;br /&gt; YEAR=$(echo $DT|awk '{print $3;}'|awk -F: '{print $1;}')&lt;br /&gt; ISODAY=$(echo $DT|awk '{print $3;}'|sed 's/://g')&lt;br /&gt; TARGET="$ARCHIVE/$YEAR/$ISODAY"&lt;br /&gt; install -d "$TARGET" &amp;&amp; \&lt;br /&gt; install "$f" "$TARGET"&lt;br /&gt; echo "$f -&gt; $TARGET"&lt;br /&gt;done&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Запускаю из каталога с фотокарточки:&lt;br /&gt;&lt;pre&gt;$ import-photos *&lt;/pre&gt;&lt;br /&gt;Так можно импортировать и JPEG-и, и RAW. И там, и там EXIF обычно есть.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37628976-4112176524324571978?l=sovety.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/sovety/~4/cfHc803EZ-E" height="1" width="1"/&gt;</description><link>http://sovety.blogspot.com/2009/07/more-on-exif-sorting-photos.html</link><author>noreply@blogger.com (Сергей)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">4</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37628976.post-2925491548198552988</guid><pubDate>Wed, 01 Jul 2009 07:27:00 +0000</pubDate><atom:updated>2009-07-01T10:13:10.688+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">firefox</category><category domain="http://www.blogger.com/atom/ns#">настройка</category><title>Как ускорить Firefox на eeePC 901</title><description>На моём eeePC 901 Firefox часто подтормаживал, и это явно совпадало с работой диска. Объяснение нашлось &lt;a href="http://www.roytanck.com/2009/04/05/more-eee-pc-firefox-speed-tweaks/" title="Tune Firefox responsiveness on eeePC"&gt;здесь&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Дело в том, что в eeePC 901 два флэш-диска, маленький на 4 ГБ и большой 16 ГБ, и этот большой флэш-диск — медленный. Домашний же раздел &lt;code&gt;/home&lt;/code&gt; у меня, естественно, на большом, и там же профиль Firefox-а. Браузер же по-умолчанию сохраняет в него текущую сессию каждые 10 секунд и туда же пытается писать кэш. Пишет он синхронно (то есть ждёт, пока не запишется), поэтому и замирает на секунду при каждом чихе.&lt;br /&gt;&lt;br /&gt;Народная медицина в этом случае рекомендует пойти в &lt;a href="about:config"&gt;about:config&lt;/a&gt; и там&lt;br /&gt;&lt;br /&gt;1) создать ключ &lt;code&gt;toolkit.storage.synchronous&lt;/code&gt; с целым значением &lt;code&gt;0&lt;/code&gt; (запись на диск вести асинхронно, то есть не ждать, пока, например, состояние сессии действительно запишется)&lt;br /&gt;&lt;br /&gt;2) создать или изменить ключ &lt;code&gt;browser.cache.disk.parent_directory&lt;/code&gt;, его строковое значение установить в &lt;code&gt;/dev/shm/firefox-username&lt;/code&gt; (сохранять кэш в памяти, а не писать на диск; кэш, конечно, будет утерян при перезагрузке, и памяти потребуется больше, но зато и работать будет быстрее)&lt;br /&gt;&lt;br /&gt;Дополнительно я увеличил промежуток времени между записями состояния сессии (списка открытых вкладок):&lt;br /&gt;&lt;br /&gt;3) в &lt;code&gt;browser.sessionstore.interval&lt;/code&gt; поставил &lt;code&gt;60000&lt;/code&gt; (60 секунд), вместо &lt;code&gt;10000&lt;/code&gt; (10 секунд).&lt;br /&gt;&lt;br /&gt;Я попробовал — так явно лучше (iceweasel 3.0.6). Возможно, с кэшем есть и более красивые решения (сделать внутри профиля символическую ссылку куда-нибудь на быстрый диск, или какими-то другими ключами отключить дисковый кэш, увеличив кэш в памяти...). Можете оставлять в комментариях ссылки и рецепты.&lt;br /&gt;&lt;br /&gt;Советы, полагаю, применимы не только к eeePC, но и к другим нетбукам с SSD (флэш-дисками).&lt;br /&gt;&lt;br /&gt;В предыдущей заметке можно прочитать, &lt;a href="http://sovety.blogspot.com/2008/12/eeepc-901-debian.html" title="eeePC 901 и Debian Linux"&gt;как мы ставили и настраивали Debian на eeePC&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37628976-2925491548198552988?l=sovety.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/sovety/~4/aEJ8Y-A6KzY" height="1" width="1"/&gt;</description><link>http://sovety.blogspot.com/2009/07/faster-firefox-on-eeepc-901.html</link><author>noreply@blogger.com (Сергей)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">4</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37628976.post-1466817933603141247</guid><pubDate>Mon, 29 Jun 2009 16:35:00 +0000</pubDate><atom:updated>2009-06-29T18:49:22.938+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">командная строка</category><category domain="http://www.blogger.com/atom/ns#">фото</category><title>Как исправить дату EXIF в фото</title><description>Иногда, отфотографировав день-два-три можно обнаружить, что всё это время в камере стояла неправильная дата. Я тут как раз ошибся: на один год (вперёд), 12 часов (a.m./p.m, эх) и один часовой пояс.&lt;br /&gt;&lt;br /&gt;Дело поправимое. Поможет &lt;code&gt;exiftool&lt;/code&gt;. Чтобы откатить дату на один год и 12+1 (13) часов, поступаем так:&lt;br /&gt;&lt;pre class="sh_sh"&gt;$ exiftool "-DateTimeOriginal-=1:0:0 13:00:00" *.jpg&lt;/pre&gt;&lt;br /&gt;Смотрим, что получилось, и если что-то не так, то возвращаемся к оригиналам (&lt;code&gt;exiftool&lt;/code&gt; их услужливо сохранит):&lt;br /&gt;&lt;pre class="sh_sh"&gt;$ for f in *_original ; do mv $f ${f%_original} ; done&lt;/pre&gt;&lt;br /&gt;Если же всё в порядке, добавляем фотографии в свой архив и используем по назначению. Например, &lt;a href="http://sovety.blogspot.com/2008/07/blog-post.html"&gt;добавляем геотаги&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37628976-1466817933603141247?l=sovety.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/sovety/~4/f8rXxF7EUUg" height="1" width="1"/&gt;</description><link>http://sovety.blogspot.com/2009/06/fix-exif-date-and-time-in-jpegs.html</link><author>noreply@blogger.com (Сергей)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37628976.post-7272139288446218343</guid><pubDate>Thu, 25 Jun 2009 11:33:00 +0000</pubDate><atom:updated>2009-07-16T16:50:30.529+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">gnome</category><category domain="http://www.blogger.com/atom/ns#">настройка</category><title>Metacity: сочетания клавиш, чтобы прижимать окна к краю</title><description>При всей любви и уважении к &lt;a href="http://ru.wikipedia.org/wiki/%D0%A4%D1%80%D0%B5%D0%B9%D0%BC%D0%BE%D0%B2%D1%8B%D0%B9_%D0%BE%D0%BA%D0%BE%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BC%D0%B5%D0%BD%D0%B5%D0%B4%D0%B6%D0%B5%D1%80_X_Window_System"&gt;мозаичным&lt;/a&gt; оконным менеджерам, я пользуюсь традиционными перекрывающимися окошками. С мозаичными менеджерами на маленьком ноутбучном экране у меня обычно всё складывалось примерно так:&lt;br /&gt;&lt;br /&gt;Первый экран — терминал во всю стену. Второй экран — браузер на 80% ширины, IM на 20% ширины. Остальные экраны — для остальных приложений (отдельный для Gimp, отдельный для полноэкранного плеера, и т.д.).&lt;br /&gt;&lt;br /&gt;Итак, я на практике понял, что в основных рабочих приложениях удобно работать или в полноэкранном режиме, или поместив рядом два приложения. В двух этих случаях, кстати, мозаичность удобна. В остальных случаях, мозаичность иногда даже мешает. Однако для терминала во весь экран никакой мозаичности не нужно. Достаточно нажать F11 в Гноме (аналогичное сочетание клавиши где угодно). Остаётся случай «два окна рядом», одно справа, другое слева. Раз в день разложить так окна руками, конечно, можно.&lt;br /&gt;&lt;br /&gt;Однако удобнее, когда есть клавиатурные сочетания. Во-первых, нужна максимизация по горизонтали и особенно по вертикали. Тогда окно сразу займёт всю ширину (или высоту) экрана, а для другого окна рядом останется место. Во-вторых, очень полезны сочетания, позволяющие подвинуть окно к краю экрана. С парой окон на экране и такими сочетаниями можно наслаждаться мозаичностью в любом оконном менеджере. В Openbox такие сочетания легко настраиваются, а вот в конфигураторе для Metacity (Gnome) — команд, чтобы прижать окно к краю, нет.&lt;br /&gt;&lt;br /&gt;Так я думал и продолжал передвигать окна ручками. Одно влево, другое вправо. А сегодня узнал, что команды такие в Metacity есть. И сочетания клавиш к ним привязать можно в &lt;code&gt;gconf-editor&lt;/code&gt;. Запускаем, идём в &lt;code&gt;/apps/metacity/window_keybinding&lt;/code&gt; и там для команд &lt;code&gt;move_to_side_x&lt;/code&gt; (где &lt;code&gt;x&lt;/code&gt; — n, e, s, w) прописываем сочетания клавиш (строковое значение вида &lt;code&gt;&amp;lt;Shift&amp;gt;&amp;lt;Control&amp;gt;Up&lt;/code&gt;). Всё сразу работает.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://lh5.ggpht.com/_PiUWFeprZSw/SkNmTVmc2QI/AAAAAAAALKg/bKFj-vvIgtg/s512/pseudo-tiling.png" alt="сочетания клавиш для того, чтобы прижимать окна Metacity к краю экрана" /&gt;&lt;br /&gt;&lt;br /&gt;С моим настройками я нажимаю &lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;Shift&lt;/kbd&gt;+&lt;kbd&gt;F11&lt;/kbd&gt;, и окно вытягивается на всю высоту экрана. Нажимаю &lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;Shift&lt;/kbd&gt;+&lt;kbd&gt;⇒&lt;/kbd&gt;, и окно прижимается к правому краю. Другое окно вытягиваю и прижимаю к левому краю. На другом экране я запускаю терминал и нажимаю F11 (полный экран). Всё остальное, что собираюсь держать открытым дольше минуты (читалку PDF, плеер, и т.п.) запускаю на других экранах.&lt;br /&gt;&lt;br /&gt;Примечание: при более внимательно рассмотрении оказалось, что прижимание окон к краю не только недокументированная, но и весьма глючная возможность в Metacity. Вот такой замечательный WM. В минималистичном Openbox, однако, всё ОК.&lt;br /&gt;&lt;br /&gt;Кстати, полноэкранный терминал (без заголовков окон, без полосы прокрутки) — по-моему, ещё и идеальный полноэкранный редактор, то есть такая среда для работы с текстом, в которой &lt;em&gt;ничего не отвлекает&lt;/em&gt;. То, что людям такая среда нужна — доказывает обилие &lt;a href="http://www.techmalaya.com/2009/02/07/full-screen-text-editor-blogger/"&gt;специальных полноэкранных редакторов&lt;/a&gt;. А ничего специального не нужно: достаточно полноэкранного терминала и &lt;code&gt;vi&lt;/code&gt; (или &lt;code&gt;emacs&lt;/code&gt;). Вообще, полноэкранный режим чрезвычайно удобен, не только для терминала, но и для некоторых других приложений.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37628976-7272139288446218343?l=sovety.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/sovety/~4/L7vqwnc68Is" height="1" width="1"/&gt;</description><link>http://sovety.blogspot.com/2009/06/hidden-metacity-keybindings.html</link><author>noreply@blogger.com (Сергей)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh5.ggpht.com/_PiUWFeprZSw/SkNmTVmc2QI/AAAAAAAALKg/bKFj-vvIgtg/s72-c/pseudo-tiling.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">4</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37628976.post-3367756898269793366</guid><pubDate>Thu, 11 Jun 2009 12:58:00 +0000</pubDate><atom:updated>2009-07-01T15:10:45.058+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">железо</category><category domain="http://www.blogger.com/atom/ns#">linux</category><category domain="http://www.blogger.com/atom/ns#">debian</category><title>Debian Lenny на Samsung X22</title><description>Мой новый рабочий ноутбук — Samsung X22. Черновой отчёт об установке и настройке Debian &lt;a href="http://nix-tips.blogspot.com/2009/06/debian-lenny-on-samsung-x22.html" title="Debian Lenny on Samsung X22"&gt;здесь&lt;/a&gt; (по-английски).&lt;br /&gt;&lt;br /&gt;Когда разрешу оставшиеся вопросы, перепишу по-русски и помещу здесь. Пока очень кратко.&lt;br /&gt;&lt;br /&gt;WiFi нормально работает после установки &lt;code&gt;&lt;a href="http://packages.debian.org/lenny/all/firmware-iwlwifi/download"&gt;firmware-iwlwifi&lt;/a&gt;&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Комбинации клавиш &lt;kbd&gt;Fn&lt;/kbd&gt;+... работают не все. Из коробки работают «кнопка сна» и регулировка громкости. Оказалось, не страшно. Простейший &lt;a href="http://pastebin.com/f51dc8175"&gt;патч к hal-info&lt;/a&gt;, починяющий кнопки, разработчикам отправлен.&lt;br /&gt;&lt;br /&gt;Видео-драйвер &lt;code&gt;radeon&lt;/code&gt; работает, но не летает. Версия в Lenny недостаточно свежая и XVideo не поддерживает. Буду разбираться. &lt;code&gt;fglrx&lt;/code&gt; вроде работает. &lt;blockquote&gt;&lt;strong&gt;Дополнение 2009-07-01:&lt;/strong&gt; &lt;code&gt;fglrx&lt;/code&gt; который в Lenny, работает без нареканий, а вот с Xorg из unstable мне завести &lt;code&gt;fglrx&lt;/code&gt; не удалось. Зато в Xorg из unstable драйвер &lt;code&gt;radeon&lt;/code&gt; — уже 6.12.2. А именно в 6.12 появилась поддержка ускорения EXA и XVideo для чипов серии R6xx. Чтобы заработало, нужно также либо ядро 2.6.30 или новее, либо пересобрать модули &lt;code&gt;radeon.ko&lt;/code&gt; и &lt;code&gt;drm.ko&lt;/code&gt; для старых ядер. Как это сделать — написано в &lt;a href="http://www.x.org/wiki/radeon%3Ar6xx_r7xx_branch" title="EXA accelleration and XVideo with open source radeon driver"&gt;X.org вики&lt;/a&gt; (это минутное дело). Да, если ядро Debian — нужно ещё доустановить пакет &lt;code&gt;firmware-linux&lt;/code&gt; (то, что выкинули из дебиановского ядра). И действительно — после этого новый свободный драйвер &lt;code&gt;radeon&lt;/code&gt; работает. Видео высокого разрешения играется, по-моему, даже лучше и ровнее, чем с &lt;code&gt;fglrx&lt;/code&gt;. 3D, правда, пока в свободном драйвере нет, только в &lt;code&gt;fglrx&lt;/code&gt;. В общем, сейчас есть выбор: жить с Xorg из stable и проприетарным &lt;code&gt;fglrx&lt;/code&gt;, или с Xorg из unstable и со свободным &lt;code&gt;radeon&lt;/code&gt; (видео лучше, нет 3D).&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Звук играет и с драйвером из Lenny, но, чтобы заработал микрофон, нужна свежая ALSA и в &lt;code&gt;/etc/modprobe.d/alsa-base&lt;/code&gt; пришлось добавить&lt;br /&gt;&lt;pre&gt;options snd-hda-intel model=ultra&lt;/pre&gt;По нажатию на &lt;kbd&gt;Fn&lt;/kbd&gt;+&lt;kbd&gt;Esc&lt;/kbd&gt; машина и с самого начала засыпала хорошо, но после просыпания экран не включался. Установил, что работают:&lt;br /&gt;&lt;pre&gt;s2ram -f -a 2&lt;/pre&gt;и&lt;br /&gt;&lt;pre&gt;pm-suspend --quirk-s3-mode&lt;/pre&gt;Соответственно в &lt;code&gt;/usr/share/hal/fdi/information/10freedesktop/20-video-quirk-pm-samsung.fdi&lt;/code&gt; переделал так:&lt;br /&gt;&lt;pre&gt;     &amp;lt;!-- this does not work for my SX22S! --&amp;gt;&lt;br /&gt;     &amp;lt;match key="system.hardware.product" string_outof="R40/R41;CoronaR"&amp;gt;&lt;br /&gt;       &amp;lt;merge key="power_management.quirk.vbestate_restore" type="bool"&amp;gt;true&amp;lt;/merge&amp;gt;&lt;br /&gt;     &amp;lt;/match&amp;gt;&lt;br /&gt;     &amp;lt;!-- I use this one: --&amp;gt;&lt;br /&gt;     &amp;lt;match key="system.hardware.product" string="SX22S"&amp;gt;&lt;br /&gt;       &amp;lt;merge key="power_management.quirk.s3_mode" type="bool"&amp;gt;true&amp;lt;/merge&amp;gt;&lt;br /&gt;     &amp;lt;/match&amp;gt;&lt;/pre&gt;После этого и спим хорошо, и просыпаемся.&lt;br /&gt;&lt;br /&gt;Вебкамера, кардридер (xD/SD/MS) работают отлично. Оптический привод, вроде, тоже. Bluetooth пока не пробовал, а вот ExpressCard и HDMI проверять просто не на чем.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37628976-3367756898269793366?l=sovety.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/sovety/~4/fVsGalsvtAM" height="1" width="1"/&gt;</description><link>http://sovety.blogspot.com/2009/06/debian-lenny-samsung-x22.html</link><author>noreply@blogger.com (Сергей)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">7</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37628976.post-2849003577191728736</guid><pubDate>Tue, 26 May 2009 13:57:00 +0000</pubDate><atom:updated>2009-08-20T13:36:46.086+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">русский язык</category><category domain="http://www.blogger.com/atom/ns#">программирование</category><category domain="http://www.blogger.com/atom/ns#">haskell</category><category domain="http://www.blogger.com/atom/ns#">моё</category><category domain="http://www.blogger.com/atom/ns#">скрипт</category><title>Twtrize — сократитель речи</title><description>Как известно, письменность избыточна: мы можем угадывать написанные слова, даже если некоторые буквы неразборчивы, перепутаны местами или вообще отсутствуют. К счастью, в компьютерной письменности все буквы разборчивы, почерк у всех одинаково хорош.  Именно поэтому появилась возможность очень сильно сокращать слова, убирая из них «лишние» буквы.&lt;br /&gt;&lt;br /&gt;Люди иногда сознательно сокращают слова, набирая SMS или твиты — чтобы потратить меньше денег или укоротить сообщение.&lt;br /&gt;&lt;br /&gt;Идея возникла, когда на одном из многочисленных «сократителей URL» я увидел надпись «Shrink text». И мне пришло в голову, что вот он возьмёт, и сократит сам текст: выдаст что-нибудь вроде «shrnk txt». Конечно, сервис всего лишь заменял в тексте URL, но я подумал, что можно было бы сокращать и сам текст.&lt;br /&gt;&lt;br /&gt;Не знаю, как в английском, а в русском, по-моему, можно убрать довольно много гласных букв, а текст будет по-прежнему читаться. Я решил испытать идею, и написал этот сократитель.&lt;br /&gt;&lt;br /&gt;Программа преобразует текст на русском языке, выкидывая из него некоторые буквы и символы. Прошу рассматривать это как забавную игрушку и программой не злоупотреблять.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Зависимости&lt;/h3&gt;&lt;br /&gt;Программа написана на Literate Haskell (это значит, что то, что, вы сейчас читаете, и есть программа!). Используются следующие модули:&lt;br /&gt;&lt;pre&gt;&gt; import System.IO.UTF8 as U&lt;br /&gt;&gt; import Data.Char (toLower)&lt;br /&gt;&gt; import Text.Regex.Posix ((=~))&lt;br /&gt;&gt; import Data.Char (isPunctuation)&lt;/pre&gt;&lt;br /&gt;TODO: Я использую старый способ работать с UTF-8 (utf8-string), надо переделать под новую библиотеку text.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Алгоритм&lt;/h3&gt;&lt;br /&gt;Данная программа «сжимает» русский текст так:&lt;br /&gt;I. Из слов убираются (почти) все гласные и мягкие знаки,&lt;br /&gt;&lt;pre&gt;&gt; filterVowels = filter (`notElem` (aVowels ++ jVowels))&lt;/pre&gt;&lt;br /&gt;Неприкосновенны гласные, которые:&lt;br /&gt;I.a. являютя частью приставки «не-»&lt;br /&gt;&lt;pre&gt;&gt; rmVowels = map wordFilter&lt;br /&gt;&gt;   where&lt;br /&gt;&gt;    wordFilter ('н':'е':cs) = "не" ++ wordFilter cs&lt;/pre&gt;&lt;br /&gt;I.b. стоят в трёх- и менее -буквенных словах&lt;br /&gt;&lt;pre&gt;&gt;    wordFilter w = if length w &lt;= 3&lt;br /&gt;&gt;                    then w&lt;/pre&gt;&lt;br /&gt;I.c. стоят в начале или конце слова&lt;br /&gt;&lt;pre&gt;&gt;                    else&lt;br /&gt;&gt;                      let (prefix,inner,ending) = splitWord w&lt;br /&gt;&gt;                      in prefix ++ (ajaFilter inner) ++ ending&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&gt;    splitWord s  = let p = takeWhile dontRemove s&lt;br /&gt;&gt;                       r = drop (length p) s&lt;br /&gt;&gt;                       e = reverse $ takeWhile dontRemove $ reverse r&lt;br /&gt;&gt;                       m = take ((length r) - (length e)) r&lt;br /&gt;&gt;                       dontRemove c = c `elem` vowels || isPunctuation c&lt;br /&gt;&gt;                    in (p,m,e)&lt;/pre&gt;&lt;br /&gt;I.d. являются комбинациями со звуком «й»: «-ою-», «-ая—» и проч.&lt;br /&gt;&lt;pre&gt;&gt;    ajaFilter [] = []&lt;br /&gt;&gt;    ajaFilter s  = let (b,m,a) = s =~ diftPat :: (String,String,String)&lt;br /&gt;&gt;                       diftPat = "[" ++ vowels ++ "][" ++ jVowels ++ "]"&lt;br /&gt;&gt;                    in (sameConsFilter b) ++ m ++ (ajaFilter a)&lt;/pre&gt;&lt;br /&gt;I.e. стоят меж двух одинаковых согласных&lt;br /&gt;&lt;pre&gt;&gt;    sameConsFilter [] = []&lt;br /&gt;&gt;    sameConsFilter s  =&lt;br /&gt;&gt;      let (b,m,a) = s =~ sameConsPat :: (String,String,String)&lt;br /&gt;&gt;          sameConsPat = "(["++consonants++"])[" ++ vowels ++ "]\\1"&lt;br /&gt;&gt;       in (filterVowels b) ++ m ++ (sameConsFilter a)&lt;/pre&gt;&lt;br /&gt;Программа использует такой список гласных:&lt;br /&gt;&lt;pre&gt;&gt; vowels = aVowels ++ jVowels&lt;/pre&gt;&lt;br /&gt;где есть и простые гласные (к ним же причислен и мягкий знак)&lt;br /&gt;&lt;pre&gt;&gt; aVowels = "аиоуыэь"&lt;/pre&gt;&lt;br /&gt;и дифтонгообразующие (не знаю правильного термина — в общем, дающие звук «й»),&lt;br /&gt;к ним же причислена и буква «й»:&lt;br /&gt;&lt;pre&gt;&gt; jVowels = "яйёюе"&lt;/pre&gt;&lt;br /&gt;Для некоторых правил требуется также список русских согласных:&lt;br /&gt;&lt;pre&gt;&gt; consonants = "бвгджзклмнпрстфхцчшщ"&lt;/pre&gt;&lt;br /&gt;II. из предложений убираются знаки препинания, кроме точек, вопросительных и восклицательных знаков&lt;br /&gt;&lt;pre&gt;&gt; rmSomePunctuation = filter (not . null) . map rmTrailing&lt;br /&gt;&gt;   where rmTrailing      = reverse . rmHead . reverse&lt;br /&gt;&gt;         rmHead []       = []&lt;br /&gt;&gt;         rmHead s@(c:cs) = case c `elem` rmlist of&lt;br /&gt;&gt;             True -&gt; rmHead cs&lt;br /&gt;&gt;             False -&gt; s&lt;/pre&gt;&lt;br /&gt;  Список подлежащих удалению знаков препинания:&lt;br /&gt;&lt;pre&gt;&gt;         rmlist = ",;-—:–"&lt;/pre&gt;&lt;br /&gt;III. из текста удаляются некоторые предлоги (в телеграфном стиле)&lt;br /&gt;&lt;pre&gt;&gt; rmPrepositions = filter (`notElem` preps) . words&lt;br /&gt;&gt;   where preps = [ "в", "во", "на", "над", "к", "от", "из"&lt;br /&gt;&gt;                 , "по", "под", "через" ]&lt;/pre&gt;&lt;br /&gt;IV. для пущей стилизации текст пишется в нижнем регистре&lt;br /&gt;&lt;pre&gt;&gt; tolower = map toLower&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Использование программы&lt;/h3&gt;&lt;br /&gt;Программу можно использовать как простой unix-фильтр: он читает текст из потока stdin и печает «сжатый» текст в стандартный вывод (stdout).&lt;br /&gt;&lt;pre&gt;&gt; main = U.interact $ (++ "\n") . twtrize&lt;br /&gt;&lt;br /&gt;&gt; twtrize = unwords . filter ( not . null ) .&lt;br /&gt;&gt;           rmVowels . rmSomePunctuation . rmPrepositions . tolower&lt;/pre&gt;&lt;br /&gt;Пример:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;    $ &lt;strong&gt;printf "Гласные, а также некоторые предлоги — как, например, «на», — из \&lt;br /&gt;    текста удаляются, но какие-то остаются.\n" | runhaskell twtrize.lhs&lt;/strong&gt;&lt;br /&gt;    глсные а ткже нектрые прдлги как нпрмр «на» ткста удляются но какие-то&lt;br /&gt;    остаются.&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Последняя версия: &lt;a href="http://bitbucket.org/jetxee/twtrize/src/tip/twtrize.lhs"&gt;исходник здесь&lt;/a&gt;. Лицензия: BSD-3.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37628976-2849003577191728736?l=sovety.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/sovety/~4/9Am4mab-e9w" height="1" width="1"/&gt;</description><link>http://sovety.blogspot.com/2009/05/twtrize.html</link><author>noreply@blogger.com (Сергей)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">13</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37628976.post-7713591025848434716</guid><pubDate>Fri, 15 May 2009 12:31:00 +0000</pubDate><atom:updated>2009-06-12T19:12:57.979+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">haskell</category><category domain="http://www.blogger.com/atom/ns#">командная строка</category><category domain="http://www.blogger.com/atom/ns#">ledger</category><title>Ledger — бухучёт в командной строке</title><description>Решил, что надо наводить порядок в своей жизни и деньги считать. В общем, вести домашнюю бухгалтерию. Познания в бухучёте у меня очень скромные (хотя когда-то и прослушал вводный курс), помню только, что такое &lt;a href="http://ru.wikipedia.org/wiki/%D0%94%D0%B2%D0%BE%D0%B9%D0%BD%D0%B0%D1%8F_%D0%B7%D0%B0%D0%BF%D0%B8%D1%81%D1%8C"&gt;двойная запись&lt;/a&gt;, и &lt;a href="http://ru.wikipedia.org/wiki/%D0%94%D0%B5%D0%B1%D0%B5%D1%82"&gt;дебет/кредит&lt;/a&gt;. Когда-то, правда, пользовался &lt;a href="http://eugeniavlasova.com/archives/zhizn_v_linukse/gnucash_programma_ucheta_finansov_.html" title="Введение в GNUcash"&gt;GNUcash&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Кроме GNUcash в линуксе есть ещё несколько программ для учёта личных финансов: KMyMoney и Grisbi. Программы красивые, удобные, наглядные. Однако меня впечалил и заинтересовал &lt;code&gt;ledger&lt;/code&gt;. Это бухучёт в стиле unix. В общем, для фанатов.&lt;br /&gt;&lt;br /&gt;Идея проста: записываем все расходы/доходы в текстовый файл (файл редактируем сами, программа его не трогает), а программа всегда поможет проверить баланс и составить отчёт о текущем состоянии или по периоду. Что может быть естественней, проще, надёжней? Полный простор в организации учёта.&lt;br /&gt;&lt;br /&gt;Отмечу, что вначале я нашёл не сам &lt;a href="http://github.com/jwiegley/ledger/tree" title="ledger: A double-entry accounting system with a command-line reporting interface"&gt;ledger&lt;/a&gt;, а его клон &lt;a href="http://hledger.org/Hledger" title="hledger is a text-mode double-entry accounting tool"&gt;hledger&lt;/a&gt;, написанный на Haskell. Есть ещё и вариант написанный на Python — &lt;a href="http://furius.ca/beancount/" title="BeanCount: Command-line Double-Entry Accounting"&gt;beancount&lt;/a&gt;. Какую программу выбрать — дело вкуса. Формат файла, к счастью, у них (почти) одинаковый. «Старший» &lt;code&gt;ledger&lt;/code&gt; уже есть в репозиториях Ubuntu и Debian, но мне пока больше понравился вариант на Haskell (исходники короче и понятнее), про него и буду рассказывать.&lt;br /&gt;&lt;br /&gt;Хотя сложного в использовании &lt;code&gt;ledger&lt;/code&gt; вроде ничего нет, трудно начать, потому что все примеры в сети на английском и используют английскую бухгалтерскую терминологию. Для меня, чтобы разобраться, было важно понять &lt;a href="http://joyful.com/repos/ledger/doc/ledger/File-format.html#File-format"&gt;формат&lt;/a&gt; в котором вести записи. Итак, файл состоит из записей, формат каждой записи&lt;pre&gt;ДАТА[=ФАКТИЧЕСКАЯ ДАТА] [*|!] [(КОД)] [ОПИСАНИЕ] [ ; КОММЕНТАРИЙ ]&lt;br /&gt;отступ НАЗВАНИЕ СЧЕТА хотя бы два пробела СУММА [ ; КОММЕНТАРИЙ ]&lt;br /&gt;отступ НАЗВАНИЕ ДРУГОГО СЧЕТА [ хотя бы два пробела СУММА ] [ ; КОММЕНТАРИЙ ]&lt;br /&gt;[другие счета, если необходимо]&lt;/pre&gt;Каждая запись начинается с цифры, то есть даты. Даты пишем в формате ISO, ГГГГ-ММ-ДД. Для краткости можем указать в файле&lt;pre&gt;Y2009&lt;/pre&gt;и все последующие записи без указания года будут относится к 2009-му году.&lt;br /&gt;&lt;br /&gt;Описание проводки может быть любым. Номера квитанций, счетов и тому подобные коды можно вписывать в поле (КОД) (указывать перед описанием в скобках).&lt;br /&gt;&lt;br /&gt;СУММЫ можно писать так, как удобно, «13», «$42», «17 EUR», «121 Kb», «9 L». &lt;code&gt;Ledger&lt;/code&gt; понимает, что разные единицы измерения нужно считать отдельно. Единицы можно указывать любые, те, которые нужно учитывать. В том числе и неденежные (и программа умеет их правильно пересчитывать, если ей дать файл с историей цен).&lt;br /&gt;&lt;br /&gt;В каждой записи обычно два или три счета, но указывать сумму для одного из них необязательно, понятно, что изменение будет равно сумме всех других, но с противоположенным знаком:&lt;pre&gt;2009-05-14&lt;br /&gt;    расходы:обед   123 RUB&lt;br /&gt;    актив:наличные ; понятно, что здесь должно быть -123 RUB&lt;/pre&gt;Счета можно называть как угодно. В том числе и по-русски. Можно делать подсчета, используя двоеточие в названии счёта, например, «расходы:услуги:интернет». Видимо, необходимо иметь счета как минимум пяти категорий: актив, долги, доходы, расходы и какой-то счёт для уравнивания балансов. Я назвал его «собственные», в англоязычных примерах его обычно называют «equity».&lt;br /&gt;&lt;br /&gt;Дело в том, что двойная запись подразумевает, что всегда выполняется закон сохранения денег. Всегда, когда к какому-то счёту мы их приписываем, точно такую же сумму мы должны с какого-то счёта списать. Поэтому уже в начале, чтобы указать сумму денег в кошельке и на счету в банке, нужно ввести некий счёт, с которого они «пришли».&lt;br /&gt;&lt;br /&gt;Составим такой файл со счетами, для начала. Пусть у нас есть текущий счёт в банке, на котором лежит 1001 рубль (единицы не будем указывать для краткости), есть что-то в кошельке, скажем 150 рублей. Пишем файл:&lt;br /&gt;&lt;pre&gt;Y2009&lt;br /&gt;&lt;br /&gt;05-14 начальное состояние счета в банке&lt;br /&gt;  актив:банк  1001&lt;br /&gt;  собственные:начало&lt;br /&gt;&lt;br /&gt;05-14 начальное содержимое кошелька&lt;br /&gt;  актив:наличные  150&lt;br /&gt;  собственные:начало&lt;/pre&gt;Сохраняем в файл, например &lt;code&gt;~/.ledger&lt;/code&gt; (там его по-умолчанию ищет &lt;code&gt;hledger&lt;/code&gt;). Смотрим, что получилось:&lt;pre&gt;~$ &lt;strong&gt;hledger&lt;/strong&gt;&lt;br /&gt;                1151  актив&lt;br /&gt;                1001    банк&lt;br /&gt;                 150    наличные&lt;br /&gt;               -1151  собственные:начало&lt;/pre&gt;Программа сама догадалась подсчитать баланс виртуального счёта «актив». Всё сходится: всё, что было списано со счёта «собственные:начало» оказалось в «активе». Если что-то не сходится, программа будет страшно ругаться.&lt;br /&gt;&lt;br /&gt;Как записывать расход-приход — см пример записи выше. Допустим, мы что-то купили и что-то получили в подарок: &lt;pre&gt;05-15 * подарок&lt;br /&gt;  актив:наличные  100&lt;br /&gt;  доходы:подарки&lt;br /&gt;05-15 * яблоко&lt;br /&gt;  расходы:покупки  9&lt;br /&gt;  актив:наличные&lt;/pre&gt;Я добавил звёздочку перед описнием проводок. Как её интерпретировать — дело хозяйское. Я помечаю ей те операции, которые уже завершены (а восклицательным знаком — те, которые запланированы). Программа потом позволяет легко отбирать проводки со звёздочкой и без.&lt;br /&gt;&lt;br /&gt;Предположим, например, что на завтра я запланировал ответный подарок, но ещё его не сделал: &lt;pre&gt;05-16 ! ответный подарок&lt;br /&gt;  расходы:подарки  90&lt;br /&gt;  актив:наличные&lt;/pre&gt;Теперь, если посмотреть баланс с ключиком &lt;code&gt;-C&lt;/code&gt;, увидим только то, что завершено: &lt;pre&gt;$ &lt;strong&gt;hledger -C bal&lt;/strong&gt;&lt;br /&gt;                  91  актив:наличные&lt;br /&gt;                -100  доходы:подарки&lt;br /&gt;                   9  расходы:покупки&lt;/pre&gt;Чтобы посмотреть не баланс, а список проводок, есть команда &lt;code&gt;register&lt;/code&gt;: &lt;pre&gt;~$ &lt;strong&gt;hledger reg&lt;/strong&gt;&lt;br /&gt;2009/05/14 начальное состояни.. актив:банк                     1001         1001&lt;br /&gt;                                собственные:начало            -1001            0&lt;br /&gt;2009/05/14 начальное содержим.. актив:наличные                  150          150&lt;br /&gt;                                собственные:начало             -150            0&lt;br /&gt;2009/05/15 подарок              актив:наличные                  100          100&lt;br /&gt;                                доходы:подарки                 -100            0&lt;br /&gt;2009/05/15 яблоко               расходы:покупки                   9            9&lt;br /&gt;                                актив:наличные                   -9            0&lt;br /&gt;2009/05/16 ! ответный подарок   расходы:подарки                  90           90&lt;br /&gt;                                актив:наличные                  -90            0&lt;/pre&gt;Отмечу, что в «сишной» версии &lt;code&gt;ledger&lt;/code&gt; колонки здесь &lt;a href="http://hpaste.org/fastcgi/hpaste.fcgi/view?id=4900#a4900" rel="nofollow"&gt;разъедутся&lt;/a&gt;. А вот в &lt;code&gt;hledger-0.5&lt;/code&gt; уже всё в порядке (и для 0.4 я тоже сделал &lt;a href="http://sites.google.com/site/sovetyplus/Home/hledger-0.4-utf8.diff?attredirects=0"&gt;патчик&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;В общем, баланс (&lt;code&gt;balance&lt;/code&gt;) и проводки (&lt;code&gt;register&lt;/code&gt;) — два главных отчёта. Можно ограничивать отчёты по диапазону дат или периодам. Или по названиям счетов. Так, чтобы выбрать все счета со словом «подарки» в названии, используем регулярное выражение: &lt;pre&gt;$ &lt;strong&gt;hledger reg .*подарки.*&lt;/strong&gt;&lt;br /&gt;2009/05/15 подарок              доходы:подарки                 -100         -100&lt;br /&gt;2009/05/16 ! ответный подарок   расходы:подарки                  90          -10&lt;/pre&gt;Чтобы выбрать записи по описанию, поступаем так: &lt;pre&gt;$ &lt;strong&gt;hledger reg desc:яблоко&lt;/strong&gt;&lt;br /&gt;2009/05/15 яблоко               расходы:покупки                   9            9&lt;br /&gt;                                актив:наличные                   -9            0&lt;/pre&gt;В общем, идея понятна. Для отчётов по неделям и по месяцам есть ключики &lt;code&gt;-W&lt;/code&gt; и &lt;code&gt;-M&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Бывают ещё периодические записи, но только в &lt;code&gt;ledger&lt;/code&gt;, в &lt;code&gt;hledger&lt;/code&gt; их пока нет. Они не означают, что со счёта что-то автоматически списывается. Проводку нужно всё равно вносить вручную, но периодические записи позволяют рассчитывать прогноз бюджета. В &lt;code&gt;ledger&lt;/code&gt; также можно делать виртуальные записи, ими можно автоматически учитывать разные комиссии или проценты.&lt;br /&gt;&lt;br /&gt;Вот такая программка. Думаю, не одному мне понравится. Вижу большое достоинство программы в том, что формат данных очень простой и естественный. Посмотрю теперь, будет ли её хватать для моих нужд.&lt;br /&gt;&lt;br /&gt;Вот пример &lt;a href="http://sites.google.com/site/sovetyplus/Home/ledger.%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D1%80.txt?attredirects=0"&gt;ledger-файла&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37628976-7713591025848434716?l=sovety.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/sovety/~4/J2AdeFVHieE" height="1" width="1"/&gt;</description><link>http://sovety.blogspot.com/2009/05/personal-accounting-in-command-line.html</link><author>noreply@blogger.com (Сергей)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">21</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37628976.post-5880616341106674867</guid><pubDate>Sat, 09 May 2009 13:54:00 +0000</pubDate><atom:updated>2009-05-09T16:14:53.206+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">vim</category><category domain="http://www.blogger.com/atom/ns#">настройка</category><title>Подсветка табуляций между пробелами и концевых пробелов в Vim</title><description>Продолжаю тему вспомогательной подсветки в Vim. Прошлый раз писал &lt;a href="http://sovety.blogspot.com/2009/05/show-lines-longer-than-80-in-vim.html"&gt;как показать длинные (&gt; 80 символов) строчки в Vim&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Другая зараза для исходного кода — концевые пробелы (trailing whitespace) и табуляции вперемежку с пробелами. Для их подсветки у меня в &lt;code&gt;~/.vimrc&lt;/code&gt; есть вот такие выделения:&lt;pre class="sh_vim"&gt;" highlight trailing spaces&lt;br /&gt;au BufNewFile,BufRead * let b:mtrailingws=matchadd('ErrorMsg', '\s\+$', -1)&lt;br /&gt;" highlight tabs between spaces&lt;br /&gt;au BufNewFile,BufRead * let b:mtabbeforesp=matchadd('ErrorMsg', '\v(\t+)\ze( +)', -1)&lt;br /&gt;au BufNewFile,BufRead * let b:mtabaftersp=matchadd('ErrorMsg', '\v( +)\zs(\t+)', -1)&lt;/pre&gt;Первое включает подсветку любых пробелов на конце строки. Второе и третье подсвечивают табуляции, перед которыми или после которых есть пробелы. Понятно, что вместо &lt;code&gt;*&lt;/code&gt; можно явно прописать типы файлов, для которых это должно работать, но единственное место, где мне это не нужно — это буферы со справкой. Поэтому у меня сделано сейчас так:&lt;pre class="sh_vim"&gt;" disable matches in help buffers&lt;br /&gt;au BufEnter,FileType help call clearmatches()&lt;/pre&gt;Выглядит это так:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://picasaweb.google.com/lh/photo/nzMmdsy2_wOx2romQWolEg?authkey=Gv1sRgCJaI8-Xni6izlAE&amp;feat=embedwebsite"&gt;&lt;img src="http://lh5.ggpht.com/_PiUWFeprZSw/SgWM5qjluBI/AAAAAAAAKuc/ft_fR5UI0fw/s400/vim-highlight-trailing-space-and-tabs.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Подобным же целям служат, но иначе работают, скрипт &lt;a href="http://www.vim.org/scripts/script.php?script_id=443" title="Togglable syntax highlighting of tabs and trailing spaces."&gt;spacehi.vim&lt;/a&gt; и рецепт &lt;a href="http://vim.wikia.com/wiki/Highlight_unwanted_spaces" title="Vim: highlight unwanted space"&gt;#396&lt;/a&gt; (для подсветки пробелов).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37628976-5880616341106674867?l=sovety.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/sovety/~4/QbYHVgl-6OQ" height="1" width="1"/&gt;</description><link>http://sovety.blogspot.com/2009/05/highlight-trailing-whitespace-and-tabs.html</link><author>noreply@blogger.com (Сергей)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh5.ggpht.com/_PiUWFeprZSw/SgWM5qjluBI/AAAAAAAAKuc/ft_fR5UI0fw/s72-c/vim-highlight-trailing-space-and-tabs.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">3</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-37628976.post-4746177849778659720</guid><pubDate>Tue, 05 May 2009 09:11:00 +0000</pubDate><atom:updated>2009-05-11T10:25:40.425+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">программирование</category><category domain="http://www.blogger.com/atom/ns#">vim</category><category domain="http://www.blogger.com/atom/ns#">настройка</category><title>Показать длинные (&gt;80) строчки в Vim</title><description>Большинство программистов согласятся, что строчки кода должны быть короче 80 символов. Часто это просто хороший тон: читаем Linux Kernel Coding Style &lt;a href="http://kerneltrap.org/files/Jeremy/CodingStyle.txt"&gt;(80)&lt;/a&gt; , Style Guide for Python code &lt;a href="http://www.python.org/dev/peps/pep-0008/"&gt;(79)&lt;/a&gt;, Good Haskell Style &lt;a href="http://urchin.earth.li/~ian/style/haskell.html"&gt;(79)&lt;/a&gt;, Ruby Coding Conventions &lt;a href="http://pub.cozmixng.org/~the-rwiki/rw-cgi.rb?cmd=view;name=RubyCodingConvention"&gt;(80)&lt;/a&gt;, Google C++ Style Guide &lt;a href="http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml"&gt;(80)&lt;/a&gt;...&lt;br /&gt;&lt;br /&gt;Практический вопрос: а как в Vim увидеть, что строка стала длиннее 80 символов? Это может быть очень полезно, если ширина окна больше 80. Простой и дубовый способ: &lt;kbd&gt;8&lt;/kbd&gt;&lt;kbd&gt;0&lt;/kbd&gt;&lt;kbd&gt;|&lt;/kbd&gt; и курсор перемещается на 80-ю колонку. Однако каждую строчку так проверять неудобно.&lt;br /&gt;&lt;br /&gt;Более элегантный выход — подсвечивать всё, что за 80-ю колонку вылазит. Сразу куча (похожих) рецептов: &lt;a href="http://vim.wikia.com/wiki/Highlight_long_lines"&gt;Highlight long lines&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Включить подсветку вручную: &lt;pre class="sh_sh"&gt;:match ErrorMsg '\%&gt;80v.\+'&lt;/pre&gt;Чтобы включать подсветку автоматически, каждый раз при открытии буфера, в &lt;code&gt;~/.vimrc&lt;/code&gt; помещаем: &lt;pre class="sh_sh"&gt;:au BufWinEnter * let w:m1=matchadd('Search', '\%&lt;81v.\%&gt;77v', -1)&lt;br /&gt;:au BufWinEnter * let w:m2=matchadd('ErrorMsg', '\%&gt;80v.\+', -1)&lt;/pre&gt;Должно работать в Vim после 7.1.40. При этом последние 4 символа до 80-й колонки будут предупреждающе подсвечиваться «поиском», а все, что после 80-й — «ошибкой».&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;strong&gt;Дополнение:&lt;/strong&gt; в комментариях предложен ещё и другой способ выделить последние 4 символа строки: &lt;pre class="sh_sh"&gt;:au BufWinEnter * let w:m1=matchadd('Search', '\%&gt;76v.*\%&lt;81v', -1)&lt;/pre&gt;Тоже работает.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Получается вот так:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://picasaweb.google.com/lh/photo/1RpiYvMcwhoub-Qd1N3_Jg?authkey=Gv1sRgCJaI8-Xni6izlAE&amp;feat=embedwebsite"&gt;&lt;img src="http://lh6.ggpht.com/_PiUWFeprZSw/SgQ6EiADuNI/AAAAAAAAKuU/G4fyMpfzmt8/s400/vim-highlight-beyond-80.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Про подсветку табуляций вперемежку с пробелами и концевых пробелов см.&amp;nbsp;&lt;a href="http://sovety.blogspot.com/2009/05/highlight-trailing-whitespace-and-tabs.html" title="Подсветка табуляций между пробелов и концевых пробелов в Vim"&gt;следующую заметку&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/37628976-4746177849778659720?l=sovety.blogspot.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/sovety/~4/95D2bhCJkx8" height="1" width="1"/&gt;</description><link>http://sovety.blogspot.com/2009/05/show-lines-longer-than-80-in-vim.html</link><author>noreply@blogger.com (Сергей)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh6.ggpht.com/_PiUWFeprZSw/SgQ6EiADuNI/AAAAAAAAKuU/G4fyMpfzmt8/s72-c/vim-highlight-beyond-80.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">8</thr:total></item></channel></rss>
