Евгений Степанищев

Эксперт в области разработки веб-приложений и безопасности в интернете. Каждый месяц мой блог посещают около 90 тысяч человек. Работаю техническим директором в «Системах документооборота», занимаюсь электронным правительством.

Киев

Съедили в Киев на выходные. Красивый, (относительно) чистый, добрый, старинный город. Оооочень много красивых старых зданий, всё (по сравнению с Москвой) очёнь дёшево. Украинская речь понимается на слух без каких-либо проблем, но большая часть людей в Киеве разговаривает по-русски.

Украинский очень милый язык («подарункы», «кава» и «смачно» ласкают слух), первый день понравился больше всего, на второй день был ливневый дождь, попытка посушиться в жлобском отеле и холодный и поломанный поезд №4, который почему-то называют «фирменным».

Из приятных мелочей — множество растений (каштаны, о!), в переходах — куча цветов, которые не продаются у нас, и интересная фишка — на телефоне, где высвечивается информация о базовой станции, в метро показывается название станции метро (проезд в метро стоит 50 копеек, т.е. около 2.5 российских рублей!).

В итоге, поездка (включая номер в отеле, который мы сняли, потому что нам нужно было посушиться), с дорогой, едой и посещениями музеев, обошлась около 6500 рублей на человека.

Юра, спасибо что позвал!
10 комментариев
22 сентября 2008 18:30

Когда начинается старость?

Мы в офисе немного поспорили на тему каким возрастам соотвествуют какие названия. Вот в каком возрасте начинается зрелость? Мы поискали в «Яндексе». Я недавно читал на эту тему небольшую главку в одной книжке, а для моих сослуживцев эти данные оказались сюрпризом:

до 15 лет — детство
16—30 лет — юность
31—45 — молодость
46—60 — зрелость
61—75 — пожилой возраст
76—90 — старческий возраст
91 и старше — долгожители

Я недавно завязал с юностью и шагнул в молодость.
9 комментариев
19 сентября 2008 19:20

Python iterstrip: itertools для чайников

Я общел рассказать как работает конструкция, которую мы, в итоге, в Ваней породили для тех, кто не знаком с itertools в Python. Для начала формулировка задачи: есть список (list) последовательности строк. В нём все поледовательности пустых строк, длиной более двух надо заменить на одну пустую строку, а пустые строке в начале и конце списка удалить.

Из ['', 1, 2, 3, '', '', '', '', 4, '', ''] мы должны получить аккуратное [1, 2, 3, '', 4].

Код, который получился в итоге, с некоторыми упращениями, выглядит вот так:
return list(
            chain(
                *(
                    [[''], grp][item or len(grp)<3]
                        for item, grp in (item, list(grp))
                            for item, grp in groupby(chain(['']*3, letter, ['']*3), lambda x:x))
                )
                )
        )[1:-1]
Заголовок функции я приводить не буду, чтобы не плодить отступы, в функцию входит параметр letter, который содержит обрабатываемый список, возвращается, соответственно, обработанный список. Я постараюсь обойтись без определения генераторов и итераторов, просто представьте, что данные «текут» по функциям, что позволяет их обрабатывать очень экономично, даже если они бесконечные.

Центральное место этой программы, функция groupby группирует входящий список, пользуясь результатом функции, записанной во втором параметре, как признаком группировки. Список не пересортируются, в группы объединяются данные, идущие подряд. На выходе появляется список туплов (tuples), состоящих из результата работы функции, применительно к группе и списка (точнее объекта _grouper) объединённых результатов:

[(x, list(k)) for x, k in groupby([1,2,3,-4,-1,2], lambda x: x>=0)]

Я тут прошёлся по списку [1, 2, 3, -4, -1, 2], объединяя его значения при помощи функции x>=0. Т.е. все подряд идущие числа с одинаковым знаком будут сгруппированы. Получится вот что:

[(True, [1, 2, 3]), (False, [-4, -1]), (True, [2])]

сверху на groupby я навесил генератор — по сути, цикл, который проходит по всем элементам groupby, раскладывая тупл (tuple) в два элемента — x и k. Этот цикл для каждой итерации возвращает результат, форма которого записана перед циклом. Это тупл, состоящий из двух элементов: k (где записан результат группировочной функции) я оставил неизменным, а объект _grouper я преобразовал в список.

