951 заметка с тегом

программирование

Как я чужие тексты своровал

Ночью не спалось и, чтобы чем-то себя занять, сидел и прогонял тексты своего блога через алгоритмы кластеризации. В какой-то момент случайно наткнулся на небольшой кластер, где все тексты были на английском. Это меня очень удивило, как ни старался, никак не мог вспомнить, чтобы когда-либо пытался вести блог на другом языке.

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

Чужой текст за моим авторством, сконвертированный из блога на движке «Регистр»

Тут нужна небольшая историческая справка. Мой блог — довольно разношёрстное собрание текстов, собранных сейчас в единую ленту, это бумажные дневники, ранний сайт, блог на движке «Регистр», блог на самописном движке, и наконец «Эгея», на которую я перешёл года полтора назад.

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

Так выглядел изнутри импортированный в ленту моего блога текст Димы Смирнова

Чтобы понять откуда у меня чужие тексты, заглянул в резервную копию блога того времени, благо она у меня кочует с ноутбука на ноутбук в составе остального барахла. А ларчик просто открывался…

«Регистр» в своё время был ещё и РСС-читалкой других блогов на том же движке — у него была публичная страница, на которую они подключались. Тексты из других блогов валились в общую текстовую базу, отличаясь от общей массы лишь одним — наличием значения в поле «source». Я этой страницей почти не пользовался, но 34 чужие заметки успели туда попасть.

Годы спустя, когда я писал конвертор для преобразования блога в новый формат, это уже всё забылось, а поскольку, просматривая основную массу сохранённых заметок, этот тег я не увидел, то сконвертировал всё, что было в базе, вместе с чужими текстами.

Сегодня ночью эту несправедливость удалось исправить — написал по-быстрому парсер резервной копии и удалил чужие заметки.

 1 комментарий    151   2 дн   программирование

Переход на PHP 7.4

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

Во-первых, в недокументированной функции net_get_interfaces появилось новое поле — состояние сетевого интерфейса, в одном месте у нас вышло предупреждение в коде из-за этого;

Во-вторых, часть функций для работы с массивами (например, key, pos и некоторые другие), которые раньше работали с объектами класса ArrayObject внезапно перестали работать:

$arr = new \ArrayObject(['key' => 'value']);
// теперь выдаёт NULL, а не «key»
var_dump(key($arr));

Маловероятно, что кто-то ещё столкнётся с этим несовместимостями — всё-таки они очень специфичные, но всё же имейте ввиду.

Добавлено позднее: однако я неправ — про прекращение поддержки объектом класса ArrayObject функций работы с массивами вскользь всё же упомянуто.

 Нет комментариев    162   4 дн   php   php7   программирование

99 бутылок: ElanTele

70. ElanTele («змеиный язык», видимо намёк на Пайтон) — язык программирования, интерпретатор которого позволяет писать как на английском, так и на татарском. Насколько я понял, это какой-то студенческий проект. Возможностей в языке немного, нет даже способа присоединить число к строке, поэтому пришлось писать функцию преобразования типов самостоятельно.

Обычные конструкции на месте — ветвление, два цикла, определение функции, простенький ввод-вывод, массивы, числа и строки. Думаю, несмотря на незнакомый многим татарский, всё вполне узнаваемо.

// beer.et by Evgeny Stepanischev
вар бөтеннәрюлы := функция(ә) бу
    вар саннар := ["0","1","2","3","4","5","6","7","8","9"]
    вар олыара := ә/10
    вар нәтиҗә := саннар[ә - олыара*10 + 1]

    әгәр ә > 9 буочракта
        нәтиҗә := саннар[олыара + 1] + нәтиҗә
    бетте

    кайтар нәтиҗә
бетте

вар форматлау := функция(шешәләр) бу
    вар нәтиҗә

    әгәр шешәләр = 0 буочракта
        нәтиҗә := "no bottles"
    башкаочракта
        әгәр шешәләр = 1 буочракта
            нәтиҗә := "1 bottle"
        башкаочракта
            нәтиҗә := бөтеннәрюлы(шешәләр) + " bottles"
        бетте
    бетте

    кайтар нәтиҗә + " of beer"
