Под впечатлением Literate Haskell, я решил сделать что-то подобное для Python, чтобы было проще писать заметки в блоге. Пишу я редко, поэтому это было стимулом повозиться с блогом и написать что-нибудь.
Literate programming, литературное программирование — идея, придуманная и реализованная Дональном Кнутом в 1980-x для C и TEX:
Главная идея — рассматривать программу как средство взаимодействия между людьми, а не набор инструкций для компьютера.
Идею Кнута реализовали и в Haskell. Там поддерживается два стиля разделения кода и текста: добавление к строкам кода > (птичий стиль) или окружение кода парами \begin{code} и \end{code} (стиль LATEX). К тому же для Haskell часто применяют преобразователи кода из ASCII в расширенный математический набор символов, из-за чего код выглядит неприлично красивым :)
Для меня целью было получить хотя бы некоторые преимущества литературного программирования для Python. Я хотел иметь возможность запускать код, который я пишу в заметках для блогов. Раньше приходилось писать его в отдельном исходнике, затем перенося в заметку, либо полагаться на то, что код написан правильно и запускать его не нужно.
Когда я пишу заметки в блог, я размечаю текст при помощи XHTML или (намного чаще) Markdown. В последнем случае при помощи утилиты markdown я перевожу разметку в XHTML. Для автоматизации я использую GNU make: здесь нужен совсем простой makefile. Для себя я придумал довольно простой способ написания текста в Literate Python. Для разделения кода я использовал птичий стиль Haskell. Исходный файл *.lpy автоматически преобразуется при помощи make в два файла: обычный исходник *.py и файл *.xhtml.
Приведу пример. Вначале код Literate Python, файл example.lpy:
<!-- vi:set tw=80 ts=4 filetype=python: -->
Немного кода *Literate Python*, по мотивам [Literate Haskell][lit-hs]. Вначале
определим функцию возведения в квадрат и комбинатор композиции функций:
[lit-hs]: http://www.haskell.org/haskellwiki/Literate_programming
> sqr = lambda x: x * x
> o = lambda f, g: lambda x: f(g(x))
В Haskell функция `o` была бы записана как инфиксный оператор `∘`: `f ∘ g = f
g` (если я не ошибаюсь). В Unicode ∘ — это символ U+2218 RING OPERATOR. Кстати,
в Python 3.0 станут возможными идентификаторы Unicode.
А это простой тест модуля. Вначале получаем функцию `hex_square`, которая
является композицией `hex` и `sqr`:
> if __name__ == '__main__':
> hex_square = o(hex, sqr)
Затем красиво печатаем список квадратов первых 30 числел ≥ 0:
> from pprint import pprint
> pprint(list(hex_square(x) for x in xrange(30)))
На этом пример заканчивается.
Вот файл Makefile, при помощи которого из example.lpy получаются example.py и example.xhtml:
XHTMLS = example.xhtml
PYS = example.py
ALL = $(XHTMLS) $(PYS)
%.xhtml: %.lpy
echo "<div xmlns='http://www.w3.org/1999/xhtml'>`sed 's/^> \(.*\)$$/ \1/' \
$< | markdown`</div>" > $@
%.py: %.lpy
sed -n 's/^> \(.*\)$$/\1/p' $< > $@
all: $(ALL)
clean:
rm -f $(ALL)
А вот результаты. Файл example.py:
sqr = lambda x: x * x
o = lambda f, g: lambda x: f(g(x))
if __name__ == '__main__':
hex_square = o(hex, sqr)
from pprint import pprint
pprint(list(hex_square(x) for x in xrange(30)))
И файл example.xhtml:
<div xmlns='http://www.w3.org/1999/xhtml'>
<!-- vi:set tw=80 ts=4 filetype=python: -->
<p>Немного кода <em>Literate Python</em>, по мотивам <a
href="http://www.haskell.org/haskellwiki/Literate_programming">Literate
Haskell</a>. Вначале определим функцию возведения в квадрат и комбинатор
композиции функций:</p>
<pre><code>sqr = lambda x: x * x
o = lambda f, g: lambda x: f(g(x))
</code></pre>
<p>В Haskell функция <code>o</code> была бы записана как инфиксный оператор
<code>∘</code>: <code>f ∘ g = f g</code> (если я не ошибаюсь). В Unicode ∘ — это
символ U+2218 RING OPERATOR. Кстати, в Python 3.0 станут возможными
идентификаторы Unicode.</p>
<p>А это простой тест модуля. Вначале получаем функцию <code>hex_square</code>,
которая является композицией <code>hex</code> и <code>sqr</code>:</p>
<pre><code>if __name__ == '__main__':
hex_square = o(hex, sqr)
</code></pre>
<p>Затем красиво печатаем список квадратов первых 30 числел ≥ 0:</p>
<pre><code> from pprint import pprint
pprint(list(hex_square(x) for x in xrange(30)))
</code></pre>
<p>На этом пример заканчивается.</p></div>
В общем, всё просто: нужно только быть знакомым с make и sed. Зато теперь можно одновременно писать заметку в блог с кодом программы и проверять как она выполняется.



