<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-2153457809622714873</id><updated>2010-07-17T01:11:45.280-07:00</updated><title type='text'>Vladimir Gubarkov</title><subtitle type='html'>Java, Smalltalk, Python, Ruby, Haskell, Prolog, Javascript and more...</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://www.xonix.info/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2153457809622714873/posts/default'/><link rel='alternate' type='text/html' href='http://www.xonix.info/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>xonix</name><uri>http://www.blogger.com/profile/10521854320903151029</uri><email>noreply@blogger.com</email></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>11</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-2153457809622714873.post-3003236148406992034</id><published>2008-12-20T10:50:00.000-08:00</published><updated>2008-12-20T11:55:45.878-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='declarative'/><category scheme='http://www.blogger.com/atom/ns#' term='refal'/><category scheme='http://www.blogger.com/atom/ns#' term='prolog'/><category scheme='http://www.blogger.com/atom/ns#' term='logic'/><title type='text'>Prolog</title><content type='html'>В описании блога фигурирует слово Prolog но пока ни одного поста, посвященного этому интересному языку я не удосужился создать. Хочу исправить это досадное упущение.&lt;br /&gt;&lt;br /&gt;Если честно, мне лень описывать синтаксис и особенности пролога, кому интересно, без труда найдут достаточное количество материала в интернете, благо язык довольно академичный. Скажу лишь, чем меня он заинтересовал. Дело в том, что пролог, по сути единственный язык, предлагающий качественно другой подход к программированию, чем хорошо известные императивный, ООП (который, по сути, тоже императивный, но нацелен на структурирование и модульность), функциональный. Можно назвать этот подход декларативно-логическим.&lt;br /&gt;Не претендуя на точность терминологии, этот подход можно определить как такой, при котором программа представляет собой описанние теми или иными конструкциями языка программирования &lt;span style="font-style:italic;"&gt;самого условия задачи&lt;/span&gt;. Роль ЯП при этом понять это описание, и сделать из него некоторый вывод, который окажется ни чем иным как правильным решением задачи.&lt;br /&gt;Проиллюстрируем, что под этим подразумевается. Возьмем следующую задачу.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Условие задачи: &lt;/span&gt;Сократ - человек. Все люди смертны. &lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Найти: &lt;/span&gt;Смертен ли Сократ?&lt;br /&gt;&lt;br /&gt;Запишем условие в терминах языка пролога (со знака % начинаются комментарии):&lt;br /&gt;&lt;pre&gt;&lt;code class=no-highlight&gt;&lt;br /&gt;% Сократ - человек&lt;br /&gt;human(sokrat).&lt;br /&gt;% Платон - тоже человек&lt;br /&gt;human(platon).&lt;br /&gt;&lt;br /&gt;% Чтобы некто был смертным, он должен быть человеком&lt;br /&gt;mortal(Someone) :- human(Someone).&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;теперь спросим пролог систему, смертен ли Сократ:&lt;br /&gt;&lt;pre&gt;&lt;code class=no-highlight&gt;&lt;br /&gt;?- mortal(sokrat).&lt;br /&gt;&lt;br /&gt;Yes % да&lt;br /&gt;&lt;br /&gt;?- mortal(stranger).&lt;br /&gt;&lt;br /&gt;No % нет, поскольку пролог система не знает ничего о stranger и потому, не может ответить на вопрос&lt;br /&gt;&lt;br /&gt;% Мы можем спросить какие смертные существа известны системе:&lt;br /&gt;?- mortal(Who).&lt;br /&gt;&lt;br /&gt;Who = platon ;&lt;br /&gt;&lt;br /&gt;Who = sokrat&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Именно исходя из этой идеи программирования неудивительно, что именно пролог стал незаменимым средством в исследованях задач искусственного интеллекта, а именно, задачах поиска с возвратом, символьных вычислений, экспертных систем, анализа естественного языка, и множества других.&lt;br /&gt;&lt;br /&gt;Фактически такая гибкость пролога построена всего лишь на двух концепциях, заложенных в язык - унификация (сопоставление с образцом) и перебор с возвратом.&lt;br /&gt;&lt;br /&gt;Собственно, хочу привести для примера решение на прологе задачи о расстановке на шахматной доске 8 ферзей, так чтоб они не били друг друга, основанное на приведенном в книге Братко И. Программирование на языке ПРОЛОГ для искусственного интеллекта (кстати, очень хорошая книга, рекомендую).&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code class=no-highlight&gt;&lt;br /&gt;% будем искать решение как набор 8 координат вида X/Y каждого из ферзей&lt;br /&gt;% при этом понятно, что поскольку все вертикали так или иначе будут заняты&lt;br /&gt;% задача сводится к отысканию соответствующих Y - координат&lt;br /&gt;getSolution(S):-&lt;br /&gt;        S=[1/_,2/_,3/_,4/_,5/_,6/_,7/_,8/_],&lt;br /&gt;        solution(S).&lt;br /&gt;&lt;br /&gt;% доска 0x0 без ферзей очевидно является решением&lt;br /&gt;solution([]).&lt;br /&gt;&lt;br /&gt;% доска NxN является решением, если является решением её под-доска (N-1)*(N-1), &lt;br /&gt;% а первый ферзь не бьет ферзей на этой под-доске.&lt;br /&gt;solution([X/Y | Others]) :-&lt;br /&gt;        solution(Others),&lt;br /&gt;        member(Y, [1, 2, 3, 4, 5, 6, 7, 8]),&lt;br /&gt;        nokill(X/Y, Others).&lt;br /&gt;                  &lt;br /&gt;% очевидно что ферзь с любыми координатами не бьет ферзей из пустого массива, &lt;br /&gt;% поскольку просто некого бить&lt;br /&gt;nokill(_, []).                &lt;br /&gt;&lt;br /&gt;% ферзь не бьет набор ферзей если он не бьет первого ферзя из набора &lt;br /&gt;% и не бьет остальных ферзей набора                 &lt;br /&gt;nokill(X/Y, [X1/Y1 | Others]) :-&lt;br /&gt;        Y =\= Y1,      % на разных горизонталях     &lt;br /&gt;        Y1-Y =\= X1-X, % на разных диагоналях&lt;br /&gt;        Y1-Y =\= X-X1,&lt;br /&gt;        nokill(X/Y, Others).&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Чтоб получить необходимые координаты ферзей, спросим у пролог системы решения, основанные на представленном описании.&lt;br /&gt;&lt;pre&gt;&lt;code class=no-highlight&gt;&lt;br /&gt;1 ?- getSolution(P).&lt;br /&gt;&lt;br /&gt;P = [1/4, 2/2, 3/7, 4/3, 5/6, 6/8, 7/5, 8/1] ;&lt;br /&gt;&lt;br /&gt;P = [1/5, 2/2, 3/4, 4/7, 5/3, 6/8, 7/6, 8/1] ;&lt;br /&gt;&lt;br /&gt;P = [1/3, 2/5, 3/2, 4/8, 5/6, 6/4, 7/7, 8/1] ;&lt;br /&gt;&lt;br /&gt;P = [1/3, 2/6, 3/4, 4/2, 5/8, 6/5, 7/7, 8/1] ;&lt;br /&gt;&lt;br /&gt;P = [1/5, 2/7, 3/1, 4/3, 5/8, 6/6, 7/4, 8/2] ;&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Кстати, а как это будет выглядеть на вашем любимом языке программирования? =)&lt;br /&gt;&lt;br /&gt;Кстати, &lt;a href="http://zlo.rt.mipt.ru/?read=5017359"&gt;тут можно посозерцать&lt;/a&gt; решение той же задачи на &lt;a href="http://www.refal.com/"&gt;рефале&lt;/a&gt;, еще одном интересном языке функционального программирования, идейно перекликающимся с прологом, кстати, создатель которого - наш соотечественник &lt;a href="http://www.refal.com/author.html"&gt;Валентин Федорович Турчин&lt;/a&gt; (&lt;a href="http://ru.wikipedia.org/wiki/%D0%A2%D1%83%D1%80%D1%87%D0%B8%D0%BD,_%D0%92%D0%B0%D0%BB%D0%B5%D0%BD%D1%82%D0%B8%D0%BD_%D0%A4%D1%91%D0%B4%D0%BE%D1%80%D0%BE%D0%B2%D0%B8%D1%87"&gt;вики&lt;/a&gt;).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2153457809622714873-3003236148406992034?l=www.xonix.info' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.xonix.info/feeds/3003236148406992034/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2153457809622714873&amp;postID=3003236148406992034' title='Комментарии: 49'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2153457809622714873/posts/default/3003236148406992034'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2153457809622714873/posts/default/3003236148406992034'/><link rel='alternate' type='text/html' href='http://www.xonix.info/2008/12/prolog.html' title='Prolog'/><author><name>xonix</name><uri>http://www.blogger.com/profile/10521854320903151029</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='10674690484909106230'/></author><thr:total>49</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2153457809622714873.post-5579474281179462204</id><published>2008-12-02T10:18:00.000-08:00</published><updated>2008-12-02T10:23:25.135-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='one-liner'/><category scheme='http://www.blogger.com/atom/ns#' term='питон'/><category scheme='http://www.blogger.com/atom/ns#' term='functional'/><category scheme='http://www.blogger.com/atom/ns#' term='list comprehensions'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Функциональный питон</title><content type='html'>На досуге пришла идея используя &lt;a href="http://www.python.org/dev/peps/pep-0202/"&gt;list comprehensions&lt;/a&gt; изобразить на питоне некое подобие функционального программирования.&lt;br /&gt;&lt;br /&gt;И вот что получилось:&lt;br /&gt;&lt;pre&gt;&lt;code class=python&gt;&lt;br /&gt;[ [[[[[&lt;br /&gt;  (&lt;br /&gt;    (&lt;br /&gt;      p('Multiplications:'),&lt;br /&gt;      doMult(4, 10),&lt;br /&gt;      l()&lt;br /&gt;    ),&lt;br /&gt;    (&lt;br /&gt;      p(),&lt;br /&gt;      p('Fib:'),&lt;br /&gt;      [&lt;br /&gt;        (&lt;br /&gt;          p('%d-&gt;%d' % (i, fib(i)))&lt;br /&gt;        )&lt;br /&gt;        for i in range(1, 10)&lt;br /&gt;      ]&lt;br /&gt;    ),&lt;br /&gt;    (&lt;br /&gt;      p(),&lt;br /&gt;      p('Qsort:'),&lt;br /&gt;      p(qsort([2,1,4,-11,9]))&lt;br /&gt;    )&lt;br /&gt;  )&lt;br /&gt;  &lt;br /&gt;  for doMult in [lambda I, J:&lt;br /&gt;    [&lt;br /&gt;      (&lt;br /&gt;        l() if j==1 else [],&lt;br /&gt;        p('%sx%s=%s' % (i, j, i * j))&lt;br /&gt;      )&lt;br /&gt;      for i in range(1, I)&lt;br /&gt;      for j in range(1, J)]&lt;br /&gt;  ]]  &lt;br /&gt;  &lt;br /&gt;  for qsort in [lambda L:&lt;br /&gt;      [] if L==[]&lt;br /&gt;        else [&lt;br /&gt;          qsort([e for e in T if e&lt;=H]) + [H] + qsort([e for e in T if e&gt;H])&lt;br /&gt;            for H in [L[0]]&lt;br /&gt;            for T in [L[1:]]&lt;br /&gt;          ][-1]&lt;br /&gt;  ]]&lt;br /&gt;  &lt;br /&gt;  for fib in [lambda n:&lt;br /&gt;    n if n&lt;2 else fib(n-1) + fib(n-2)&lt;br /&gt;  ]]&lt;br /&gt;  &lt;br /&gt;  for l in [lambda:&lt;br /&gt;    p('-' * 10)    &lt;br /&gt;  ]]  &lt;br /&gt;  &lt;br /&gt;  for p in [lambda s='':&lt;br /&gt;    w(str(s)+'\n')&lt;br /&gt;  ]]&lt;br /&gt;  &lt;br /&gt;  for w in [&lt;br /&gt;    __import__('sys').stdout.write&lt;br /&gt;  ]&lt;br /&gt;]&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Вывод:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code class=no-highlight&gt;Multiplications:&lt;br /&gt;----------&lt;br /&gt;1x1=1&lt;br /&gt;1x2=2&lt;br /&gt;1x3=3&lt;br /&gt;1x4=4&lt;br /&gt;1x5=5&lt;br /&gt;1x6=6&lt;br /&gt;1x7=7&lt;br /&gt;1x8=8&lt;br /&gt;1x9=9&lt;br /&gt;----------&lt;br /&gt;2x1=2&lt;br /&gt;2x2=4&lt;br /&gt;2x3=6&lt;br /&gt;2x4=8&lt;br /&gt;2x5=10&lt;br /&gt;2x6=12&lt;br /&gt;2x7=14&lt;br /&gt;2x8=16&lt;br /&gt;2x9=18&lt;br /&gt;----------&lt;br /&gt;3x1=3&lt;br /&gt;3x2=6&lt;br /&gt;3x3=9&lt;br /&gt;3x4=12&lt;br /&gt;3x5=15&lt;br /&gt;3x6=18&lt;br /&gt;3x7=21&lt;br /&gt;3x8=24&lt;br /&gt;3x9=27&lt;br /&gt;----------&lt;br /&gt;&lt;br /&gt;Fib:&lt;br /&gt;1-&gt;1&lt;br /&gt;2-&gt;1&lt;br /&gt;3-&gt;2&lt;br /&gt;4-&gt;3&lt;br /&gt;5-&gt;5&lt;br /&gt;6-&gt;8&lt;br /&gt;7-&gt;13&lt;br /&gt;8-&gt;21&lt;br /&gt;9-&gt;34&lt;br /&gt;&lt;br /&gt;Qsort:&lt;br /&gt;[-11, 1, 2, 4, 9]&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Смысл кода в том, что он является одной языковой конструкцией. В питоне это вообще-то затруднено, поскольку все управляющие конструкции не являются выражениями (не возвращают значений, как в Ruby), но, как видим, можно и так извратиться =)&lt;br /&gt;Сразу замечу, практического смысла в этом нет, и, более того, такой код является плохой практикой, но с точки зрения экспериментов, по-моему, занятно.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2153457809622714873-5579474281179462204?l=www.xonix.info' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.xonix.info/feeds/5579474281179462204/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2153457809622714873&amp;postID=5579474281179462204' title='Комментарии: 1'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2153457809622714873/posts/default/5579474281179462204'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2153457809622714873/posts/default/5579474281179462204'/><link rel='alternate' type='text/html' href='http://www.xonix.info/2008/12/blog-post.html' title='Функциональный питон'/><author><name>xonix</name><uri>http://www.blogger.com/profile/10521854320903151029</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='10674690484909106230'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2153457809622714873.post-1858026374556645958</id><published>2007-09-15T16:27:00.000-07:00</published><updated>2008-12-02T10:18:32.701-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Perl'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Почему Python не идеален</title><content type='html'>Мне, будучи Java-разработчиком (а теперь уже и .Net) грех не провести хоть какое-то сравнение Java'ы и питона (попутно коснувшись и других языков). Да, знаю, что таких сравнений пруд пруди на просторах интернета. И тем не менее... Тут я хочу выйти немного в другую плоскость, отчасти философскую. Я не претендую на объективность, более того, вероятно, мои мысли покажутся вам довольно спорными.&lt;br /&gt;Питон - язык с идеологией, есть особая культура написания программ на нем. О программе, написанной "правильно", по канонам питона говорят, что она pythonic, соответствует python way. Для непосвященных скажу, что есть своего рода Дзен Питона - сводка негласных правил правильного кодирования на питоне. Чтоб доказать, что питон - язык с идеологией, разработчики даже встроили эти правила в качестве стандартного модуля языка, вот пример работы:&lt;br /&gt;&lt;pre&gt;&lt;code class="no-highlight"&gt;&lt;br /&gt;&gt;&gt;&gt; import this&lt;br /&gt;The Zen of Python, by Tim Peters&lt;br /&gt;&lt;br /&gt;Beautiful is better than ugly.&lt;br /&gt;Explicit is better than implicit.&lt;br /&gt;Simple is better than complex.&lt;br /&gt;Complex is better than complicated.&lt;br /&gt;Flat is better than nested.&lt;br /&gt;Sparse is better than dense.&lt;br /&gt;Readability counts.&lt;br /&gt;Special cases aren't special enough to break the rules.&lt;br /&gt;Although practicality beats purity.&lt;br /&gt;Errors should never pass silently.&lt;br /&gt;Unless explicitly silenced.&lt;br /&gt;In the face of ambiguity, refuse the temptation to guess.&lt;br /&gt;There should be one-- and preferably only one --obvious way to do it.&lt;br /&gt;Although that way may not be obvious at first unless you're Dutch.&lt;br /&gt;Now is better than never.&lt;br /&gt;Although never is often better than *right* now.&lt;br /&gt;If the implementation is hard to explain, it's a bad idea.&lt;br /&gt;If the implementation is easy to explain, it may be a good idea.&lt;br /&gt;Namespaces are one honking great idea -- let's do more of those!&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Впрочем, я слукавил, правила эти вполне даже гласные, и они создают ту самую самобытность языка, за которую некоторые и изучают новые языки. Правила эти по сути были заложены в язык его создателем и главным идеологом, Гвидо ван Россумом. Он недаром получил статус &lt;a href="http://en.wikipedia.org/wiki/Benevolent_Dictator_for_Life"&gt;BDFL &lt;/a&gt;(Benevolent dictator for life, Великодушный Пожизненный Диктатор).&lt;br /&gt;Но позвольте высказать одну крамольную мысль: Java лучше соответствует этим правилам, нежели сам Питон. И вот почему.&lt;br /&gt;Возьмем, например пресловутое&lt;br /&gt;"There should be one-- and preferably only one --obvious way to do it."&lt;br /&gt;но многим известно что питон - очень гибкий язык и на практике путей довольно таки много (не так мого, правда, как в перле =). Вот хотя бы почитайте &lt;a href="http://www.python.org/doc/essays/list2str.html"&gt;рассуждения&lt;/a&gt; самого Россума об изысканиях при оптимизации.&lt;br /&gt;Язык я бы оценивал по отношению двух параметров:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Средствам программирования, реализованным в языке (предоставляемые языком) (ООП, функциональный стиль, исключения, замыкания, продолжения, система модулей) &lt;/li&gt;&lt;li&gt;Языковые конструкции, синтаксический сахар, которыми достигается функциональность пункта 1.&lt;/li&gt;&lt;/ol&gt;И я склонен первое оценивать со знаком плюс, второе со знаком минус. Вторые плохи, т.к. они вносят неоднородность, неконсистентность в язык. И позволительны, только когда действительно наглядны и явно упрощают написание кода (в эту категорию я бы внес list comprehensions). Так вот, надо отдать должное Гвидо, питон довольно хорошо вписывается в эти критерии. Он обеспечивает, в целом, удовлетворительное ООП (почему так - ниже), систему модулей, исключения, замыкания, при этом синтаксис вполне однороден (за что собственно питон называют "языком с человеческим лицом", и по некоторым версиям это самый читабельный язык). Более того, Гвидо идет далее и в версии 3.0 языка убирает синтаксические конструкции (print, exec становятся функциями, % заменяется на метод .format)&lt;br /&gt;А, например, перл совершенно не укладывается в эти рамки, у него достаточно посредственное ООП, исключения, при этом язык &lt;a href="http://www.ozonehouse.com/mark/blog/code/PeriodicTable.html" title="Периодическая таблица операторов перл"&gt;перенасыщен&lt;/a&gt; синтаксическими конструкциями настолько что позволяет писать даже &lt;a href="http://99-bottles-of-beer.net/language-perl-737.html" title="99 bottles of beer program"&gt;такой&lt;/a&gt;, &lt;a href="http://www.linux.org.ru/view-message.jsp?msgid=392747" title="rm -rf perl variant"&gt;такой&lt;/a&gt; и &lt;a href="http://en.wikipedia.org/wiki/Just_another_Perl_hacker" title="Just another perl hacker"&gt;такой&lt;/a&gt; код, ну скажите, о какой поддерживаемости кода тут может идти речь). Конечно мне возразят, что существуют стандарты кодирования на перл, в фирмах используются свои стандарты и т.п. Но в &lt;a href="http://live.julik.nl/2005/12/composition-normalization-and-morons" title="Кстати, интересная, хоть и резкая статья про проблемы в поддержке юникода"&gt;одном месте&lt;/a&gt; я слышал мысль что если что-то включено по умолчанию, то, хотя и присутствует очень простая опция сделать по-другому, но 90% пользователей (программистов) будут делать именно как по умолчанию. Применительно к перлу, слышал мысль, что большинство кода на CPAN написано хоть и не так как в вышеприведенных ссылках, но мягко говоря не по стандартам. Все из-за той-самой никому не нужной гибкости. Но ведь это гибкость из пункта 2, которая идет с минусом! Напротив, Java тем хороша, что она очень однородна по части синтаксиса. Вершиной идеала тут выступает, конечно смоллтолк. О минималистичности синтаксиса говорит тот факт, что в языке всего 6 ключевых слов и напрочь отсутствуют управляющие конструкции (&lt;b&gt;if&lt;/b&gt;, &lt;span style="font-weight:bold;"&gt;while&lt;/span&gt;, &lt;span style="font-weight:bold;"&gt;for&lt;/span&gt;). Вместо этого там присутствуют методы &lt;span style="font-weight:bold;"&gt;ifTrue:&lt;/span&gt;, &lt;span style="font-weight:bold;"&gt;whileTrue:&lt;/span&gt;, &lt;span style="font-weight:bold;"&gt;each:&lt;/span&gt;, которые в сочетании с блоками дают такую функциональность, которой джава с питоном и руби только завидуют и слюнки пускают (продолжения, особая модель "откатываемых" исключений, чудесные возможности интроспекции кода и чудовищная гибкость). Впрочем, признаюсь, не знаю, куда отнести лисп, он вроде еще более однородный, довольно мощный, но при этом как по мне, у него плохо с читаемостью кода.&lt;br /&gt;Питону, же, в отличие от того же руби который взял многое от смоллтолка, не хватает гибкости первого рода. Например, мне, в динамическом языке хотелось бы видеть&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Расширяемые базовые классы (Как в Руби, Смоллтолк, javascript(!!!))&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Любое выражение возвращает значение (руби) типа &lt;br /&gt;&lt;pre&gt;&lt;code class="ruby"&gt;&lt;br /&gt;a = if 2&gt;5 then "more" else "less" end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Хотелось бы видеть продолжения, легковесные нити, но это уже больше касается не синтаксического дизайна языка, а его внутреннего устройства, его виртуальной машины.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;По пункту 2. Тут мне возразят, дескать, в питоне 2.5 таки наконец ввели конструкцию условного присваивания.&lt;br /&gt;&lt;pre&gt;&lt;code class="python"&gt;&lt;br /&gt;a = "more" if 2&gt;5 else "less"&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Вот именно! В язык ввели сущность вида 2 из-за недостаточной гибкости по пункту 1. Вот в этом и проблема. В частности, здесь и видна обратная сторона медали синтаксиса основанного на отступах (а не там где склонны видеть некоторые). Тут же и проблема немногострочной ламбды, для нее просто не смогли придумать подходящего синтаксиса!&lt;br /&gt;&lt;br /&gt; Ruby кажется (вернее мог бы показаться, если б я только выбирал, какой динамический язык учить) более интересным, чем питон по нескольким причинам: удовлетворяет пунктам 1) и 2), менее консервативен чем Python, а потому более инновационен, более целостный (нет различий между базовыми типами и классами, которые , впрочем в Питоне 2.5 наконец тоже сведены на нет, нет функций - только методы). Единственное, мне не нравится его синтаксис, и кроме того, в нем нет чего-то концептуально нового по сравнению со смоллтолком, а продолжения, и вообще в 1.9 хотят убрать.&lt;br /&gt;&lt;br /&gt;Java vs Python. Опять же трудно (и немножко глупо) сравнивать, это довольно разные языки хотя и для довольно общих целей. Взять хотя-бы пресловутый вопрос типизации. Что дает отстутствие статической типизации питону? - гибкость, легкость переделки программы "на коленке". Что дает ее присутствие Java'е - как это не парадоксально, опять же гибкость (правда, за счет довольно развитых IDE, типа Intellij Idea, способности которых - рефакторинг, генерация кода, навигация, удобства отладки, возможны исключительно благодаря типизируемой природе языка). Второе - скорость выполнения программ - очевидно, что статически-типизированный код легче оптимизировать для выполнения. И третье - конечно, легкость поддержки и совместной разработки кода, поскольку целостность и непротиворечивость кода отслеживается на уровне самого языка (той же типизацией, модификаторами доступа, интерфейсами), еще можно упомянуть четвертое - надежность - в Java приложении программист обязан обработать все (!) исключения, иначе программа попросту не скомпилируется (точнее не все, а все не RuntimeException). В питоне обычно процесс разработки может представлять следующую картину - написал код, запустил, получил исключение, обработал его в коде, снова запустил, и т.д.&lt;br /&gt;Java - как это не странно - академический язык. Он очень хорошо подходит и для обучения, поскольку прекрасно реализует концепции программирования. Мне кажется, он "очень правильный". "It just feels right". Его правильность заключается в логичности, стройности, полноте, консервативности, очень полной  и правильной поддержке ООП. На джаву прекрасно ложатся паттерны программирования.&lt;br /&gt;Да, на счет ООП. В питоне, я бы охарактеризовал его двумя словами: оно есть. Чего в нем нет: модификаторов доступа (то что возможно с помощью префиксования имен двойным подчеркивание - не серьезно, и не соотвествует Дзену Питона). Тем самым нарушается один из принципов ООП - инкапсуляция. Там отсутствуют интерфейсы, абстрактные классы, хотя, понимаю, что это фича типизируемых языков, однако они выступают очень полезными  при построении архитектуры проекта.&lt;br /&gt; Коснемся модульной структуры. Java имеет очень мощную модульную структуру. Стандартно, библиотеки распространяются в виде jar-архивов. Питоновские eggs (яйца) пока не так распространены. Даже в Руби быстро смекнули что модули это хорошо и gems там "из коробки". Если спуститься на уровень ниже, то у джавы прекрасные возможности по организации и проектирования кода в пакеты, классы, внутренние классы, внутренние статические, локальные классы, final классы, абстрактные классы и интерфейсы. &lt;br /&gt; Единственное, что сложнее с Java'ой - это конечно, у нее выше порог вхождения. Чтоб начать комфортно программировать, нужно как минимум установить адекватную среду разработки (разработка "в блокноте" с джавой - пропащее и бездарное дело), создать проект, настроить библиотеки... собственно и все. Плюс, по мере необходимости, конечно, придется ориентироваться в целом ряде сопутствующих J-технологий и фреймворков (J2(S|M|E)E, JSP, EJB, JMX, JNDI, JAAS, Ant, JUnit).&lt;br /&gt; На самом деле все приведенные аргумены, довольно субъективны. Конкретно мне Джава польше соответствует по духу, она позволяет все "разложить по полочкам". В ней удобнее проектировать. Думаю все сказанное в той или иной мере подходит и к C#.&lt;br /&gt;Впрочем, в повседневной работе я использую Python тоже. Он прекасно дополняет джаву. Распарсить логи, сгенерировать тестовые данные или sql-скрипт для тестового заполнения базы, админские скрипты для работы с файлами на нем - одно удовольствие. Мне кажется, что комбинация статический + динамический язык (не важно, что вы изберете в качестве первого и второго) довольно обоснована и полезна.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2153457809622714873-1858026374556645958?l=www.xonix.info' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.xonix.info/feeds/1858026374556645958/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2153457809622714873&amp;postID=1858026374556645958' title='Комментарии: 49'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2153457809622714873/posts/default/1858026374556645958'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2153457809622714873/posts/default/1858026374556645958'/><link rel='alternate' type='text/html' href='http://www.xonix.info/2007/09/python.html' title='Почему Python не идеален'/><author><name>xonix</name><uri>http://www.blogger.com/profile/10521854320903151029</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='10674690484909106230'/></author><thr:total>49</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2153457809622714873.post-1020293823325821750</id><published>2007-08-23T07:40:00.000-07:00</published><updated>2007-08-27T02:17:24.105-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Stackless Python'/><category scheme='http://www.blogger.com/atom/ns#' term='Erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='continuations'/><category scheme='http://www.blogger.com/atom/ns#' term='threading'/><title type='text'>Немного о Continuations (продолжениях) и легковесных нитях</title><content type='html'>&lt;span style="font-style: italic;"&gt;Продолжения&lt;/span&gt; - очень интересная концепция в программировании, объяснять подробно лень, т.к. она, надо сказать, не очень-то проста, и всех тонкостей я и сам не знаю, поэтому и за точность изложения не поручусь (Мопед не мой, я лишь разместил объяву=) ).&lt;br /&gt; &lt;br /&gt; Опишу лишь в двух словах. Продолжения позволяют довольно хитро манипулировать потоком выполнения программы. Представьте себе обычную императивную программу. Исполнение программы - это, по сути, выполнение команды за командой "сверху - вниз". Использование функций, объектов, методов ничего не меняет, оно лишь структурирует программу, но, по сути, это то же линейное выполнение. Так вот, продолжения позволяют провернуть примерно следующее:  программа выполняется до некоторой точки (назовем ее A), в этой точке программа как бы "застывает" (для полноценных продолжений это означает что запоминается весь контекст выполнения (переменные, объекты, видимые в этой точке)), и выполнение программы переносится в другую ее точку (B). Далее она продолжает линейно выполняться до некоторой третьей точки (С) после чего, программа возвращается в первоначальную точку A (возможно, принося некоторое возвратное значение из точки C), в точке A восстанавливается сохраненный контекст исполнения, и программа продолжается (отсюда и "продолжение") дальше. Следует отметить что продолжение позволяет многократно возвращаться в точку A, восстанавливать переменные и "продолжаться". Мало того, по слухам, продолжение можно даже сериализовать. Уфф, вроде основные идеи изложил. За более точными определениями и формулировками &lt;a href="http://en.wikipedia.org/wiki/Continuation"&gt;проследуйте&lt;/a&gt; &lt;a href="http://ru.wikipedia.org/wiki/Continuation"&gt;в Википедию&lt;/a&gt; или в &lt;a href="http://www.google.ru/search?hl=ru&amp;q=continuations&amp;amp;btnG=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA&amp;amp;lr="&gt;Гугл&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Чтобы вы оценили всю значимость этих самых продолжений, скажу некоторые умные слова, услышанные где-то на просторах интернета в блогах довольно авторитетных людей =). А именно, продолжения - настолько мощная концепция, что язык, поддерживающий продолжения, может не иметь отдельно вообще никаких других средств программирования (исключений, операторов перехода, условий, блоков кода). Все эти средства с легкостью реализуемы посредством продолжений. Что собственно и демонстрирует широко известный в узких кругах "самый ООП язык" Смоллтолк, возможности которого я восторженно описывал в &lt;a href="http://xonixx.blogspot.com/2007/05/smalltalk.html"&gt;одном из постов&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Следует отметить, что подобная функциональность достигается довольно хитроумными манипуляциями с си-стеком в реализации того или иного языка программирования. В Java'е - динамическим изменением байт-кода.&lt;br /&gt;&lt;br /&gt;Ну настало самое время привести несколько линков. Уже упомянутый ранее веб-фреймворк &lt;a href="http://seaside.st/"&gt;Seaside&lt;/a&gt;, написанный на Smalltalk'е во всю мощь использует силу продолжений. Удачное использование продолжений позволило развить стройную и мощную компонентную модель.&lt;br /&gt;&lt;br /&gt;В Java'е тоже имеется full-stack веб-фреймворк RIFE основанный на продолжениях, (продолжения входят в него отдельной библиотекой, т.ч., думается, ничто не мешает использовать их и вне этого фреймворка). Реализация библиотеки основана на манипуляциях с Java байткодом, которые впрочем запрятаны, и о них не стоит беспокоится. Впрочем есть и другие java-реализации продолжений, включая и &lt;a href="http://commons.apache.org/sandbox/javaflow/"&gt;от jakarta&lt;/a&gt;. Очень рекомендую к просмотру &lt;a href="http://uwyn.com/resources/flow_with_continuations_tssjs_2006.mov"&gt;ролик&lt;/a&gt; (1.9Mb) с презентацией, наглядно показывающий смысл продолжений.&lt;br /&gt;&lt;br /&gt;В Python'е что-то близкое к продолжениям предоставляет реализация &lt;a href="http://www.stackless.com/"&gt;Stackless Python&lt;/a&gt;. Однако в целях практичности продолжения, присутствующие там ранее были заменены облегченной концепцией partial continuations (если не путаю=)). Фактически, можно сказать, что основными примитивами там являются тасклеты и каналы, которые в совокупности позволяют реализовать очень интересные вещи: сопрограммы, легковесные нити (в чем-то аналогичные Erlang'овским). Легковесные нити очень быстры (по сравнению с нитями операционной системы, на одной нити операционной системы может присутствовать очень много легковесных нитей), правда в них требуется явное переключение контекста либо через шедулинг, либо посредством каналов. Кто-то даже провел &lt;a href="http://rsdn.ru/forum/message/2607318.1.aspx"&gt;сравнительное тестирование&lt;/a&gt; такой "легкой многопоточности" и получил довольно интригующий результат - Питон в некоторых случаях уделывает Эрланг на его же поле. Впрочем, наверное, не даром &lt;a href="http://www.gamedev.ru/code/forum/?id=67013"&gt;именно  stackless python реализация&lt;/a&gt; многопоточности была выбрана для движка известной RPG многопользовательской онлайн-игры &lt;a href="http://www.eve-zone.ru/"&gt;EVE Online&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Есть однако и некоторые сложности с продолжениями. Во-первых, они сложны (извините за тавтологию). Во-вторых, их использование может быть довольно ресурсоемким (запоминать контекст), именно поэтому используются облегченные варианты реализации типа partial continuations с выборочным сохранением контекста. А в веб-фреймворках присутствует еще одно ощутимое неудобство, являющееся прямым следствием реализации - не user-friendly ссылки, например, в Seaside ссылки представляются примерно в таком виде: &lt;span style="font-style:italic;"&gt;http://seaside.st/documentation/faq?14&amp;_k=UHOHBZlm&amp;_n&amp;_s=AkYosBcncRoLPbed&lt;/span&gt;. Тут присутствуют некие некрасивые параметры _k и _s. Один из них отвечает за состояние текущего продолжения, а другой - за ваше текущее местоположение на сайте (могу путать).&lt;br /&gt;Впрочем, положительный момент - все линки генерируются автоматически (!!!) на основе построенной вами связи между компонентами.&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;UPD 1.&lt;/span&gt; На сайте ibm.com читайте статью Брюса Тэйта "&lt;a href="http://www.ibm.com/developerworks/ru/library/j-cb03216/index.html?ca=drs-ru"&gt;Пересекая границы: Continuation, Web-разработка и Java-программирование&lt;/a&gt;". В статье описываются принципы работы веб-фреймворков основаных на продолжениях.&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;UPD 2.&lt;/span&gt; Дополнение по питону. Автор Stackless Python, Кристиан Тисмер сейчас принимает участие в реализации очень интересного проекта &lt;a href="http://codespeak.net/pypy/dist/pypy/doc/news.html"&gt;PyPy&lt;/a&gt;. Это по своей задумке довольно амбициозный проект, суть которой заключается в написании интерпретатора питона на нем самом же, а так же написании компилятора питона (на нем самом же, таки) в другие низкоуровневые языки, включая в бинарный исполнимый код. Такой подход не взят с потолка, как мы помним он используется и в случае Squeak Smalltalk'а. По мнению разработчиков, благодаря гибкости питона, это позволит легко реализовывать разные сложные но полезные концепции (включая те же продолжения, легковесные нити, и т.д.), и, что самое главное, разработчики утверждают, что благодаря реализуемому &lt;a href="http://en.wikipedia.org/wiki/Just-in-time_compilation"&gt;JIT-компилятору&lt;/a&gt;, полученный питон будет быстрее его стандартной C-реализации!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2153457809622714873-1020293823325821750?l=www.xonix.info' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.xonix.info/feeds/1020293823325821750/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2153457809622714873&amp;postID=1020293823325821750' title='Комментарии: 4'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2153457809622714873/posts/default/1020293823325821750'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2153457809622714873/posts/default/1020293823325821750'/><link rel='alternate' type='text/html' href='http://www.xonix.info/2007/08/continuations.html' title='Немного о Continuations (продолжениях) и легковесных нитях'/><author><name>xonix</name><uri>http://www.blogger.com/profile/10521854320903151029</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='10674690484909106230'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2153457809622714873.post-1569938753579615861</id><published>2007-07-12T04:14:00.000-07:00</published><updated>2007-08-27T02:20:57.392-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='firefox'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Полезный скрипт для извлечения видео из кэша Firefox (под Win)</title><content type='html'>Часто поднимается вопрос, как скачать видео с &lt;a href="http://youtube.com"&gt;youtube.com&lt;/a&gt; и других подобных сайтов, есть даже сайты, типа &lt;a href="http://youtubex.com/"&gt;youtubex.com&lt;/a&gt; специально предназначенные для скачивания локально .flv - файлов, есть плагины для Firefox.&lt;br /&gt;Я использую другой метод, правда, он может помочь Вам только в случае если вы используете Firefox, как и я :-)&lt;br /&gt; Представляю вашему вниманию скрипт (python) который ползет в кэш браузера Mozilla Firefox и достает оттуда уже просмотренные вами видео-файлы, на данный момент он распознает форматы FLV (именно в этом формате хранится большинство видео на youtube) и SWF (флэш-файлы). Извлеченные файлы копируются в специальную директорию, и к ним присоединяется нужное расширение.&lt;br /&gt;Собственно, сам скрипт: &lt;span class="fullpost0"&gt;&lt;br /&gt;&lt;pre&gt;&lt;code class="python"&gt;&lt;br /&gt;&lt;br /&gt;# file: getflv.py&lt;br /&gt;import os&lt;br /&gt;import os.path&lt;br /&gt;import shutil&lt;br /&gt;import time&lt;br /&gt;&lt;br /&gt;REG_DIR = 'REG'&lt;br /&gt;DATE_FORMAT = "%Y_%m_%d, %H-%M-%S"&lt;br /&gt;&lt;br /&gt;OUTDIRS = {&lt;br /&gt; 'FLV': 'flv',&lt;br /&gt; 'SWF': 'swf',&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;def createDirIfNeeded(fullPath):&lt;br /&gt; if not os.path.exists(fullPath):&lt;br /&gt;  os.makedirs(fullPath)&lt;br /&gt; &lt;br /&gt;def createDirsIfNeeded():&lt;br /&gt; for dir in OUTDIRS.itervalues():&lt;br /&gt;  createDirIfNeeded(dir)&lt;br /&gt;&lt;br /&gt; createDirIfNeeded(REG_DIR)&lt;br /&gt;&lt;br /&gt;def fileSize(fPath):&lt;br /&gt; return os.stat(fPath).st_size&lt;br /&gt;&lt;br /&gt;def getFileType(path):&lt;br /&gt; s = open(path).read(30)&lt;br /&gt; &lt;br /&gt; if s[:3] == 'FLV':&lt;br /&gt;  return 'FLV'&lt;br /&gt; elif s[:3] == 'FWS':&lt;br /&gt;  return 'SWF'&lt;br /&gt;&lt;br /&gt; return None&lt;br /&gt; &lt;br /&gt;def isInReg(f, sourcePath):&lt;br /&gt; # name and size matches&lt;br /&gt; return (f in os.listdir(REG_DIR) and open(REG_DIR + '/' + f).read() == str(fileSize(sourcePath)))&lt;br /&gt;&lt;br /&gt;def formDirName():&lt;br /&gt; return time.strftime(DATE_FORMAT)&lt;br /&gt;&lt;br /&gt;def doCopy(fromPath, outDirName, fName, fileType):&lt;br /&gt; print 'Copying %s video: %s of size %s' % (fileType, fName, fileSize(fromPath))&lt;br /&gt; &lt;br /&gt; ext = fileType.lower()&lt;br /&gt; &lt;br /&gt; createDirIfNeeded(OUTDIRS[fileType] + '/' + outDirName)&lt;br /&gt; &lt;br /&gt; shutil.copyfile(fromPath, OUTDIRS[fileType] + '/' + outDirName + '/' + fName + '.' + ext)&lt;br /&gt; &lt;br /&gt; # add to reg&lt;br /&gt; open(REG_DIR + '/' + fName, 'w').write(str(fileSize(fromPath)))&lt;br /&gt;&lt;br /&gt;def getFirefoxCacheDir():&lt;br /&gt; profileDir = os.environ['USERPROFILE']&lt;br /&gt; firefoxProfile = profileDir + '/Local Settings/Application Data/Mozilla/Firefox/Profiles'&lt;br /&gt;&lt;br /&gt; files = os.listdir(firefoxProfile)&lt;br /&gt; assert len(files) == 1&lt;br /&gt; &lt;br /&gt; hashDir = firefoxProfile + '/' + files[0]&lt;br /&gt; assert os.path.isdir(hashDir)&lt;br /&gt;&lt;br /&gt; return hashDir + '/Cache' &lt;br /&gt;&lt;br /&gt;def main():&lt;br /&gt; createDirsIfNeeded()&lt;br /&gt; outDirName = formDirName()&lt;br /&gt; cacheDir = getFirefoxCacheDir()&lt;br /&gt; files = os.listdir(cacheDir)&lt;br /&gt;&lt;br /&gt; for f in files:&lt;br /&gt;  path = cacheDir + '/' + f&lt;br /&gt;  &lt;br /&gt;  fileType = getFileType(path)&lt;br /&gt;  &lt;br /&gt;  if fileType in ('FLV', 'SWF'):&lt;br /&gt;   if not isInReg(f, path): # path to determine size&lt;br /&gt;    # copying&lt;br /&gt;    doCopy(path, outDirName, f, fileType)&lt;br /&gt;    &lt;br /&gt;if __name__ == '__main__':&lt;br /&gt; main()&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Немного поясню принцип работы. Конфигурировать скрипт никак не требуется. После запуска (в первый раз) будет созданы директории с именами swf, flv и REG в директории со скриптом. Первые две будут, разумеется, использоваться для наполнения извлеченными видео-файлами, а директория REG имеет служебное назначение. В ней хранится список выкачанных файлов и их размеров, чтоб при следующем запуске скрипта они повторно не выкачивались. При каждом запуске в случае обнаружения в кэше новых видео-файлов в соответствующей директории (flv, swf) будет создана новая директория с именем составленным из текущего таймстемпа, в нее будут скопированны файлы данного типа. &lt;br /&gt;Директория кэша браузера находится исходя из настройки переменной окружения %USERPROFILE% (разумеется, речь идет о Windows). Файлы в кэше браузера распознаются по сигнатурам начала файла (flv-файл начинается на 3 байта 'FLV', а flash-файл на 'FWS'). Собственно по сигнатурам можно распознавать и другие файлы, было бы хорошо сделать mov, wmv, avi, mp3 но там сигнатуры посложнее. Если кто сделает - обязательно дайте знать, буду оч. благодарен! )&lt;br /&gt;Да, и совет сделать кэш браузера слегка побольше (100 - 150 мб).&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2153457809622714873-1569938753579615861?l=www.xonix.info' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.xonix.info/feeds/1569938753579615861/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2153457809622714873&amp;postID=1569938753579615861' title='Комментарии: 5'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2153457809622714873/posts/default/1569938753579615861'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2153457809622714873/posts/default/1569938753579615861'/><link rel='alternate' type='text/html' href='http://www.xonix.info/2007/07/firefox.html' title='Полезный скрипт для извлечения видео из кэша Firefox (под Win)'/><author><name>xonix</name><uri>http://www.blogger.com/profile/10521854320903151029</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='10674690484909106230'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2153457809622714873.post-3594604964518715996</id><published>2007-06-22T10:00:00.000-07:00</published><updated>2007-08-27T02:21:10.193-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='алгоритмы'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Алгоритмы Маркова</title><content type='html'>Давно, еще классе в 10, когда у меня не было компьютера, а была лишь книжка по кибернетике, я там прочитал про алгоритмы Маркова (там они, по-моему, назывались "нормальные алгорифмы Маркова"). Это что-то сродни Машине Тьюринга в том смысле, что применялось в основном для анализа таких проблем как алгоритмическая разрешимось задач, практического смысла в ней, пожалуй, не очень много.&lt;br /&gt;&lt;br /&gt;Но концепция довольно проста. Я даже тогда на бумаге написал несколько простых "алгоритмов".&lt;br /&gt;Суть этих самых алгоритмов Маркова в следующем. &lt;span class="fullpost0"&gt;Задача для алгоритмов Маркова ставится в виде: найти алгоритм (написать программу) переводящую любую строку S (заданную на некотором алфавите (т.е. наборе символов, которые могут в нее входить)) из некоторого допустимого множества входных строк в строку f(S). Т.е., построить программу - преобразователь строк, выполняющую некое преобразование.&lt;br /&gt;Например: перевести все буквы в строке в верхний регистр, инвертировать регистр, перевернуть строку задом-наперед (reverse), и даже такую задачу: из строкового представления десятичного числа получить строковое представление числа на единицу больше (или в 2 раза больше).&lt;br /&gt;&lt;br /&gt; Программа на языке алгоритмов Маркова - представляет из себя набор правил (Rules). Каждое правило представляет собой замену. Т.е. правило имете вид&lt;br /&gt;&lt;pre&gt;&lt;code class="no-highlight"&gt;&lt;br /&gt;S1 -&gt; S2&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;где S1 и S2 некие строки. Правило представляет подстановки, последовательно применяемые ко входной строке и приводящие в итоге ее к требуемой выходной строке. Порядок задания правил важен. Работает алгоритм этот следующим образом. Берется исходная строка и мы начинаем переберать правила с самого первого, анализируя, может ли оно быть применено (существует ли в строке S подстрока S1). Если не может -&gt; анализируется следующее по порядку правило. Если не одно правило не подошло, алгоритм завершается, текущее состояние строки S является результатом работы алгоритма. Если же правило применимо - совершается замена самого левого вхождения подстроки S1 на строку S2 (или, выражаясь языком питона, S = S.replace(S1, S2, 1)). Причем, (что очень важно!) далее правила начинают перебираться опять с начала.&lt;br /&gt;&lt;br /&gt;Еще есть так называемые &lt;i&gt;терминальные&lt;/i&gt; правила, обозначающиеся точкой в конце:&lt;br /&gt;&lt;pre&gt;&lt;code class="no-highlight"&gt;&lt;br /&gt;S1 -&gt; S2.&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;При срабатывании такого правила алгоритм завершается и текущее состояние строки S считается результатом работы алгоритма.&lt;br /&gt;&lt;br /&gt;Кто не понял еще, как это работает могут обратиться за помощью &lt;a href="http://ru.wikipedia.org/wiki/%D0%9D%D0%BE%D1%80%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B5_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC%D1%8B_%D0%9C%D0%B0%D1%80%D0%BA%D0%BE%D0%B2%D0%B0"&gt;к Википедии&lt;/a&gt;, а нам, я понимаю уже не терпится начать программировать на алгоритмах Маркова =).&lt;br /&gt;&lt;br /&gt;Сразу оговорюсь, что я буду использовать вместо стрелки "-&gt;" знак "&gt;".&lt;br /&gt;&lt;br /&gt;Итак, решим задачу: задана строка из 0 и 1. Получить на выходе строку в которой 1-цы заменены 0-ми, а 0-и - единицами.&lt;br /&gt;&lt;br /&gt;Нет ничего проще (";" в начале строки - это комментарий):&lt;br /&gt;&lt;pre&gt;&lt;code class="no-highlight"&gt;&lt;br /&gt;; "*"-работник движется вдоль строки, и выполняет "работу" меняет 0 -&gt; 1, 1 -&gt; 0&lt;br /&gt;*0 &gt; 1*&lt;br /&gt;*1 &gt; 0*&lt;br /&gt;&lt;br /&gt;; уничтожение "*"-работника, алгоритм завершается.&lt;br /&gt;* &gt; .&lt;br /&gt;&lt;br /&gt;; ставим в самом начале звездочку-"работника" (замена пустой подстроки на *)&lt;br /&gt;&gt; *&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt; Проследим, как это работает. Возьмем входную строку "1101". Пробуем последовательно применить подстановки. Понятно, что первые 3 не подйдут, поскольку в строке нет "*". Но подойдет самая последняя подстановка и звездочка будет добавлена. Получим "*1101". Затем, поседовательно применяя подстановки номер 1) и 2) будем получать: 0*101, 00*01, 001*1, 0010*. Сейчас применима только 3)-я подстановка, она же и завершающая. Звездочка в конце удаляется, получаем результат: 0010&lt;br /&gt;&lt;br /&gt; Рассмотрим задачу посложнее: дано строковое представление числа в десятичной системе счисления, получить десятичное представление числа на 1-цу больше.&lt;br /&gt;&lt;br /&gt;Рассмотрим решение.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code class="no-highlight"&gt;&lt;br /&gt;; incrementing decimal&lt;br /&gt;; data:&lt;br /&gt;&lt;br /&gt;9999&lt;br /&gt;&lt;br /&gt;; algorithm:&lt;br /&gt;&lt;br /&gt;0#&gt;1.&lt;br /&gt;1#&gt;2.&lt;br /&gt;2#&gt;3.&lt;br /&gt;3#&gt;4.&lt;br /&gt;4#&gt;5.&lt;br /&gt;5#&gt;6.&lt;br /&gt;6#&gt;7.&lt;br /&gt;7#&gt;8.&lt;br /&gt;8#&gt;9.&lt;br /&gt;&lt;br /&gt;9#&gt;#0&lt;br /&gt;&lt;br /&gt;*#&gt;1.&lt;br /&gt;&lt;br /&gt;*0&gt;0*&lt;br /&gt;*1&gt;1*&lt;br /&gt;*2&gt;2*&lt;br /&gt;*3&gt;3*&lt;br /&gt;*4&gt;4*&lt;br /&gt;*5&gt;5*&lt;br /&gt;*6&gt;6*&lt;br /&gt;*7&gt;7*&lt;br /&gt;*8&gt;8*&lt;br /&gt;*9&gt;9*&lt;br /&gt;&lt;br /&gt;*&gt;#&lt;br /&gt;&lt;br /&gt;&gt;*&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;На первый взгляд может показаться немного сложно и непонятно, но на самом деле все более чем просто. Рассмотрим, как работает этот алгоритм. Сперва срабатывает последяя подстановка, добавляющая "*" в начало числа. Применяя подстановки *N &gt; N* она передвигается к концу числа. Когда звездочка дойдет до конца числа, она будет заменена на "#". Собственно, это пока было сделано ровно для того, чтоб получить ту самую # в конце числа. Теперь интересно. Решетка (#) будет выполнять роль 1-цы, которую мы прибавляем к числу. В самом деле, посмотрим на первые 9 подстановок (N# &gt; {N+1}., N=0..8). Если последняя цифра числа от 0 до 8, то она увеличивается на 1 и алгоритм успешно завершаетя. Интереснее, когда в конце числа 9-ка, в этом случае срабатывает 9# &gt; #0. Фактически это как будто перенесение 1-цы в следующий разряд. Теперь благодаря опять-же первым 9 подстановкам на 1 пытается быть увеличенной предпоследняя цифра. Если она меньше 9 опять же алгоритм завершается.&lt;br /&gt;&lt;br /&gt; Положим, что все цифры в числе - девятки. Тогда # заменяя их нулями дойдет до самого начала числа. После этого произойдет интересный финт ушами. Снова сработает последняя подстановка и добавит в начало *. После этого терминальная подстановка *# &gt; 1. завершит алгоритм.&lt;br /&gt;&lt;br /&gt;Вот лог работы при входном числе 9999:&lt;br /&gt;&lt;pre&gt;&lt;code class="no-highlight"&gt;&lt;br /&gt;   Applying rule 22 &gt;*&lt;br /&gt;9999 -&gt; *9999&lt;br /&gt;&lt;br /&gt;   Applying rule 20 *9&gt;9*&lt;br /&gt;*9999 -&gt; 9*999&lt;br /&gt;&lt;br /&gt;   Applying rule 20 *9&gt;9*&lt;br /&gt;9*999 -&gt; 99*99&lt;br /&gt;&lt;br /&gt;   Applying rule 20 *9&gt;9*&lt;br /&gt;99*99 -&gt; 999*9&lt;br /&gt;&lt;br /&gt;   Applying rule 20 *9&gt;9*&lt;br /&gt;999*9 -&gt; 9999*&lt;br /&gt;&lt;br /&gt;   Applying rule 21 *&gt;#&lt;br /&gt;9999* -&gt; 9999#&lt;br /&gt;&lt;br /&gt;   Applying rule 9 9#&gt;#0&lt;br /&gt;9999# -&gt; 999#0&lt;br /&gt;&lt;br /&gt;   Applying rule 9 9#&gt;#0&lt;br /&gt;999#0 -&gt; 99#00&lt;br /&gt;&lt;br /&gt;   Applying rule 9 9#&gt;#0&lt;br /&gt;99#00 -&gt; 9#000&lt;br /&gt;&lt;br /&gt;   Applying rule 9 9#&gt;#0&lt;br /&gt;9#000 -&gt; #0000&lt;br /&gt;&lt;br /&gt;   Applying rule 22 &gt;*&lt;br /&gt;#0000 -&gt; *#0000&lt;br /&gt;&lt;br /&gt;   Applying rule 10 *#&gt;1.&lt;br /&gt;*#0000 -&gt; 10000&lt;br /&gt;&lt;br /&gt;  Terminating rule!&lt;br /&gt;Result : 10000&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt; Вот еще интересный случай. Умножение числа на 2. Тут уже объяснять не буду как работает, просто приведу программу и пример работы.&lt;br /&gt;&lt;pre&gt;&lt;code class="no-highlight"&gt;&lt;br /&gt;; doubling decimal&lt;br /&gt;&lt;br /&gt;948&lt;br /&gt;&lt;br /&gt;; algorithm:&lt;br /&gt;&lt;br /&gt;0[0]&gt;[0]0&lt;br /&gt;0[1]&gt;[0]1&lt;br /&gt;&lt;br /&gt;1[0]&gt;[0]2&lt;br /&gt;1[1]&gt;[0]3&lt;br /&gt;&lt;br /&gt;2[0]&gt;[0]4&lt;br /&gt;2[1]&gt;[0]5&lt;br /&gt;&lt;br /&gt;3[0]&gt;[0]6&lt;br /&gt;3[1]&gt;[0]7&lt;br /&gt;&lt;br /&gt;4[0]&gt;[0]8&lt;br /&gt;4[1]&gt;[0]9&lt;br /&gt;&lt;br /&gt;5[0]&gt;[1]0&lt;br /&gt;5[1]&gt;[1]1&lt;br /&gt;&lt;br /&gt;6[0]&gt;[1]2&lt;br /&gt;6[1]&gt;[1]3&lt;br /&gt;&lt;br /&gt;7[0]&gt;[1]4&lt;br /&gt;7[1]&gt;[1]5&lt;br /&gt;&lt;br /&gt;8[0]&gt;[1]6&lt;br /&gt;8[1]&gt;[1]7&lt;br /&gt;&lt;br /&gt;9[0]&gt;[1]8&lt;br /&gt;9[1]&gt;[1]9&lt;br /&gt;&lt;br /&gt;[0]&gt;.&lt;br /&gt;[1]&gt;1.&lt;br /&gt;&lt;br /&gt;*0&gt;0*&lt;br /&gt;*1&gt;1*&lt;br /&gt;*2&gt;2*&lt;br /&gt;*3&gt;3*&lt;br /&gt;*4&gt;4*&lt;br /&gt;*5&gt;5*&lt;br /&gt;*6&gt;6*&lt;br /&gt;*7&gt;7*&lt;br /&gt;*8&gt;8*&lt;br /&gt;*9&gt;9*&lt;br /&gt;&lt;br /&gt;*&gt;[0]&lt;br /&gt;&lt;br /&gt;&gt;*&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Вывод, по которому все должно стать понятно:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code class="no-highlight"&gt;&lt;br /&gt;   Applying rule 33 &gt;*&lt;br /&gt;948 -&gt; *948&lt;br /&gt;&lt;br /&gt;   Applying rule 31 *9&gt;9*&lt;br /&gt;*948 -&gt; 9*48&lt;br /&gt;&lt;br /&gt;   Applying rule 26 *4&gt;4*&lt;br /&gt;9*48 -&gt; 94*8&lt;br /&gt;&lt;br /&gt;   Applying rule 30 *8&gt;8*&lt;br /&gt;94*8 -&gt; 948*&lt;br /&gt;&lt;br /&gt;   Applying rule 32 *&gt;[0]&lt;br /&gt;948* -&gt; 948[0]&lt;br /&gt;&lt;br /&gt;   Applying rule 16 8[0]&gt;[1]6&lt;br /&gt;948[0] -&gt; 94[1]6&lt;br /&gt;&lt;br /&gt;   Applying rule 9 4[1]&gt;[0]9&lt;br /&gt;94[1]6 -&gt; 9[0]96&lt;br /&gt;&lt;br /&gt;   Applying rule 18 9[0]&gt;[1]8&lt;br /&gt;9[0]96 -&gt; [1]896&lt;br /&gt;&lt;br /&gt;   Applying rule 21 [1]&gt;1.&lt;br /&gt;[1]896 -&gt; 1896&lt;br /&gt;&lt;br /&gt;  Terminating rule!&lt;br /&gt;Result : 1896&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Самое интересное, что реализация такой машины Маркова - сущие пустяки. Вот скрипт, который я написал пару лет назад:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code class="python"&gt;&lt;br /&gt;# file: markov.py&lt;br /&gt;COMMENT_SYMBOL = ';'&lt;br /&gt;DELIM_SYMBOL = '&gt;'&lt;br /&gt;&lt;br /&gt;import sys&lt;br /&gt;if len(sys.argv) &gt; 1:&lt;br /&gt; FILE_NAME = sys.argv[1]&lt;br /&gt;else:&lt;br /&gt; FILE_NAME = 'alg.txt'&lt;br /&gt;&lt;br /&gt;arr = [s for s in open(FILE_NAME, 'r').read().split('\n') \&lt;br /&gt; if s.lstrip() and s.lstrip()[0] != COMMENT_SYMBOL ]&lt;br /&gt;&lt;br /&gt;DATA_STRING = arr[0]&lt;br /&gt;&lt;br /&gt;ALG = arr[1:]&lt;br /&gt;&lt;br /&gt;ALG_PAIRS = [ tuple(s.split(DELIM_SYMBOL)) for s in ALG ]&lt;br /&gt;&lt;br /&gt;# algorithm:&lt;br /&gt;_s = DATA_STRING&lt;br /&gt;&lt;br /&gt;class Exit:&lt;br /&gt; pass&lt;br /&gt;&lt;br /&gt;while True:&lt;br /&gt; try:&lt;br /&gt;  for i in range( len( ALG_PAIRS ) ):&lt;br /&gt;   _rule_applied = False&lt;br /&gt;   &lt;br /&gt;   pair = ALG_PAIRS[i]&lt;br /&gt;   &lt;br /&gt;   if pair[0] in _s:&lt;br /&gt;    _rule_applied = True&lt;br /&gt;    &lt;br /&gt;    _repl = pair[1]&lt;br /&gt;    _term = False&lt;br /&gt;    &lt;br /&gt;    if _repl[-1]=='.': # this is terminating rule&lt;br /&gt;     _term = True&lt;br /&gt;     _repl = _repl[0:-1]&lt;br /&gt;    &lt;br /&gt;    print '    Applying rule', i, ALG[i]&lt;br /&gt;    print _s,'-&gt;',&lt;br /&gt;    &lt;br /&gt;    _s = _s.replace(pair[0], _repl, 1)&lt;br /&gt;    &lt;br /&gt;    print _s+'\n'&lt;br /&gt;    &lt;br /&gt;    if _term:&lt;br /&gt;     print '   Terminating rule!'&lt;br /&gt;     raise Exit()&lt;br /&gt;     &lt;br /&gt;    break&lt;br /&gt;    &lt;br /&gt;  if not _rule_applied:&lt;br /&gt;   print '    No rules matched!'&lt;br /&gt;   break # no one rules matched&lt;br /&gt;   &lt;br /&gt; except Exit:&lt;br /&gt;  break&lt;br /&gt;  &lt;br /&gt;print 'Result : '+_s&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Тут охота отвлечься и сказать пару философских вещей. Данный "язык" (если можно это так назвать =)) является, думается мне, по сути своей декларативным. Порядок выполнения подстановок недетерминированный и зависит от входных данных. Похожие идеи могут быть найдены в языках &lt;a href="http://en.wikipedia.org/wiki/Prolog" title="Prolog on Wikipedia"&gt;Prolog&lt;/a&gt; и &lt;a href="http://www.refal.net/" title="Recursive functions algorithmic language"&gt;Рефал&lt;/a&gt;. Например, тут тоже присутствует так называемое "сопоставление с образцом" в виде существования подстроки.&lt;br /&gt;&lt;br /&gt;Однако, продолжим. Не так давно мне захотелось усовершенствовать немного интерпретатор алгоритмов Маркова, в следующих направлениях:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Ввести псевдосимволы начала "^" и конца "$" строки, знакомые тем кто разбирается в регулярных выражениях. В первом, пожалуй нет особой необходимости, но вот последний позволит таким правилом&lt;br /&gt;&lt;pre&gt;&lt;code class="no-highlight"&gt;&lt;br /&gt;$ &gt; #&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;сразу добавить что-нибудь (в данном случае решетку) в конец, а не так как мы ее добавляли выше&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Второе, более хитрое. Позволяет делать "универсальные" правила. Словами объяснять долго, объясню на примере:&lt;br /&gt;&lt;pre&gt;&lt;code class="no-highlight"&gt;&lt;br /&gt;{A=abc} &gt; {A}{A}{A}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Эквивалентно целым 3 правилам&lt;br /&gt;&lt;pre&gt;&lt;code class="no-highlight"&gt;&lt;br /&gt;a &gt; aaa&lt;br /&gt;b &gt; bbb&lt;br /&gt;c &gt; ccc&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;При этом можно даже так:&lt;br /&gt;&lt;pre&gt;&lt;code class="no-highlight"&gt;&lt;br /&gt;{BBB=1-3} &gt; { int(BBB)*7 }&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;что эквивалентно&lt;br /&gt;&lt;pre&gt;&lt;code class="no-highlight"&gt;&lt;br /&gt;1 &gt; 7&lt;br /&gt;2 &gt; 14&lt;br /&gt;3 &gt; 21&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;То есть это позволяет сокращать "программы". Кто не понял, как это работает, объясняю подробнее: фигурные скобки слева от "&gt;" будут соответствовать только одному символу, из тех, что находятся внутри этих фигурных скобок после знака "=". При этом, если соответсвтие произошло, переменной слева от "=" будет присвоено строковое значение этого символа. При совершении подстановки оно может быть раскрыто в правой части. Если быть точнее от того, что стоит в фигурных скобках в правой части будет взят питоновский eval. Это вообще не совсем честная операция, в том смысле что алгоритмы Маркова основаны исключительно на подстановках, и правильнее было бы сделать препроцессор, который "раскрывал" бы такие универсальные подстановки, генерируя нормальные программы для классической машины алгоритмов Маркова. Но это оказалось выполнить немного технически сложнее, поэтому все осталось именно так как я описал.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Не мудрствуя лукво, привожу исходник.&lt;br /&gt;&lt;pre&gt;&lt;code class="python"&gt;&lt;br /&gt;import re&lt;br /&gt;&lt;br /&gt;COMMENT_SYMBOL = ';'&lt;br /&gt;DELIM_SYMBOL = '&gt;'&lt;br /&gt;&lt;br /&gt;class FromMatcher(object):&lt;br /&gt;   def __init__(self):&lt;br /&gt;       self.vars = []&lt;br /&gt;&lt;br /&gt;   def appendVarName(self, varname):&lt;br /&gt;       self.vars.append(varname)&lt;br /&gt;&lt;br /&gt;   def setFromReStr(self, s):&lt;br /&gt;       self.fromReStr = s&lt;br /&gt;       self.fromRe = re.compile(s)&lt;br /&gt;&lt;br /&gt;   def getFromRe(self):&lt;br /&gt;       return self.fromRe&lt;br /&gt;&lt;br /&gt;   def __str__(self):&lt;br /&gt;       return 'FromMatcher(%s)' % self.fromReStr&lt;br /&gt;&lt;br /&gt;   def getVarDict(self, mo):&lt;br /&gt;       return dict([(v, mo.group(v)) for v in self.vars])&lt;br /&gt;&lt;br /&gt;class MarkovCompiler(object):&lt;br /&gt;   FROM_VAR_PATTERN = re.compile(r'\{(?P&amp;lt;varname&gt;[A-Z_][A-Za-z0-9_]*?)=(?P&amp;lt;chars&gt;.+?)\}') # } to place first in chars group if needed&lt;br /&gt;   SPECIAL_CHARS = re.compile(r'(?&amp;lt;!\\)([*?+\{\}\[\]\(\)])')&lt;br /&gt;   REPLACE_GROUP_RE = re.compile(r'\{([^{}]+?)\}')&lt;br /&gt;&lt;br /&gt;   @staticmethod&lt;br /&gt;   def makeRe(from_s):&lt;br /&gt;       fm = FromMatcher()&lt;br /&gt;&lt;br /&gt;       class ResultHolder:&lt;br /&gt;           def __init__(self):&lt;br /&gt;               self.result = ''&lt;br /&gt;               self.lastend = 0&lt;br /&gt;&lt;br /&gt;       rh = ResultHolder()&lt;br /&gt;&lt;br /&gt;       def processBetween(s):&lt;br /&gt;           s = MarkovCompiler.SPECIAL_CHARS.sub(r'\\\1', s)&lt;br /&gt;           return s&lt;br /&gt;&lt;br /&gt;       def subf(mo):&lt;br /&gt;           varname = mo.group('varname')&lt;br /&gt;           fm.appendVarName(varname)&lt;br /&gt;           chars = mo.group('chars')&lt;br /&gt;&lt;br /&gt;           between_s = from_s[rh.lastend:mo.start()]&lt;br /&gt;           rh.result += processBetween(between_s)&lt;br /&gt;           rh.lastend = mo.end()&lt;br /&gt;&lt;br /&gt;           rh.result += r'(?P&amp;lt;%s&gt;[%s])' % (varname, chars)&lt;br /&gt;&lt;br /&gt;       MarkovCompiler.FROM_VAR_PATTERN.sub(subf, from_s)&lt;br /&gt;&lt;br /&gt;       rh.result += processBetween(from_s[rh.lastend:])&lt;br /&gt;&lt;br /&gt;       fm.setFromReStr(rh.result)&lt;br /&gt;       return fm&lt;br /&gt;&lt;br /&gt;   @staticmethod&lt;br /&gt;   def expandTo(to_s, var_dict):&lt;br /&gt;       def subf(mo):&lt;br /&gt;#            print 'Evaling:', '#'+mo.group(1)+'#'&lt;br /&gt;           return str(eval(mo.group(1), None, var_dict))&lt;br /&gt;&lt;br /&gt;       return MarkovCompiler.REPLACE_GROUP_RE.sub(subf, to_s)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;class Rule(object):&lt;br /&gt;   def __init__(self, s):&lt;br /&gt;       self.terminating = False&lt;br /&gt;       self.parse(s)&lt;br /&gt;&lt;br /&gt;   def parse(self, s):&lt;br /&gt;       self._from, self._to = map(str.strip, s.split(DELIM_SYMBOL))&lt;br /&gt;&lt;br /&gt;       if self._to[-1] == '.':&lt;br /&gt;           self.terminating = True&lt;br /&gt;           self._to = self._to[:-1]&lt;br /&gt;&lt;br /&gt;       self.compile()&lt;br /&gt;&lt;br /&gt;   def compile(self):&lt;br /&gt;       self._fromMatcher = MarkovCompiler.makeRe(self._from)&lt;br /&gt;#        print 'Compiled:', self._fromMatcher&lt;br /&gt;&lt;br /&gt;   def match(self, s):&lt;br /&gt;       self._mo = self._fromMatcher.getFromRe().search(s)&lt;br /&gt;&lt;br /&gt;   def getMatch(self):&lt;br /&gt;       return self._mo&lt;br /&gt;&lt;br /&gt;   def expand(self):&lt;br /&gt;       return MarkovCompiler.expandTo(self._to, self._fromMatcher.getVarDict(self._mo))&lt;br /&gt;&lt;br /&gt;   def showVars(self):&lt;br /&gt;       d = self._fromMatcher.getVarDict(self._mo)&lt;br /&gt;       return ', '.join(['%s=%s' % (k, d[k]) for k in d])&lt;br /&gt;&lt;br /&gt;   def __str__(self):&lt;br /&gt;       return '%s: %s &gt; %s' % (self.__class__.__name__, self._from, self._to)&lt;br /&gt;&lt;br /&gt;   def isTerminating(self):&lt;br /&gt;       return self.terminating&lt;br /&gt;&lt;br /&gt;class Algorithm(object):&lt;br /&gt;   def __init__(self, listOfStr):&lt;br /&gt;       self.rules = list(map(Rule, listOfStr))&lt;br /&gt;&lt;br /&gt;   def __str__(self):&lt;br /&gt;       return '%s(\n\t' % self.__class__.__name__ + \&lt;br /&gt;               '\n\t'.join(map(str, self.rules)) + \&lt;br /&gt;               '\n)'&lt;br /&gt;      &lt;br /&gt;   def getRules(self):&lt;br /&gt;       return self.rules&lt;br /&gt;&lt;br /&gt;class Exit:&lt;br /&gt; pass&lt;br /&gt;&lt;br /&gt;class Processor(object):&lt;br /&gt;   def __init__(self, program=None, file=None):&lt;br /&gt;       if type(program) == str:&lt;br /&gt;           self.parse(program)&lt;br /&gt;       elif type(program) == file:&lt;br /&gt;           self.parse(program.read())&lt;br /&gt;       elif file:&lt;br /&gt;           self.parse(open(file).read())&lt;br /&gt;&lt;br /&gt;   def parse(self, _s):&lt;br /&gt;       _arr = [s.strip() for s in _s.split('\n') \&lt;br /&gt;                             if s.lstrip() \&lt;br /&gt;                             and s.lstrip()[0] != COMMENT_SYMBOL]&lt;br /&gt;       self.setData(_arr[0])&lt;br /&gt;       self.setAlgorithm(_arr[1:])&lt;br /&gt;&lt;br /&gt;   def setData(self, initialData):&lt;br /&gt;       self.initialData = initialData&lt;br /&gt;&lt;br /&gt;   def setAlgorithm(self, algorithm):&lt;br /&gt;       if type(algorithm) == str:&lt;br /&gt;           self.parse('_\n'+algorithm)&lt;br /&gt;           self.initialData = None&lt;br /&gt;       if type(algorithm) in (list, tuple) :&lt;br /&gt;           self.algorithm = Algorithm(algorithm)&lt;br /&gt;       elif type(algorithm) == Algorithm:&lt;br /&gt;           self.algorithm = algorithm&lt;br /&gt;&lt;br /&gt;   def process(self, debug=True):&lt;br /&gt;       _s = self.initialData&lt;br /&gt;&lt;br /&gt;       if debug: print 'Initial data:\n', _s, '\n\nAlgorithm:\n', self.algorithm&lt;br /&gt;&lt;br /&gt;       while True:&lt;br /&gt;           try:&lt;br /&gt;               _rule_applied = False&lt;br /&gt;&lt;br /&gt;               for rule in self.algorithm.getRules():&lt;br /&gt;                   rule.match(_s)&lt;br /&gt;                   if rule.getMatch():&lt;br /&gt;                       _rule_applied = True&lt;br /&gt;&lt;br /&gt;                       if debug:&lt;br /&gt;                           vars = rule.showVars()&lt;br /&gt;                           print str(rule) + ['', ', vars: ' + vars + ':'][int(bool(vars))]&lt;br /&gt;                       _s = _s[:rule.getMatch().start()] + rule.expand() + _s[rule.getMatch().end():]&lt;br /&gt;&lt;br /&gt;                       if debug: print _s, '\n'; import time; time.sleep(.05)&lt;br /&gt;                       if rule.isTerminating():&lt;br /&gt;                           if debug: print 'Terminating rule...'&lt;br /&gt;                           raise Exit()&lt;br /&gt;&lt;br /&gt;                       break   &lt;br /&gt;&lt;br /&gt;               if not _rule_applied:&lt;br /&gt;                   print 'No rule matched...'&lt;br /&gt;                   break&lt;br /&gt;&lt;br /&gt;           except Exit:&lt;br /&gt;               break&lt;br /&gt;&lt;br /&gt;       if debug: print 'Result:\n', _s       &lt;br /&gt;       return _s&lt;br /&gt;&lt;br /&gt;   def solve(self, data):&lt;br /&gt;       self.setData(data)&lt;br /&gt;       return self.process(debug=False)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;if __name__ == '__main__':&lt;br /&gt; import sys&lt;br /&gt; if len(sys.argv) &gt; 1:&lt;br /&gt;  FILE_NAME = sys.argv[1]&lt;br /&gt; else:&lt;br /&gt;  FILE_NAME = 'alg.txt'&lt;br /&gt; Processor(file=FILE_NAME).process()&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Вот как будет выглядеть программа для удвоения числа для этой версии интерпретатора:&lt;br /&gt;&lt;pre&gt;&lt;code class="no-highlight"&gt;&lt;br /&gt;99999&lt;br /&gt;&lt;br /&gt;{N1=0-9}[{N2=0-9}] &gt; [{ (2*int(N1)+int(N2)) / 10 }]{ (2*int(N1)+int(N2)) % 10 }&lt;br /&gt;&lt;br /&gt;^[0] &gt; .&lt;br /&gt;^[1] &gt; 1.&lt;br /&gt;&lt;br /&gt;$&gt;[0]&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Вот вывод:&lt;br /&gt;&lt;pre&gt;&lt;code class="no-highlight"&gt;&lt;br /&gt;G:\!TRY\Python\markov&gt;python markov_enhanced.py doubling_decimal2.txt&lt;br /&gt;Initial data:&lt;br /&gt;99999&lt;br /&gt;&lt;br /&gt;Algorithm:&lt;br /&gt;Algorithm(&lt;br /&gt;       Rule: {N1=0-9}[{N2=0-9}] &gt; [{ (2*int(N1)+int(N2)) / 10 }]{ (2*int(N1)+int(N2)) % 10 }&lt;br /&gt;       Rule: ^[0] &gt;&lt;br /&gt;       Rule: ^[1] &gt; 1&lt;br /&gt;       Rule: $ &gt; [0]&lt;br /&gt;)&lt;br /&gt;Rule: $ &gt; [0]&lt;br /&gt;99999[0]&lt;br /&gt;&lt;br /&gt;Rule: {N1=0-9}[{N2=0-9}] &gt; [{ (2*int(N1)+int(N2)) / 10 }]{ (2*int(N1)+int(N2)) % 10 }, vars: N1=9, N2=0:&lt;br /&gt;9999[1]8&lt;br /&gt;&lt;br /&gt;Rule: {N1=0-9}[{N2=0-9}] &gt; [{ (2*int(N1)+int(N2)) / 10 }]{ (2*int(N1)+int(N2)) % 10 }, vars: N1=9, N2=1:&lt;br /&gt;999[1]98&lt;br /&gt;&lt;br /&gt;Rule: {N1=0-9}[{N2=0-9}] &gt; [{ (2*int(N1)+int(N2)) / 10 }]{ (2*int(N1)+int(N2)) % 10 }, vars: N1=9, N2=1:&lt;br /&gt;99[1]998&lt;br /&gt;&lt;br /&gt;Rule: {N1=0-9}[{N2=0-9}] &gt; [{ (2*int(N1)+int(N2)) / 10 }]{ (2*int(N1)+int(N2)) % 10 }, vars: N1=9, N2=1:&lt;br /&gt;9[1]9998&lt;br /&gt;&lt;br /&gt;Rule: {N1=0-9}[{N2=0-9}] &gt; [{ (2*int(N1)+int(N2)) / 10 }]{ (2*int(N1)+int(N2)) % 10 }, vars: N1=9, N2=1:&lt;br /&gt;[1]99998&lt;br /&gt;&lt;br /&gt;Rule: ^[1] &gt; 1&lt;br /&gt;199998&lt;br /&gt;&lt;br /&gt;Terminating rule...&lt;br /&gt;Result:&lt;br /&gt;199998&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Ну и наконец приведу задачку такую: определить правильность (неправильность) скобочной структуры. То есть на вход подаетя строка вида ()((()()()((((())))()()) и требуется определить правильно ли вложены скобки (очевидно, что ")(" и "())" - неправильно). Результатом работы программы должно быть слово "right" в случае положительного ответа и "wrong" в противном случае.&lt;br /&gt;&lt;br /&gt;Вот собственно решение.&lt;br /&gt;&lt;pre&gt;&lt;code class="no-highlight"&gt;&lt;br /&gt;()()(()(()))&lt;br /&gt;&lt;br /&gt;** &gt; *&lt;br /&gt;()* &gt; *&lt;br /&gt;*() &gt; *&lt;br /&gt;(*) &gt; *&lt;br /&gt;&lt;br /&gt;() &gt; *&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;1* &gt; 1&lt;br /&gt;1( &gt; 0&lt;br /&gt;1) &gt; 0&lt;br /&gt;&lt;br /&gt;0{A=*()} &gt; 0&lt;br /&gt;&lt;br /&gt;1$ &gt; right.&lt;br /&gt;0$ &gt; wrong.&lt;br /&gt;&lt;br /&gt;^ &gt; 1&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Вывод:&lt;br /&gt;&lt;pre&gt;&lt;code class="no-highlight"&gt;&lt;br /&gt;G:\!TRY\Python\markov&gt;python markov_enhanced.py brackets.txt&lt;br /&gt;Initial data:&lt;br /&gt;()()(()(()))&lt;br /&gt;&lt;br /&gt;Algorithm:&lt;br /&gt;Algorithm(&lt;br /&gt;       Rule: ** &gt; *&lt;br /&gt;       Rule: ()* &gt; *&lt;br /&gt;       Rule: *() &gt; *&lt;br /&gt;       Rule: (*) &gt; *&lt;br /&gt;       Rule: () &gt; *&lt;br /&gt;       Rule: 1* &gt; 1&lt;br /&gt;       Rule: 1( &gt; 0&lt;br /&gt;       Rule: 1) &gt; 0&lt;br /&gt;       Rule: 0{A=*()} &gt; 0&lt;br /&gt;       Rule: 1$ &gt; right&lt;br /&gt;       Rule: 0$ &gt; wrong&lt;br /&gt;       Rule: ^ &gt; 1&lt;br /&gt;)&lt;br /&gt;Rule: () &gt; *&lt;br /&gt;*()(()(()))&lt;br /&gt;&lt;br /&gt;Rule: *() &gt; *&lt;br /&gt;*(()(()))&lt;br /&gt;&lt;br /&gt;Rule: () &gt; *&lt;br /&gt;*(*(()))&lt;br /&gt;&lt;br /&gt;Rule: () &gt; *&lt;br /&gt;*(*(*))&lt;br /&gt;&lt;br /&gt;Rule: (*) &gt; *&lt;br /&gt;*(**)&lt;br /&gt;&lt;br /&gt;Rule: ** &gt; *&lt;br /&gt;*(*)&lt;br /&gt;&lt;br /&gt;Rule: (*) &gt; *&lt;br /&gt;**&lt;br /&gt;&lt;br /&gt;Rule: ** &gt; *&lt;br /&gt;*&lt;br /&gt;&lt;br /&gt;Rule: ^ &gt; 1&lt;br /&gt;1*&lt;br /&gt;&lt;br /&gt;Rule: 1* &gt; 1&lt;br /&gt;1&lt;br /&gt;&lt;br /&gt;Rule: 1$ &gt; right&lt;br /&gt;right&lt;br /&gt;&lt;br /&gt;Terminating rule...&lt;br /&gt;Result:&lt;br /&gt;right&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Если же на вход подать такую скобочную структуру: ()()((()(())), то получим:&lt;br /&gt;&lt;pre&gt;&lt;code class="no-highlight"&gt;&lt;br /&gt;G:\!TRY\Python\markov&gt;python markov_enhanced.py brackets.txt&lt;br /&gt;Initial data:&lt;br /&gt;()()((()(()))&lt;br /&gt;&lt;br /&gt;Algorithm:&lt;br /&gt;Algorithm(&lt;br /&gt;       Rule: ** &gt; *&lt;br /&gt;       Rule: ()* &gt; *&lt;br /&gt;       Rule: *() &gt; *&lt;br /&gt;       Rule: (*) &gt; *&lt;br /&gt;       Rule: () &gt; *&lt;br /&gt;       Rule: 1* &gt; 1&lt;br /&gt;       Rule: 1( &gt; 0&lt;br /&gt;       Rule: 1) &gt; 0&lt;br /&gt;       Rule: 0{A=*()} &gt; 0&lt;br /&gt;       Rule: 1$ &gt; right&lt;br /&gt;       Rule: 0$ &gt; wrong&lt;br /&gt;       Rule: ^ &gt; 1&lt;br /&gt;)&lt;br /&gt;Rule: () &gt; *&lt;br /&gt;*()((()(()))&lt;br /&gt;&lt;br /&gt;Rule: *() &gt; *&lt;br /&gt;*((()(()))&lt;br /&gt;&lt;br /&gt;Rule: () &gt; *&lt;br /&gt;*((*(()))&lt;br /&gt;&lt;br /&gt;Rule: () &gt; *&lt;br /&gt;*((*(*))&lt;br /&gt;&lt;br /&gt;Rule: (*) &gt; *&lt;br /&gt;*((**)&lt;br /&gt;&lt;br /&gt;Rule: ** &gt; *&lt;br /&gt;*((*)&lt;br /&gt;&lt;br /&gt;Rule: (*) &gt; *&lt;br /&gt;*(*&lt;br /&gt;&lt;br /&gt;Rule: ^ &gt; 1&lt;br /&gt;1*(*&lt;br /&gt;&lt;br /&gt;Rule: 1* &gt; 1&lt;br /&gt;1(*&lt;br /&gt;&lt;br /&gt;Rule: 1( &gt; 0&lt;br /&gt;0*&lt;br /&gt;&lt;br /&gt;Rule: 0{A=*()} &gt; 0, vars: A=*:&lt;br /&gt;0&lt;br /&gt;&lt;br /&gt;Rule: 0$ &gt; wrong&lt;br /&gt;wrong&lt;br /&gt;&lt;br /&gt;Terminating rule...&lt;br /&gt;Result:&lt;br /&gt;wrong&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt; Из этих двух примеров отчетливо виден принцип решения. В скобочной структуре все "правильные" фрагменты заменяются на звезочки, подряд-идущие звездочки удаляются. Если в конце останется ряд звезочек - исходная скобочная структура была правильна. Если же в ней останутся скобки -&gt; структура скобок была неправильна.&lt;br /&gt;&lt;br /&gt; Если вам показалось интересным что нибудь из этого, то предлагаю придумать какие задачи еще можно решить на этой "машине". Если хотите поломать голову, могу подкинуть несколько задачек:&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt; Усовершенствовать последнюю задачу для определения правильности скобочной структуры из (){}[] и символов английского алфавита между ними.&lt;br /&gt;&lt;/li&gt;&lt;li&gt; Написать алгоритм обращения строки (из символов a-zA-Z0-9) задом-наперед (reverse).&lt;br /&gt;&lt;/li&gt;&lt;li&gt; Сложение 2-х десятичных чисел, заданных строкой вида "12345+678".&lt;br /&gt;&lt;/li&gt;&lt;li&gt; Реализовать алгоритм &lt;a href="http://www.miranda.org/%7Ejkominek/rot13/"&gt;rot13&lt;/a&gt;.&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Задачи 2)-4) были решены мною, и решения будут опубликованы следующим постом.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Выводы.&lt;/b&gt; Алгоритмы Маркова представляют собой очень простую но вместе с тем интересную концепцию. Думается, что-то подобное было в древнем языке &lt;a href="http://ru.wikipedia.org/wiki/Snobol"&gt;Snobol&lt;/a&gt; и в (возможно, ошибаюсь) его современном диалекте &lt;a href="http://snowball.tartarus.org/index.php"&gt;Snowball&lt;/a&gt; применяемом для стеммеров при полнотекстовом поиске. Пока что практическая польза такого подхода не очень велика, но, возможно, найдутся задачи, где подобный подход может показать себя с положительной стороны. Возможно, стоит улучшить интерпретатор чтоб распознавались не одиночные символы, а целые регулярные выражения.&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2153457809622714873-3594604964518715996?l=www.xonix.info' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.xonix.info/feeds/3594604964518715996/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2153457809622714873&amp;postID=3594604964518715996' title='Комментарии: 25'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2153457809622714873/posts/default/3594604964518715996'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2153457809622714873/posts/default/3594604964518715996'/><link rel='alternate' type='text/html' href='http://www.xonix.info/2007/06/blog-post_22.html' title='Алгоритмы Маркова'/><author><name>xonix</name><uri>http://www.blogger.com/profile/10521854320903151029</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='10674690484909106230'/></author><thr:total>25</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2153457809622714873.post-2902931069384028601</id><published>2007-06-01T03:05:00.000-07:00</published><updated>2007-06-01T03:10:02.637-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='decorators'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Еще про декораторы</title><content type='html'>Интересная статья про декораторы &lt;a href="http://www.ibm.com/developerworks/ru/library/l-cpdecor/index.html?ca=drs-ru" title="Очаровательный Python: Магия декораторов"&gt;на сайте ibm.com&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2153457809622714873-2902931069384028601?l=www.xonix.info' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.xonix.info/feeds/2902931069384028601/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2153457809622714873&amp;postID=2902931069384028601' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2153457809622714873/posts/default/2902931069384028601'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2153457809622714873/posts/default/2902931069384028601'/><link rel='alternate' type='text/html' href='http://www.xonix.info/2007/06/blog-post.html' title='Еще про декораторы'/><author><name>xonix</name><uri>http://www.blogger.com/profile/10521854320903151029</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='10674690484909106230'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2153457809622714873.post-1024552800177528201</id><published>2007-05-16T10:20:00.000-07:00</published><updated>2007-05-16T10:48:25.143-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='html'/><title type='text'>Кроссбраузерность</title><content type='html'>Такой код&lt;br /&gt;&lt;pre&gt;&lt;code class="html"&gt;&lt;br /&gt;&amp;lt;textarea id="t"&amp;gt;&lt;br /&gt;123&lt;br /&gt;123&lt;br /&gt;123&lt;br /&gt;123&lt;br /&gt;&amp;lt;/textarea&amp;gt;&lt;br /&gt;&amp;lt;script&amp;gt;&lt;br /&gt;var t = document.getElementById('t');&lt;br /&gt;alert(t.value.length+", "+&lt;br /&gt; t.value.charCodeAt(3)+", "+t.value.charCodeAt(4));&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;под Firefox'ом (1.5.0.11) выдает &lt;br /&gt;&lt;pre&gt;&lt;code&gt;16, 10, 49&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;а под IE 7.0&lt;br /&gt;&lt;pre&gt;&lt;code&gt;20, 13, 10&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Напомню, что ASCII-код '\r' равен 13, а '\n' равен 10.&lt;br /&gt;Таким образом файрфокс переносы строк обрабатывает как '\n' а IE как '\r\n' в текстовых полях. Но что характерно, оба браузера при сабмите шлют на сервер '\r\n', такая вот небольшая несуразность с файрфоксом. Проблема с этим у меня возникла при клиентской валидации длины вводимого текста. При этом на файрфоксе в некоторых случаях клинентская валидация проходила успешно, а серверная нет.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2153457809622714873-1024552800177528201?l=www.xonix.info' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.xonix.info/feeds/1024552800177528201/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2153457809622714873&amp;postID=1024552800177528201' title='Комментарии: 5'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2153457809622714873/posts/default/1024552800177528201'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2153457809622714873/posts/default/1024552800177528201'/><link rel='alternate' type='text/html' href='http://www.xonix.info/2007/05/blog-post_16.html' title='Кроссбраузерность'/><author><name>xonix</name><uri>http://www.blogger.com/profile/10521854320903151029</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='10674690484909106230'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2153457809622714873.post-2905576392886710157</id><published>2007-05-11T05:40:00.000-07:00</published><updated>2007-08-27T02:22:01.200-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='bicycle'/><category scheme='http://www.blogger.com/atom/ns#' term='oop'/><title type='text'>ООП, замыкания, или изобретая велосипеды...</title><content type='html'>Тут одна занятная штука нарисовалась. Вот возьмем объект (экземпляр класса) из ООП. Упрощенно, его методы можно считать обычными функциями, в контекст которых захвачена переменная &lt;span style="font-style: italic;"&gt;this &lt;/span&gt;(или &lt;span style="font-style: italic;"&gt;self&lt;/span&gt;, кому что нравится). &lt;span class="fullpost0"&gt;Поясню на примере языка Python, что такое замыкание.&lt;br /&gt;&lt;pre&gt;&lt;code class="python"&gt;&lt;br /&gt;def makeAdder(num):&lt;br /&gt;   def adder(num1):&lt;br /&gt;       return num + num1&lt;br /&gt;   return adder&lt;br /&gt;&lt;br /&gt;sevenAdder = makeAdder(7)&lt;br /&gt;print sevenAdder(2) # =&gt; 9&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;В данном случае вложенная функция &lt;span style="font-style: italic;"&gt;adder &lt;/span&gt;при своем создании захватывает окружающий ее контекст. В частности, в этом контексте оказывается переданная в функцию &lt;span style="font-style: italic;"&gt;makeAdder &lt;/span&gt;переменная &lt;span style="font-style: italic;"&gt;num.&lt;br /&gt;&lt;/span&gt;И вот пришла идея создать на такой основе что-то вроде ООП. Вот что в результате получилось:&lt;br /&gt;&lt;pre&gt;&lt;code class="python"&gt;from bicycleoop import *&lt;br /&gt;&lt;br /&gt;@klass()&lt;br /&gt;def Superclass(this, kls):&lt;br /&gt;   kls.AAA = 'Superclass_AAA'&lt;br /&gt;&lt;br /&gt;   @method&lt;br /&gt;   def say1():&lt;br /&gt;       print 'from super.say1: [arg1 = %s];' % this.arg1&lt;br /&gt;&lt;br /&gt;   @method&lt;br /&gt;   def say3():&lt;br /&gt;       print 'from say3;'&lt;br /&gt;&lt;br /&gt;   @method&lt;br /&gt;   def say5():&lt;br /&gt;       print 'from say5:',&lt;br /&gt;       this.say2()&lt;br /&gt;&lt;br /&gt;   @method&lt;br /&gt;   def say6():&lt;br /&gt;       print 'from super say6;'&lt;br /&gt;&lt;br /&gt;   @method&lt;br /&gt;   def say7():&lt;br /&gt;       print 'from say7: this.AAA='+this.klass.AAA, 'super.AAA='+this.super.klass.AAA&lt;br /&gt;&lt;br /&gt;@klass(Superclass)&lt;br /&gt;def Klass1(this, kls):&lt;br /&gt;   kls.AAA = 'Klass1_AAA'&lt;br /&gt;&lt;br /&gt;   @klassmethod&lt;br /&gt;   def klm1():&lt;br /&gt;       print 'KlassMethod1: Klass1.AAA='+kls.AAA, 'Superclass.AAA='+kls.superClass.AAA&lt;br /&gt;&lt;br /&gt;   @method&lt;br /&gt;   def __init__(arg1, arg2):&lt;br /&gt;       print 'creating instance of %s with arg1=%s, arg2=%s' % (this.klass.name, arg1, arg2)&lt;br /&gt;       this.arg1 = arg1&lt;br /&gt;       this.arg2 = arg2&lt;br /&gt;&lt;br /&gt;   @method&lt;br /&gt;   def say1():&lt;br /&gt;       print "from say1: [arg1 = %s]" % this.arg1&lt;br /&gt;&lt;br /&gt;   @method&lt;br /&gt;   def say2():&lt;br /&gt;       print "from say2: [arg2 = %s]" % this.arg2,&lt;br /&gt;       this.say1()&lt;br /&gt;&lt;br /&gt;   @method&lt;br /&gt;   def say4():&lt;br /&gt;       print 'from say4:',&lt;br /&gt;       this.say3()&lt;br /&gt;&lt;br /&gt;   @method&lt;br /&gt;   def say6():&lt;br /&gt;       print 'from say6:',&lt;br /&gt;       this.super.say6()&lt;br /&gt;&lt;br /&gt;print Superclass.AAA        # =&gt; Superclass_AAA&lt;br /&gt;print Klass1.AAA            # =&gt; Klass1_AAA&lt;br /&gt;&lt;br /&gt;k = Klass1.new(17, "Hello") # =&gt; creating instance of Klass1 with arg1=17, arg2=Hello&lt;br /&gt;k.say1          # =&gt; from say1: [arg1 = 17]&lt;br /&gt;k.super.say1()  # =&gt; from super.say1: [arg1 = 17];&lt;br /&gt;k.say2()        # =&gt; from say2: [arg2 = Hello] from say1: [arg1 = 17]&lt;br /&gt;k.say3()        # =&gt; from say3;&lt;br /&gt;k.say4()        # =&gt; from say4: from say3;&lt;br /&gt;k.say5()        # =&gt; from say5: from say2: [arg2 = Hello] from say1: [arg1 = 17]&lt;br /&gt;k.say6()        # =&gt; from say6: from super say6;&lt;br /&gt;k.say7()        # =&gt; from say7: this.AAA=Klass1_AAA super.AAA=Superclass_AAA&lt;br /&gt;&lt;br /&gt;k1 = Klass1.new(20, "sdfs") # =&gt; creating instance of Klass1 with arg1=20, arg2=sdfs&lt;br /&gt;k.say2()        # =&gt; from say2: [arg2 = Hello] from say1: [arg1 = 17]&lt;br /&gt;k1.say2()       # =&gt; from say2: [arg2 = sdfs] from say1: [arg1 = 20]&lt;br /&gt;&lt;br /&gt;print Superclass.AAA        # =&gt; Superclass_AAA&lt;br /&gt;print Klass1.AAA            # =&gt; Klass1_AAA&lt;br /&gt;&lt;br /&gt;Klass1.klm1()     # =&gt; KlassMethod1: Klass1.AAA=Klass1_AAA Superclass.AAA=Superclass_AAA&lt;br /&gt;&lt;br /&gt;sup = Superclass.new()&lt;br /&gt;&lt;br /&gt;print k.methods.keys()      # =&gt; ['say7', 'say6', 'say5', 'say4', 'say3', 'say2', 'say1', '__init__']&lt;br /&gt;print sup.methods.keys()    # =&gt; ['say7', 'say6', 'say5', 'say3', 'say1']&lt;br /&gt;&lt;br /&gt;print k.klass == Klass1                                 # =&gt; True&lt;br /&gt;print k.super.klass == Klass1.superClass == Superclass  # =&gt; True&lt;br /&gt;&lt;br /&gt;o1 = obj()&lt;br /&gt;&lt;br /&gt;Klass1(o1, Klass1)&lt;br /&gt;o1.arg1 = 15&lt;br /&gt;o1.say1()   # =&gt; from say1: [arg1 = 15]&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Как видим, реализовано наследование (одиночное, правда), обычные и статические методы (методы класса), и статические поля тоже.  Создание объектов реализовано в стиле Смоллтолк и Руби (через Klass.new). В объявлении класса передаются 2 переменные (this и kls), что кажется немного странным. Но это, во-первых, более pythonic (&lt;span style="font-style: italic;"&gt;Explicit is better than implicit&lt;/span&gt;) а, во-вторых, это как раз те переменные, которые будут "замкнуты" в замыкания из функций-методов. Замечу, что они инициализируются лишь в момент создания объекта.&lt;br /&gt;Для выразительности объявление классов и методов выполненно в виде декораторов. Декоратор (для тех, кто не в курсе) это функциональность, работающаю следующим образом: код&lt;br /&gt;&lt;pre&gt;&lt;code class="python"&gt;&lt;br /&gt;@decor&lt;br /&gt;def f(arg):&lt;br /&gt;    # do smth&lt;br /&gt;    pass&lt;/code&gt;&lt;/pre&gt;эквивалентен коду:&lt;br /&gt;&lt;pre&gt;&lt;code class="python"&gt;&lt;br /&gt;def f(arg):&lt;br /&gt;    # do smth&lt;br /&gt;    pass&lt;br /&gt;&lt;br /&gt;f = decor(f)&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;То есть декоратор - это обычная функия, которой мы "декорируем" некоторую другую функцию. Я полагаю это имеет отношение к &lt;span title="Aspect-oriented programming" style="text-decoration: underline;"&gt;AOP&lt;/span&gt; и вероятно, этот механизм призван внести большую выразительность и декларативность в код. Подробнее про декораторы читать &lt;a href="http://www.python.org/dev/peps/pep-0318/"&gt;тут&lt;/a&gt;.&lt;br /&gt;А вот описание модуля с декораторами:&lt;br /&gt;&lt;pre&gt;&lt;code class="python"&gt;# this is file bicycleoop.py&lt;br /&gt;import sys&lt;br /&gt;&lt;br /&gt;def obj():&lt;br /&gt;   def _obj():pass&lt;br /&gt;   return _obj&lt;br /&gt;&lt;br /&gt;def klass(*args):&lt;br /&gt;   # args - superclasses, now only 1 allowed&lt;br /&gt;   def classDecorator(classFunc):&lt;br /&gt;       if len(args) == 1:&lt;br /&gt;           _superClass = args[0]&lt;br /&gt;       else:&lt;br /&gt;           _superClass = None&lt;br /&gt;&lt;br /&gt;       def copyMethodsTo(this):&lt;br /&gt;           this.klass = classFunc&lt;br /&gt;&lt;br /&gt;           if _superClass:&lt;br /&gt;               this.klass.superClass = _superClass&lt;br /&gt;               this.klass.superClass.copyMethodsTo(this)# now into this exist all functions with captured 'this'&lt;br /&gt;               this.klass = classFunc # return back after ^^^ changes it&lt;br /&gt;               this.super = obj()&lt;br /&gt;               this.super.klass = _superClass&lt;br /&gt;               for meth in this.methods.keys():&lt;br /&gt;                   setattr(this.super, meth, this.methods[meth])&lt;br /&gt;           else:&lt;br /&gt;               classFunc.superClass = None&lt;br /&gt;&lt;br /&gt;           this.klass(this, obj()) # initing methods&lt;br /&gt;           return this&lt;br /&gt;&lt;br /&gt;       def new(*a, **k):&lt;br /&gt;           this = obj()&lt;br /&gt;           copyMethodsTo(this)&lt;br /&gt;           this.__init__(*a, **k)&lt;br /&gt;           return this&lt;br /&gt;&lt;br /&gt;       classFunc.copyMethodsTo = copyMethodsTo&lt;br /&gt;       classFunc.new = new&lt;br /&gt;       classFunc.name = classFunc.func_name&lt;br /&gt;&lt;br /&gt;       classFunc(obj(), classFunc) # for class vars and class methods&lt;br /&gt;&lt;br /&gt;       return classFunc&lt;br /&gt;&lt;br /&gt;   return classDecorator&lt;br /&gt;&lt;br /&gt;def method(methodFunc):&lt;br /&gt;   this = sys._getframe().f_back.f_locals['this']&lt;br /&gt;   setattr(this, methodFunc.func_name, methodFunc)&lt;br /&gt;   if not hasattr(this, 'methods'):&lt;br /&gt;       this.methods = {}&lt;br /&gt;   this.methods[methodFunc.func_name] = methodFunc&lt;br /&gt;   return methodFunc&lt;br /&gt;&lt;br /&gt;def klassmethod(methodFunc):&lt;br /&gt;   kls = sys._getframe().f_back.f_locals['kls']&lt;br /&gt;   setattr(kls, methodFunc.func_name, methodFunc)&lt;br /&gt;   if not hasattr(kls, 'klassmethods'):&lt;br /&gt;       kls.klassmethods = {}&lt;br /&gt;   kls.klassmethods[methodFunc.func_name] = methodFunc&lt;br /&gt;   return methodFunc&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Тут есть пару моментов. Такой вот замысловатый код:&lt;br /&gt;&lt;pre&gt;&lt;code class="python"&gt;&lt;br /&gt;sys._getframe().f_back.f_locals['this']&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;используется, чтоб подняться вверх по стеку вызовов функций и получить из локального контекста внешней функции необходимые переменные. А второй довольно смешной хак тут - использование все тех же функций в качестве объектов (функция &lt;span style="font-style: italic;"&gt;obj&lt;/span&gt; на самом деле возвращает новую пустую функцию, которую мы используем совсем не по назначению))), которым можно присваивать любые поля, ввиду того, что такой код, как видим, не хочет работать:&lt;br /&gt;&lt;pre&gt;&lt;code class="python"&gt;&lt;br /&gt;&gt;&gt;&gt; o = object()&lt;br /&gt;&gt;&gt;&gt; o.a = 123&lt;br /&gt;&lt;br /&gt;Traceback (most recent call last):&lt;br /&gt;File "&lt;pyshell#16&gt;", line 1, in &lt;module&gt;&lt;br /&gt;o.a = 123&lt;br /&gt;AttributeError: 'object' object has no attribute 'a'&lt;br /&gt;&gt;&gt;&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;Заключение:&lt;/span&gt;&lt;br /&gt;Полезность (практическая) этого дела нулевая, но позволяет хорошенько размять мозг. И, кроме того, получилось довольно концептуально: ключевое слово языка &lt;span style="font-style: italic;"&gt;class &lt;/span&gt;использовано не было. Правда опять же, ООП-природа языка была использована, по этому все довольно не честно, но мы никому ничего и не были обязаны =)&lt;br /&gt;&lt;br /&gt;PS. Извините за длинный пост. Кто-нибудь может подсказать как показывать только часть поста со ссылкой "читать далее" на лицевой странице?&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2153457809622714873-2905576392886710157?l=www.xonix.info' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.xonix.info/feeds/2905576392886710157/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2153457809622714873&amp;postID=2905576392886710157' title='Комментарии: 52'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2153457809622714873/posts/default/2905576392886710157'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2153457809622714873/posts/default/2905576392886710157'/><link rel='alternate' type='text/html' href='http://www.xonix.info/2007/05/blog-post_11.html' title='ООП, замыкания, или изобретая велосипеды...'/><author><name>xonix</name><uri>http://www.blogger.com/profile/10521854320903151029</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='10674690484909106230'/></author><thr:total>52</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2153457809622714873.post-4453656276401988000</id><published>2007-05-08T04:42:00.000-07:00</published><updated>2007-05-14T06:55:21.292-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><title type='text'>Smalltalk, въезд</title><content type='html'>Ну что. Расскажу я вам немного про &lt;span style="font-style: italic;"&gt;Смоллтолк&lt;/span&gt;. Наверное, все (нормальные) программисты слышали такое слово как &lt;span style="font-style: italic;"&gt;Смоллтолк&lt;/span&gt;, и что это есть дедушка всех ООП языков. Все это так, но мало кто знает, что это действительно очень концептуальный и интересный язык.&lt;br /&gt;Что же в нем такого интересного? В общем то основные фичи языка можно прочитать &lt;a href="http://ru.wikipedia.org/wiki/Smalltalk"&gt;тут &lt;/a&gt;и &lt;a href="http://en.wikipedia.org/wiki/Smalltalk"&gt;тут&lt;/a&gt;. В двух словах: все - суть объекты, которые общаются посредством сообщений (эквивалент сообщения - вызов метода).&lt;br /&gt;Интересное начинается дальше. В смоллтолке все программы как бы неразрывны, более того, в смоллтолке все есть одна большая неразрывная программа. Другими словами, эта большая программа - смоллтолк-система, которая вертится на виртуальной машине. Виртуальная машина, как вы могли догадаться, написана на смоллтолке :-). При этом С-рантайм, на котором собственно вертится эта виртуальная машина очень маленький, например у &lt;a href="http://squeak.org/" title="Самая интересная реализация Смоллтолка, над которой трудится Алан Кей"&gt;Squeak &lt;/a&gt;исполняемый файл весит всего 1 Мб, что даже меньше чем размер текущей версии флэш-плеера!!! Основная же функциональность вынесена в виртуальную машину, которая в Squeak на данный момент весит ~17 Мб. За счет такого положения дел Squeak чрезвычайно хорошо портируем ). Похожим может похвастать Java. В ней тоже основные библиотеки (коллекции, IO, регекспы, дата-время, графич. библиотека) написаны на ней самой, благо скорость работы Java'ы это позволяет. Преимущества такого подхода очевидны. На текущий момент ни питон ни руби не могут похвастать подобным (. Подробнее об архитектуре можно прочитать &lt;a href="http://www.smalltalk.ru/2005/02/big-talk-with-creator-of-smalltalk.html" title="Big talk with the creator of Smalltalk"&gt;тут &lt;/a&gt;и &lt;a href="http://smallthought.com/avi/?p=22" title="Turtles need Speed"&gt;тут&lt;/a&gt;.&lt;br /&gt;Однако, отвлеклись )). Архитектура смоллтолка в реализации Squeak выглядит так: на маленьком си-рантайменном ядре вертится виртуальная машина, на виртуальной машине вертится смоллтолк-система.&lt;br /&gt;Сама смоллтолк-система тоже очень интересная сущность. Это исключительно динамичная (абсолютно все параметры системы можно менять на лету) система. Сказать кратко - это мини-ось, а что, в качестве шелла Workspace+Transcript, в качестве скриптового языка - его величество, Смоллтолк, рабочий стол, Morphic'овские окошки, графика (&lt;a href="http://wiki.squeak.org/squeak/uploads/morphic_squeak.gif"&gt;1&lt;/a&gt;, &lt;a href="http://wiki.squeak.org/squeak/uploads/683/squeak34-1600x1200.1.png"&gt;2&lt;/a&gt;, &lt;a href="http://lamswww.epfl.ch/reference/squeak/main_screen.jpg"&gt;3&lt;/a&gt;))) Только не смейтесь, реализация в виде ОС тоже &lt;a href="http://sourceforge.net/projects/squeaknos" title="&amp;quot;What we gonna do with SqueakNOS is getting rid of the OS under Squeak&amp;quot;"&gt;есть&lt;/a&gt;).&lt;br /&gt;В смоллтолке реализована так называемая инкрементальная компиляция. Т.е. создавать классы и методы, а так-же менять методы вы можете на лету. При этом каждый раз, когда Вы говорите системе Accept некоторый код, этот код компилируется. Это возможно, потому что в Смоллтолк-системе существует порожденный экземпляр компилятора, который и выполняет данную работу)) Продолжая параллели с Java'ой - в джаве тоже замечательно, что компилируя проект, вы компилируете только еще не скомпилированные классы, при этом вам совершенно не нужно иметь исходники используемых библиотек.&lt;br /&gt;Кроме того Смоллтолк предоставляет поистине невообразимые, чудовищные, колоссальные, уникальные, невероятные (эко меня торкнуло) средства для интроспекции уже работающей программы (кода). Тем более невероятно, вы можете воздействовать на работающую программу (изменять ее параметры) прямо на лету, и вам для этого не нужно ставить брекпойнты, проходить в пошаговом режиме (как в Java), вы как бы можете вклиниваться в работающую программу и подхачивать ее, не останавливая. Небольшой наглядный пример. Перед вами окошко, в котором летают шарики, отбаваясь от стенок (типа, модель газа). Вы в состоянии затормозить часть шариков, удалить, перекрасить, наблюдать за изменением их скоростей, все это на лету (Используя Inspect It, Explore It).&lt;br /&gt;И вот таким динамизмом эта среда пронизана польностью ).&lt;br /&gt;Что еще интересного. Исключения. В смоллтолке они особенные. Взять Java'у. В ней, если произошло исключение, стек вызовов методов как бы "раскручивается", и мы попадаем в некоторый &lt;span style="font-style: italic;"&gt;catch &lt;/span&gt;блок. В смоллтолке все хитрее!! Там стек не раскручивается. И после произошедшего исключения вы можете продолжить выполнение программы. Пример. Вы написали код, запускаете - упс, исключение - перед вами возникает окошко с названием исключения, стеком вызовов, и тремя кнопками (Proceed, Abandon, Debug). Вы по стеку вызовов перемещаетесь в точку возникновения исключения, исправляете, делаете Accept, и жмете Proceed, программа побежала дальше. Красота )) В какой Java'е, Python'е, Ruby это возможно?&lt;br /&gt;Ну и последнее. Когда вы выходите из системы она (система) сохраняется на диск в виде образа. Образ - это есть &lt;span style="font-style: italic;"&gt;дамп запущенной (работающей) системы&lt;/span&gt;! Там хранится весь введенный вами код, все запущенные программы, все установленные пакеты, и т.д. По сути вся система (та самая большая живая неразрывная программа) сериализуется  на диск. А при следующем запуске загружается! Это очень удобно, хоть и довольно непривычно. (Предствьте, у вас запущен сервер. Вы выключаете систему, затем через время включаете, и севрер вновь запущен как ни в чем не бывало). Можно иметь несколько образов с разным набором установленного ПО, или с разной конфигурацией, кроме того разные довольно большие проекты (&lt;a href="http://seaside.st/" title="Continuations-based web-framework"&gt;Seaside&lt;/a&gt;, &lt;a href="http://www.opencroquet.org/index.php/Main_Page" title="3D applications development"&gt;Croquet&lt;/a&gt;) имеют обыкновение распространяться в виде отдельных образов, хотя обычно их можно установить и как пакеты.&lt;br /&gt;Ладно, пока достаточно ). Тут еще не затронуты вопросы о континуациях (продолжениях), блоках кода (замыканиях) и других интересных вещах. Но хорошего по немножку. До связи!&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;UPD. &lt;/span&gt;Сдается мне в архитектуру вкралась досадная неточность, на самом деле, виртуальная машина написанная на смоллтолке автоматически конвертируется в исходники си, которые затем компилируются в исполняемый код. Это удобно, т.к. можно отлаживать виртуальную машину в смоллтолк-среде, а потом когда все работает как надо автоматически получить корректные ее си-исходники. Вот так то)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2153457809622714873-4453656276401988000?l=www.xonix.info' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.xonix.info/feeds/4453656276401988000/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2153457809622714873&amp;postID=4453656276401988000' title='Комментарии: 7'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2153457809622714873/posts/default/4453656276401988000'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2153457809622714873/posts/default/4453656276401988000'/><link rel='alternate' type='text/html' href='http://www.xonix.info/2007/05/smalltalk.html' title='Smalltalk, въезд'/><author><name>xonix</name><uri>http://www.blogger.com/profile/10521854320903151029</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='10674690484909106230'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2153457809622714873.post-2580149668911091938</id><published>2007-05-04T10:26:00.000-07:00</published><updated>2007-05-04T11:20:21.777-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='begin'/><title type='text'>Пожалуй, начнем...))</title><content type='html'>Надо что-то написать в первом посте... Блин, это мой первый блог, не знаю с чего начать))&lt;br /&gt;Ну в общем так.. Я завел блог, во-первых, потому, что это нынче модно, ну а во-вторых, потому что хочу поделиться с вами (моими будущими читателями) тем что мне интересно, и, возможно, заинтересовать Вас в этом.&lt;br /&gt;Немного о себе: 21 год, студент МФТИ, 5 курс, работаю программистом-аналитиком в некой оффшорной компании, занимаюсь J2EE-технологиями.&lt;br /&gt;Что же все таки мне интересно: разумеется Java, хотя, впрочем как и все остальное: различные интересные концептуальные языки программирования (Python, Ruby, Smalltalk), вообще философия программирования, веб-технологии, agile development, как впрочем и RIA (в прошлом работал Flash-&lt;s&gt;погромистом&lt;/s&gt;программистом), да и сейчас приходится иногда садиться за Flex, хотя в &lt;a href="http://www.novemberain.com/"&gt;некоторых кругах&lt;/a&gt; его вполне обоснованно принято ругать, Flash, AJAX, ... Так, что-то точно упустил, ну ладно, потом наверстаем))&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/2153457809622714873-2580149668911091938?l=www.xonix.info' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.xonix.info/feeds/2580149668911091938/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2153457809622714873&amp;postID=2580149668911091938' title='Комментарии: 1'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2153457809622714873/posts/default/2580149668911091938'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2153457809622714873/posts/default/2580149668911091938'/><link rel='alternate' type='text/html' href='http://www.xonix.info/2007/05/blog-post.html' title='Пожалуй, начнем...))'/><author><name>xonix</name><uri>http://www.blogger.com/profile/10521854320903151029</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='10674690484909106230'/></author><thr:total>1</thr:total></entry></feed>