бетте

вар ә, юл

дәвамында ә эчендә 1..99 элмәк
    юл := форматлау(100-ә)
    яз юл + " of beer on the wall, " + юл + " of beer."
    яз "Take one down and pass it around, " + форматлау(99-ә) + " on the wall.\n"
бетте

яз "No more bottles of beer on the wall, no more bottles of beer."
яз "Go to the store and buy some more, 99 bottles of beer on the wall."

Естественно сначала я написал программу на английском, потом перевёл её на татарский, руководствуясь примером в репозитории проекта и файлом расцветки для редактора, после чего обратился к своему хорошему другу А. Х., который помог перевести на татарский имена переменных и функций.

Расцветка синтаксиса программы редактором «СаблаймТекст 3»

Было очень интересно попробовать программировать, не зная язык, который лежит в основе — из всех татарских слов в программе мне понятны «бу» («это»), «функция», «бетте» («всё»), ну может ещё «форматлау» (что-то с форматированием). Теперь я отчасти понимаю плач на информатике той части моих согруппников, которые в школе учили немецкий или французский.

 1 комментарий    387   13 дн   99   программирование

PythonFuck: разбор

Как я писал в конце прошлого года, есть такое развлечение — не используя алфовитно-цифровые символы, писать осмысленные программы на разных языках программирования. Для конкретного языка это называется «имя языка»+«Fuck». В прошлый раз я попробовал провернуть это на «Пайтоне». Я не в курсе, пытался ли кто-то такое сделать раньше, даже не гуглил, хотелось избежать влияния чужих наработок.

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

_=(''<())+(''<());__=_**_**_
___='%'+('%'+`'±'`[_])%(__-_-_)

___*(_*_*_-_)%((
(__+_)*_*_,
`{_}`[_/_],
)+(`(''<'')`[_],)*_+\
(
__*_*_*_-__-_/_,
__*_+_/_,
))

Пришло время разобрать как она работает. Я собирался сделать написать разбор через неделю после её написания, но заботы конца года не позволили.

С самого начала было очевидно, что в указанных ограничениях Пайтон не позволит вызвать какой-либо метод по имени, которое записано в переменной. Если попробовать, получится что-то похожее вот на это:

module, method = "__builtin__", "print"
vars(vars()[module])[method]("Hello")

Есть варианты, но без использования букв — никак.

Так как в силу вышеизложенного функцию печати не вызовешь, пришлось довольствоваться выражением в консоли интерпретатора — там результат выводится на экран автоматически. В остальном, как будет видно ниже, удалось обойтись теми конструкциями, которые есть в языке.

Посмотрим в код. Первая строка:

_=(''<())+(''<());__=_**_**_

Тут два разных выражения, разделённых точкой с запятой. В первом в скобках я дважды сравниваю строку и кортеж, получая два True. Второй Пайтон позволяет сравнивать разные встроенные типы, возвращая результат сравнения их названий, записанных как строки. Так как строка «str» меньше «tuple», то результат — истина.

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

_=-~(''<()) # число «2» можно получить и так тоже

Результат мы кладём в переменную с именем «подчёркивание». В той же строке мы получаем «16» возводя два в степень два в степени два, результат помещается в переменную «два подчёркивания».

Вторая строка:

___='%'+('%'+`'±'`[_])%(__-_-_)

Тут используется символ «±», он не алфавитно-цифровой, хоть и за пределами однобайтной таблицы, так что правила не нарушаются.

Этот символ вставлен внутрь конструкции «обратный апостроф», которая является во втором Пайтоне эквивалентом repr — функции, возвращающей строковое представление объекта, в данном случае это будет «’\\xc2\\xb1’». Нам отсюда нужен второй символ (нумерация с нуля) — «x». Цифра два для индекса у нас уже есть — в переменной «_».

Дальше используется оператор «процент» — форматированный вывод. Полученное «x» используется как шаблон для «процента», чтобы преобразовать число в шестнадцатеричный вид. Из выражения «__-_-_» (16−2−2) получаем «12» или, после преобразования, «c».

Вообще можно было обойтись без этого шага, получив эту же букву из строкового представления «±» («’\\xc2\\xb1’»), но в процессе продумывания я себе выписал ряд трюков и мне хотелось использовать их все.