Вы ошибаетесь, если думаете что “literate programming” - это просто программа с комментариями плюс простая utility чтобы выбрать из неё строчки-код или отформатировать все это как ХТМЛ.
Главная идея Кнута в другом. Файл вообще без комментариев (хотя это непично) может быть a literate program, и наоборот, файл с морем комментариев может не быть a literate program.
Идея же - в письме программы НА ПСЕВДОКОДЕ, который вы выдумываете на ходу и на ЧЕЛОВЕЧЕСКОМ языке.
Операторы этого псевдокода становятся точными МАКРО, которые раскроются и подставятся.
Но и это не все, потому что вторая важная идея - возможность писать в порядке, удобном для мышления и выдумывания, избавившись от порядка, предписанного языком инструкций для машины.
Пример можно посмотреть здесь:
http://community.livejournal.com/ru_perl/249441.html
Да, третье: давно уже существуют общие простые программы, которые обеспечивают literate programming на любом языке.
Можете написать такой скриптик на Питоне (и положить его на sourceforge), я писал свои на пёрле.
Комментарий от anonym_mouse — 2008-05-20 @ 03:03
@anonym_mouse: Спасибо за комментарий и ссылку.
Конечно, оригинальная концепция намного шире, чем просто выдирание комментариев из кода. Я знаком с WEB и FunnelWeb. Конечно, возможность писать код в порядке изложения текста замечательна. И в Haskell это возможно на уровне языка. Но для языков с последовательным выполнением инструкций, таких как Python, такой стиль документации небольшим скриптом, конечно, не сделаешь. Тем не менее при нормальной модульности кода вещи, о которых нужно говорить в документации, уже имеют своё имя, будь то вычисления или данные.
Я же хотел получить практичное, хотя и очень ограниченное средство написания заметок в блоге с кодом. Думаю, две строчки в makefile — достаточно мало, чтобы позволить себе «запрограммировать» такую функциональность :)
Да, я посмотрел в Вебе разные программы: и общие, и для Python, но ничего не приглянулось. Многие из них настаивают на языке форматирования доков, многие не достаточно простые по задумке.
Комментарий от vlasovskikh — 2008-05-20 @ 15:33
Нет ничего проще noweb.
Вы пишете свой html файл, но код помещаете в
<<кусок кода>>=
код
код
@
и пишете дальше
Куски могут быть в произвольном порядке и включать ссылки друг на друга (те же угловые скобки, но БЕЗ знака равно)
Вот и все. Я использую noweb с perl’ом и lisp’ом. Мейкфайл в моем случае также состоит из 2х строчек. Одна ссылается на noweave, вторая на notangle. Для красоты я в noweave вставляю post-processing фильт на перле еще из 3х строчек.
Все-таки какая-то тайна здесь скрыта. Все это так просто - и вместе с тем мало кто понимает принцип - программирования фразами на человеческом языке, которые могут выстраивать произвольно большие иерархии концепций
Комментарий от anonym_mouse2 — 2008-05-20 @ 19:05