Функция chain объединяет итератор, для простоты можно пока представить, что она склеивает списки. Конструкция «['']*3» эквивалентна ['', '', ''] — трём пустым строкам. Таким образом, строка «for item, grp in groupby(chain(['']*3, letter, ['']*3), lambda x:x))» делает следующее: по итератору, состоящему из трёх пустых строк, нашего списка letter и ещё трёх пустых строк, проходит функция groupby, которая группирует элементы по самому простому признаку: «lambda x: x», т.е. группируются одинаковые элементы, далее по получившемуся списку туплов проходит цикл, каждый тупл раскладывается на два элемента: item и grp, где из grp делается список (при помощи вызова «list»).

Выше находится конструкция «[ ['', grp] ][item or len(grp)<n]», выглядит она страшно и кажется «птичим языком», но на деле широко применяется в самых разнообразных языках, включая PHP, сейчас я её объясню. Результатом «item or len(grp)<n» будет True или False. Условие, на мой взгляд, очевидное: если строка не пустая или последовательность меньше 3, то True. True и False используются как индекс списка и выбирают, соответственно, первый или нулевой элемент массива, т.е. список, содержащий пустую строку или группу символов.

Цепочка такая:

Иcходная последовательность:
['', '1', '1', '', '', '', '3', '', '5']
Добавили с двух сторон пробельные строки:
['', '', '', '', '1', '1', '', '', '3', '', '5', '', '', '']
Прошли groupby и генератор:
[ ('', ['', '', '', '']), ('1', ['1', '1']), ('', ['', '']), ('3', ['3']), ('', ['']), ('5', ['5']), ('', ['', '', '']) ]
Прошли через условие на птичьем языке:
[ [''], ['1', '1'], ['', ''], ['3'], [''], ['5'], [''] ]

Далее у нас стоит chain, который принимает аргументом всё, что получилось, но с префиксом звёздочка. Chain, как я писал выше, объединяет итераторы, для простоты мы считаем, что это, с натяжкой, эквивалентно объединению списков. У нас есть список, содержащий списки. Хорошо бы эти списки склеить.

В PHP есть функция call_user_func_array, которая принимает на вход два параметра: фукнцию, которую надо вызывать и массив её параметров. В PHP я бы склеил эти списки вот так: call_user_func_array('array_merge', $letter). В Python делается всё то же самое, только синтаксис этого вызова другой: func(*args). Это означает, что когда будет вызвана функция func, список args станет списком её аргументов, chain принимает на вход любое количество элементов.

Таким образом мы получим вызов
chain(*[ [''], ['1', '1'], ['', ''], ['3'], [''], ['5'], [''] ]), что эквивалентно
chain([''], ['1', '1'], ['', ''], ['3'], [''], ['5'], ['']). На выходе мы получим следующий итератор, который как список выглядит вот так:
['', '1', '1', '', '', '3', '', '5', ''].

Легко заметить, что какое бы количество пробельных строк не было по краям, в конце всегда остаётся по одной. От них нам нужно избавиться. Делается это просто, в Python есть срезы, которыми я и пользуюсь: список[1: -1] означает «вернуть список с первого элемента, убрав ещё один с конца». В PHP это будет array_slice($list, 1, -1).

В итоге ['', '1', '1', '', '', '3', '', '5', ''][1:-1] даст нам искомое: ['1', '1', '', '', '3', '', '5'].

Мда. Что-то как-то не очень всё просто получилось :)
13 комментариев
19 сентября 2008 18:50

Спираль истории: @font-face

Когда на земле было всего два браузера — IE и Netscape Navigator, они оба поддерживали разные виды внедряемых шрифтов. IE поддерживал своё, но бесплатное решение, Netscape использовал платное решение от BitStream. В те времена вебмастера интересовались возможностями браузеров ещё меньше, чем сейчас и этой возможностью мало кто пользовался.

Хотя в Татарстане, где я жил, технологию внедряемых шрифтов использовали очень часто для поддержки татарского алфавита на сайтах.