В итоге, в переменную «три подчёркивания» попадает строка «%с», её мы будем использовать для оператора форматированного вывода («%»), чтобы получать символ по его коду.

Дальше символы получаются следующим образом:

(__+_)*_*_ # (16+2)*2*2 = 72, код символа «H», через шаблон «%c» получим отсюда букву
`{_}`[_/_] # первая часть даёт строку «set([2])», отсюда берём символ «e»
`(''<'')`[_]*_ # получаем строку «False», оттуда символ «l», удваиваем его умножением на два
__*_*_*_-__-_/_ # 16*2*2*2-16-2/2 = 111, код символа «о»
__*_+_/_ # 16*2+2/2 = 33, код символа «!»

Не требующее преобразования оставляем как есть, а из кодов символы получаем шаблоном «%с». В результате складывается строка «Hello!».

Второй, а не третий Пайтон нужен, в основном, из-за двух вещей — оператора «обратный апостроф» и двух типов строк, в одном из них юникодные символы отображаются в кодированном представлении, в третьей версии языка ото всего этого избавились.

 Нет комментариев    306   16 дн   *fuck   python   программирование

Дескрипторы файлов и PHP

На днях подошёл на работе один из программистов и спросил не знаю ли я способа как передать из ПХП во входной поток утилиты не один файл, а два. Когда мы разобрались, что он имеет ввиду, оказалось, что задача следующая: имеется утилита командной строки, которая делает PDF из двух файлов — тела и заголовочной части. Нужно работать с ней из ПХП, но создавать промежуточные файлы не хочется.

Один файл передаётся во входной поток, с этим многие знакомы и проблемы нет, но как передать два файла? В командной строке такой способ есть — создаём ещё один файловый дескриптор, связываем его с файлом или вводом/выводом какой-то команды и радуемся. Например:

#!/bin/bash
# связываем третий дескриптор с чтением из указанного файла
exec 3< /etc/passwd
# связываем четвёртый со входом команды, которая будет писать в другой файл
exec 4> >(cat > /tmp/passwd)
# читаем из третьего дескриптора, пишем в четвёртый
cat <&3 >&4

А можно ли так в ПХП? Документация к proc_open говорит, что да:

The file descriptor numbers are not limited to 0, 1 and 2 — you may specify any valid file descriptor number and it will be passed to the child process. This allows your script to interoperate with other scripts that run as «co-processes».

Ну что же, давайте попробуем:

<?php
// заполняем первые три файловых дескриптора как обычно и,
// кое-что новое, — создаём ещё один
$ds = [
    'stdin'  => ['file', '/dev/null', 'r',],
    'stdout' => ['pipe', 'w',],
    'stderr' => ['file', '/dev/null', 'w',],
    'stdnew' => ['pipe', 'r',],
];

// передаём наши дескрипторы команде ???, специальный файл
// /???/??/3 связан с третьим (с нуля) дескриптором
$process = proc_open("cat /dev/fd/3", array_values($ds), $pipes);

// связываем дескрипторы с переменными, указанными в массиве $??
// (пожалуйста не используйте ??????? без ?ℎ???? в своём коде)
extract(array_combine(array_intersect_key(array_keys($ds), $pipes), $pipes));

// пишем в наш новый дескриптор
fwrite($stdnew, "Hello world!\n");
fclose($stdnew);

// читаем из вывода переданное
fpassthru($stdout);
fclose($stdout);

proc_close($process);

Всё работает! Фраза, переданная на такой не совсем стандартный вход, благополучно выводится на экран.

Должен сразу сказать — не все утилиты командной строки умеют работать таким образом, некоторые требуют указания настоящего файла, не знаю из каких соображений. Но с большинством это сделать получится.

 1 комментарий    127   28 дн   php   программирование

PythonFuck

Давайте вспомним, что у меня блог ещё и про программирование.

Есть такое развлечение — не используя алфовитно-цифровые символы, писать осмысленные программы на разных языках программирования. Для конкретного языка это называется «имя языка»+«Fuck». Меня это занятие не то чтобы захватило, но иногда интересно поразмяться и попробовать себя в этой забаве.