Суть технологии очень проста: шрифт подключается как любой внешний ресурс (другие примеры — графика, CSS, JS) и им можно писать что-то на странице. Netscape помер, а решение Microsoft всё-таки вошло в CSS, хотя и в изменённом виде.

На данный момент, оказывается, во всех браузерах, включая альтернативный, на какой-то стадии есть поддержка внедрения шрифтов (@font-face): Хочу сразу сказать почему я считаю, что у IE наиболее удачное решение: разработчик может указать какие именно знаки оставить в шрифте, что даёт огромную экономию по размеру. Другие же браузеры поддерживают шрифты в форматах TrueTypeFont и OpenType. Легко представить к чему это приведёт: подключать будут гигантские Unicode-шрифты, содержащие всё что только возможно, вплоть до вымерших алфавитов.
17 комментариев
19 сентября 2008 15:31

За что я люблю свою жену

Диалог по SMS:

— не обижайтесь на своего мужа, он хороший. Аноним
— аноним, я на мужа не обижаюсь. но пусть к тому времени, когда надумает ребёнка, сделает куклу в свой рост, чтобы быть иногда с малышом. :)

За что ещё (кроме всего прочего) люблю жену — за то, что она может привести сильные и разумные аргументы, при этом без всякого давления.

P.S. Ссылка от Кости Коломейца: картонный папа.
1 комментарий
18 сентября 2008 19:13

PHP: мануалы читать необходимо

Я изучаю новый язык следующим образом: сначала всегда полностью читаю руководство (вот с «Пайтоном» мне не повезло — в руководстве, которое я выбрал, об itertools было упомянуто вскользь), потом уже пробую что-то писать, изредка показывая куски кода для критики.

Конечно, руководство полностью наизусть я не помню, но зато всегда помню, что «что-то где-то было», это частно избавляет от изобретения велосипедов.

В свежей статье на «Хабре» — один из подобных велосипедов:
function uptime() {
	$fp=@popen('uptime','r');
	$s=@fgets($fp);
	@fclose($fp);

	@preg_match('#load average: ([0-9\.]+)#', $s, $m);
	return $m[1];
};
Во-первых, в PHP есть функция sys_getloadavg, во-вторых, (если версия PHP ниже 5.1.3) эту информацию можно добыть значительно проще: «(float) substr(strstr(`uptime`, "load average:"), 13)», в третих, можно (на Линуксах) читать /proc/loadavg.

В этой статье вообще масса забавного: в разделе «временные припарки», где index.php заменили для снятия нагрузки, для чего-то использовался print, вместо того, чтобы выдать сообщение голым html, memcached используется как база данных и так далее.
8 комментариев
18 сентября 2008 13:46

Разум проснулся: Python itertools

С подсказки Вани в комментариях к предыдущему посту посмотрел itertools. И вот что я вам скажу товарищи. Если за что-то и любить «Пайтон», то за итераторы и генераторы (а ещё за суперпозицию функций, но это отдельная тема), а переписанный код из предыдущего поста выглядит теперь вот так:
from itertools import groupby, chain,  starmap

def ntrim(letter, n=3, replby=1):
	return list(chain(
			*starmap(
				lambda item, grp: [grp, [''] * replby][item and len(grp)>=n],
				((item, list(grp)) for item, grp in
					groupby(chain(['']*n, letter, ['']*n), lambda x:x)
				)
			)
		))[replby:-replby]
Я модифицировал исходную задачу: последовательности из трёх и более пустых строк заменяются на одну, пустые элементы в начале и конце списка удаляются. И ещё я прибил ленивый триплет из Python 2.5, потому что на домашнем ноуте у меня 2.5, а на рабочем сервере будет 2.4. Надо, кстати, будет посмотреть не цепляется ли он как-то через __future__. Завтра посмотрю, сейчас — спать.
13 комментариев
17 сентября 2008 00:23

Сон разума: снится Python