Для некоторых языков такого ещё никто не делал, приходится изобретать собственные приёмы, в конечном счете это интереснее всего. Я уже делал PHPFuck, BashFuck (и даже не один раз) и совмещал JavaScriptFuck с языком программирования Brainfuck.

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

Долго мне казалось, что с Пайтоном ничего такого не сделать и я даже пытался, но не получилось. А вчера, пока делал для студентов задачу-завлекалку на день карьеры, решил сделать очередной «подход к снаряду».

_=(''<())+(''<());__=_**_**_
___='%'+('%'+`'±'`[_])%(__-_-_)

___*(_*_*_-_)%((
(__+_)*_*_,
`{_}`[_/_],
)+(`(''<'')`[_],)*_+\
(
__*_*_*_-__-_/_,
__*_+_/_,
))

Это запускается и работает (выводит строку «Hello!»), но только из командной строки и только во второй версии Пайтона. Когда напишу небольшой разбор кода, станет понятным почему.

 Нет комментариев    52   1 мес   *fuck   python   программирование

99 бутылок: Zephir

69. Zephir — язык программирования, созданный для написания модулей расширения PHP и являющийся его сателлитом. Текущая версия (0.12.2) содержит несколько багов, с которыми пришлось столкнуться при написании программы. К счастью, перед компиляцией «Зефир» транслируется в Си, поэтому разобраться в происходящем труда не стоило.

Язык со сборкой мусора, использует как статическую, так и динамическую типизацию. Последняя необходима, например, для сохранения результата вызова пользовательских методов, похоже транслятор пока не умеет опираться на подсказку возвращаемого типа, поэтому требует в таких местах динамическую типизацию.

По синтаксису «Зефир» явный наследник ПХП, но выглядит почище — без «долларов» в именах переменных (которые многих раздражают) и скобок в управляющих конструкциях (из-за чего напоминает мне Гоу). Процедурное прошлое здесь почти не видно — многие функции на типами имеют объектные эквиваленты.

К сожалению, реализация языка пока сыровата — на первые баги я наткнулся в первые десять минут программирования, а сообщения об ошибке синтаксиса предельно лаконичны — показывается только строка, в чём ошибка заключается приходится разбираться самостоятельно.

«Песня о пиве» на «Зефире» выглядит следующим образом:

// Beer song by Evgeny Stepanischev
// song.zep
namespace Beer;

class Song
{
	private static function format(int bottles) -> string
	{
		string result = "";

		switch bottles {
			case 0:
				let result = "No bottles";
				break;
			case 1:
				let result = "1 bottle";
				break;
			default:
				let result = "%d bottles"->format(bottles);
		}

		return result . " of beer";
	}


    public static function sing() -> void
    {
    	int bottles = 99;
    	var botStr = self::format(bottles);

        while bottles > 1 {
        	echo "%s of beer on the wall, %1$s of beer.\n"->format(botStr);

        	let bottles--;
        	let botStr = self::format(bottles);

        	echo "Take one down and pass it around, %s on the wall.\n\n"->format(botStr);
        }

        echo "No more bottles of beer on the wall, no more bottles of beer.\n",
        	"Go to the store and buy some more, 99 bottles of beer on the wall.\n";
    }

}

Поскольку язык используется для написания расширений, а не самостоятельных программ, написанному выше надо помочь запуститься:

zephir build
php -d extension=beer.so -r 'Beer\Song::sing();'
 Нет комментариев    33   4 мес   99   программирование

PHP: доступ к приватным свойствам

Кинули ссылку на один из способов получения доступа к приватным свойствам в ПХП, тут создаётся функция, которая вызывается в контексте объекта с закрытым свойством:

function inspect_closure(object $o, string $p)
{
    return (function () use ($p) {
        return $this->$p;
    })->call($o);
}

Я решил вспомнить сколько таких способов вообще существует в ПХП.

Сначала, очевидно, на ум должен прийти рефлекшн, ведь он для этого и предназначен:

function inspect_reflection(object $o, string $p)
{
	$refClass = new \ReflectionClass(get_class($o));
	$refProp = $refClass->getProperty($p);
	$refProp->setAccessible(true);

	return $refProp->getValue($o);
}