Вот что родил мой мозг в текущем проекте:
def trim(letter):
    SKIPLEAD = object()
    SKIPINNE = object()

    todelete, stage, skipped = [], SKIPLEAD, 0

    for idx, blank in enumerate([x == '' for x in letter]):
        if stage == SKIPLEAD:
            if blank:
                todelete.append(idx)
            else:
                stage = SKIPINNE
        else:
            if blank:
                skipped += 1
                todelete.append(idx)
            else:
                if 0 < skipped < 3:
                     todelete = todelete[0:-skipped]
    return [letter[idx] for idx in set(range(0, len(letter))) - set(todelete)]
20 комментариев
16 сентября 2008 17:16

Питон — это такая змея

Меня удивляет, что некоторые программисты со рвением называют язык программирования «Пайтон» «Питоном». Безотносительно истории происхождения названия, должен заметить, что названия не переводятся. Ruby никто не называет «Рубином», язык «Си» языком «Вэ», а SmallTalk «Непринуждённой Беседой».

Python — это «Пайтон».
60 комментариев
16 сентября 2008 13:01

Расширение RSS: «Яндекс», поиск по блогам

«Яндекс» выпустил специальный RSS-плагин к популярным PHP-форумам для сервиса «Поиск по блогам». Автор — известный PHP-программист Дима Смирнов. На медленно стухающей «Хабре» немедленно появились те, кто «не читал, но обсуждают».

В чём же заключается смысл плагина? Поскольку я в курсе, давайте расскажу.

Сервис поиска по блогам «Яндекса», как вытекает из названия, индексирует блоги и многим кажется, что на этом его отличие от основного поиска заканчивается. Как бы не так! Поисковик «Яндекса» индексирует HTML-страницы, поисковик поиска по блогам индексирует RSS, а в RSS сайты отдают какое-то небольшое количество последних постов.

Проблема, как несложно догадаться, в том, что существую сайты, которые старше сервиса, а так же такие, до которых поисковик добрался недавно. Львиную часть их содержимого индексатор поиска по блогам не видит — из RSS они давным-давно пропали.

Дима с командой поиска по блогам придумали очень простую и эффективную штуку. RSS — формат расширяемый, для него уже масса расширений, это нормальная практика. Если заглянуть в RSS любого диминого сайта, то можно увидеть, что в нём упоминается новое расширение: «ya:more», т.е. новый тег «more» в пространстве имён «ya».

Для того, чтобы его использовать, в тег «rss» нужно дописать строку «xmlns:ya="http://blogs.yandex.ru/"» и вставить тег «ya:more», содержащий URL, указывающий на RSS, содержащий предыдущие N постов:
<rss xmlns:ya="http://blogs.yandex.ru/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" version="2.0">
<ya:more>http://coprophagous.ru/rss/posts/740</ya:more>
Получается много связанных этим тегом документов, в последнем из которых тег «ya:more» отсутствует. Таким образом индексатор поиска по блогам может пройтись по всем постам блога и проиндексировать их. СЕОшники должны оценить.

Заслуга Димы заключается не только в том, что он придумал простое решение проблемы и мотивировал команду поиска реализовать её, но ещё и в том, что он написал готовые плагины, которые добавляют эту функциональность для трёх популярных форумов на языке PHP: PHPBB, vBulletin и IP.Board.

Кроме этого, формат позволяет индексировать и комментарии к записям, в том числе и древовидные. Подробнее об этом можно прочитать в файле readme.html, который находится внутри архива с плагинами.
16 комментариев
15 сентября 2008 23:21

То, что мешало плохому танцору, теперь у музыканта

тестигитара (46.46КБ)

Не знаю что это, но выглядит забавно. Нашлось поиском в «Яндексе» по какому-то из запросов, исходный сайт не отвечал.
5 комментариев
15 сентября 2008 00:28

The reports of my deatch are greatly exaggerated.

Пока я праздновал свой день рождения ЖЖисты из сообщества «kazan», модератором коего я являюсь, похоронили президента республики Татарстан Минтимера Шаймиева. Якобы президент умер в Кемере, где отдыхал с семьёй.

Главную «виновницу» переполоха, которая первой поделилась новостью, уже вызывали в «куда следует». Центральные издания информацию опровергают, а в качестве источника сведений о кончине президента цитируют, в основном, ЖЖ Ирека Муртазина, который поделился новостью позднее, но зато является заметной фигурой в официальных кругах. Хотя кое-кто уже цитирует официальный некролог одной из казанских газет.