Теперь экзотика. Например, мне вспоминается доступ через преобразование в массив, я о нём уже однажды писал. При преобразовании объекта в массив его приватные и защищённые свойства видны по специальным именам в полном соответствии с документацией:

function inspect_array(object $o, string $p)
{
    return ((array) $o)[
    	sprintf("\0%s\0%s", get_class($o), $p)
    ];
}

Последний способ самый спорный. Не работает с анонимными классами и объектами, в свойствах которого лежат анонимные функции. А ещё от него легко защититься, если закрыть магический метод, отвечающий за сериализацию.

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

function inspect_serialize(object $o, string $p)
{
	$class = get_class($o);
	$serialized = serialize($o);

	if (preg_match("/s:\d+:\"\\0{$class}\\0{$p}\";([^;]+;)/s", $serialized, $m)) {
		return unserialize($m[1]);
	}

	throw new \UnexpectedValueException('Something went wrong.');
}

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

 Нет комментариев    9   4 мес   php   программирование

HA

Сначала немного не по теме.

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

dd if=/dev/zero of=floppy.img bs=1024 count=1440
diskutil eraseVolume MS-DOS VOLUME `hdiutil attach -nomount floppy.img`
hdiutil attach floppy.img

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

Как справедливо напомнили читатели в комментариях к моему эксперименту c ARJ, в 90-е чемпионом по сжатию был архиватор HA (с алгоритмом PPM). Он был очень медленным по тем временам (помню DOOM сжимался за часы) и поэтому не слишком распространённым, но я с ним сталкивался.

Успехи архиватора HA — 23,7% исходного объёма

Для тестирования я выбрал форк архиватора, с которым сталкивался во времена интереса к языку программирования Си-​-. Форк быстрее оригинала в 1,5 раза, впрочем, как оказалось, сейчас это вряд ли имеет значение — сжатие файла из вчерашнего теста заняло малозаметное время.

214962-байтный файл сжался в 51088 байт, это 23,8% от исходного объёма. Это лучше современного xz с его 25,4 процентами, но по-прежнему хуже архиватора Compressia с его космическими 20,6%!

 3 комментария    9   4 мес   ha   программирование

ARJ

Что-то меня потянуло на разную околокомпьютерную древность.

Пока экспериментировал с арифметическим кодированием, как-то сами собой вспомнились различные архиваторы времён ДОСа, но больше всего почему-то ARJ, который в «Иркеннефти», где я иногда в летние каникулы работал в детстве, называли «а-эр-йот».

Один из рассказов Анджея Земянского, сжатый «а-эр-йодом» в экране «Волкова Коммандера»

Захотелось его сравнить с чем-нибудь современным.

Скачал виртуальную машину с MS-DOS 6.22, установил туда ARJ 2.21 (это версия сентября 1991 года) и попробовал посмотреть насколько уменьшится один из рассказов Анджея Земянского — первый попавшийся текстовый файл, который был у меня на компьютере.

В итоге файл размером 214962 байт ужался до 67634, это 31,5% исходного объёма. Современный архиватор xz 5.4.2 на максималках (использует алгоритмы LZMA и LZMA2) ужал тот же файл до 54648 байт, это 25,4% исходного объёма. Вроде неплохой прогресс.

Архиватор 2003-го года уделывает современный xz, как Тузик грелку

Но потом я вспомнил, что где-то в сети существует таблица сравнения архиваторов за 2002-й год. Победителя нагуглить не удалось, зато архиватор «Compressia», который находится строчкой ниже, нашёлся на «ВебАрхиве». Он смог сжать тот же файл до 44361 байт, что составляет 20,6% исходного файла. А это архиватор 2003-го года (я скачал последнюю версию — 1.0b)!

Всё как обычно — есть популярные продукты, а есть почти неизвестные программы, которые в тёмном углу пилят какие-то одиночки. Любопытно — как обстоят дела в этом пыльном углу в этом году, каких успехов добились такие малоизвестные архиваторы?

 8 комментариев    10   4 мес   arj   программирование
Ранее Ctrl + ↓