Теорий у народа на этот счёт две: президент умер или кто-то намеренно распускает «дезу», чтобы сыграть на падении акций предприятий Татарстана.
9 комментариев
13 сентября 2008 22:55

Проводит ночи 31-я весна

Ну вот мне и 31 год. Формально это случится в 6:30 утра, но, думаю, уже можно поздравлять.

За прошедший год я успел:
35 комментариев
13 сентября 2008 00:10

Если бы программистов звали элохим

Когда я слышу «если бы программисты строили дома, то первый же дятел разрушил бы цивилизацию», мне хочется сказать:

Посмотри на себя! Одна нога короче другой, один глаз меньше, позвоночник плохо приспособлен к вертикальным нагрузкам, не говоря об ударных, одна трубка для пищи и воздуха, нижние рёбра имеют незакреплённые концы! Зачем тебе соски? Почему ногти защищают ту сторону пальцев, которая меньше всего контактирует с окружающей действительностью? Плохо с балансом: стоит чем-то не попользоваться с месяц и оно атрофируется, не говоря уже о мелких багах, как персональных (чихание на яркий свет), так и общих (слепое пятно в глазу). Кстати, запакован человек неплохо, вот только распаковывается мучительно долго.
19 комментариев
12 сентября 2008 12:51

Бесполезное: переводим строку в десятичные коды

Недавно решил попристальнее посмотреть что нового появилось в CMD (язык пакетных файлов Windows) со времён Windows NT. Написал небольшую программу, которая переводит строку в десятичные коды. Мне показалось, что CMD стал ещё более нелогичным чем был. Например, я так и не догадался как решить все проблемы с кавычками. Какое бы решение я не придумывал для того, чтобы проверить входной параметр, я тут же придумывал решение для того, чтобы разрушить код.

В итоге, исключить в программе влияние кавычек я не смог. Если у кого-то получится, расскажите как это делается в CMD. Вот программа:
@ECHO OFF
REM BOLK

REM Вспомогательная процедура для прохождения по массиву
IF NOT "%2"=="" GOTO :LINE

SETLOCAL
REM Входной параметр
SET LINE="%~1"
ECHO In: %LINE%

REM Сюда попадёт результат из ONE
SET OUT=""
REM Переменная останова вспомогательной процедуры
SET LINEBREAK=0
REM Сюда мы поместим результат и выведем его
SET RESULT=
CALL :ONE

REM ECHO Out: %OUT%
REM GOTO :EOF

REM Цикл прохода для DEBUG, который даст нам кучу переменных с шестнадцатеричными
REM значениями в каждой
(ECHO E100 %OUT% 0 && ECHO D100 && ECHO Q) | ^
FOR /F "usebackq tokens=2-15 skip=2 delims=- " %%a IN (`DEBUG`) DO ^
@CALL %0 %%a %%b %%c %%d %%e %%f %%g %%h %%i %%j %%k %%l %%m %%n

GOTO :EOF

: Процедура осторожного разбора входной строки, разбираем так,
: чтобы спецсимволы не попали в код
:ONE
SET L="%LINE:~-2,1%"

REM Это для DEBUG — нужно чтобы заэкранировалась кавычка
IF ^%L:~1,-1%==^" (
  SET OUT="""%OUT:~1,-1%
) ELSE (
  SET OUT="%L:~1,-1%%OUT:~1,-1%"
)

SET LINE="%LINE:~1,-2%"

IF NOT %LINE%=="" GOTO ONE
GOTO :EOF

: Процедура для прохода по массиву. Так как вызывается через pipe,
: тормозить придётся самостоятельно — родительский процесс ничего отсюда
: не получает
:LINE
IF %LINEBREAK%==1 GOTO :EOF
IF "%1"=="00" (SET LINEBREAK=1 && ECHO %RESULT% && GOTO :EOF)
CALL :CHAR %1
SHIFT
GOTO LINE

: Победа разума над CMD — преобразование hex->dec
:CHAR
SET /A RES=0x%1
IF DEFINED RESULT (SET RESULT=%RESULT% %RES%) ELSE SET RESULT=%RES%
8 комментариев
12 сентября 2008 10:24