программирование
«Странный» JS
В ДжаваСкрипте полно странностей, да, но популярная картинка, которая получила сейчас широкое хождение (не буду её приводить, просто суть перепишу) имеет отношение только к безграмотности её авторов. Суть такова:
[] + [] // массив + массив
"" // результат — пустая строка
[] + {} // массив + объект
"[object Object]" // В результате получаем объект? Ну ок
{} + [] // объект + массив
0 // 0? Реально?
{} + {} // объект + объект
NaN // Not a Number? WAT?Выглядит очень всё ооочень странно, если думать, что происходит именно то, что написано в коментариях справа. На самом деле происходящее неверно интерпретировано авторами.
В ДжаваСкрипте фигурные скобки, помимо объекта, задают ещё и блок кода. В данном случае все «объекты» слева — на самом деле блоки кода. Откроем консоль и попробуем это доказать:

Как видите, в левый блок можно без труда добавить код и станет очевидно, что это не описание объекта. Так два последних обсуждаемых примера распадаются на две независимые команды — определение пустого блока кода, который ничего не выполнит и унарную операция «плюс» над пустым объектом или массивом.
С массивом, возвращающим пустоту при суммировании (конкатенации) всё ещё проще — в данном случае производится попытка вызвать метод toString, который у массива выводит строку его значений через запятую. Так как значений нет, строка пустая.
В общем, ничего сильно странного не происходит.
PHP/FI
На ПХП я начинал программировать с третьей версии, предыдущую, вторую, никогда в глаза не видел. Занялся сегодня археологией, что интересно, интерпретатор собрался под «Мак»
По описанию, вторая версия — удивительно бедный язык, я попытался написать подобие функции var_dump (которой тогда в языке не было), описал в комментариях с чем столкнулся. Надо сказать, некоторые вещи очень сильно удивили.
Вообще, я хотел написать одну функцию, а отступ для вывода скаляра передавать вторым параметром, но оказалось,
В общем, пришлось сделать две разные функции.
#!php.cgi -q
<?
# однострочные комментарии только такие, два слеша не работают
/* так описываются функции */
function var_dump_scalar $var, $indent (
if ($indent) {
/* вот так выглядит конкатенация строки — плюс, а не точка */
/* интерполяции нет, а одинарные кавычки вернут код символа в них */
$indent = strtr(sprintf("%' '" + $indent + "d", 0), "0", " ");
/* функции str_repeat тоже нет, приходится вот так выкручиваться */
} else {
$indent = "";
}
/* всего три типа данных, нет булевого типа, нет ресурсов — fopen вернёт число */
/* объектов тоже нет, конечно же */
switch (gettype($var)) {
case "integer"; /* после case точка с запятой, а не двоеточие */
echo "%sint(%d)\n" $indent, $var;
break;
case "string";
echo "%sstring(%d) \"%s\"\n" $indent, strlen($var), $var;
break;
case "double";
echo "%sfloat(%f)\n" $indent, $var;
break;
}
/* можно было бы вернуть всё через return, но мне не удалось бы продемонстрировать несколько вещей */
);
function var_dump $var (
/* смотрим — передан ли параметр */
if (isset($var)) {
/* пытаемся определить — не массив ли это, отдельного типа нет */
$cnt = count($var);
if ($cnt > 1 || key($var) != "0") {
reset($var);
/* echo умеет принимать форматирующую строку */
echo "array(%d) {\n" $cnt;
/* других способов проверять конец массива нет — только итерировать по длине */
/* цикла for в языке тоже нет */
while ($cnt > 0) {
$key = key($var);
echo " [\"%s\"]=>\n" $key;
next($var);
/* тут только скаляры, так как массивов, кроме одномерных не бывает */
var_dump_scalar($var[$key], 2);
$cnt--;
}
echo "}\n";
} else {
var_dump_scalar($var, 0);
}
} else {
echo "Warning: var_dump() expects exactly 1 parameter, 0 given\n";
}
);
/* ассоциативный массив */
$b["aaa"] = "aaa";
/* числовой массив, отдельного типа массива нет, любой тип является нулевым элементом массива */
/* конструкции array(...) тоже нет, массивы задаются только так — в строку */
$a = 1;
$a[1] = 2;
/* двухмерных массивов у нас нет, это просто операция слияния массивов */
$a[] = $b;
var_dump($a);
/* Результат работы:
array(3) {
["0"]=>
int(1)
["1"]=>
int(2)
["2"]=>
string(3) "aaa"
}
*/
/* вот так заканчиваются PHP-скрипты, знака вопроса у закрывающего тега нет */
>Но самый сок, конечно, это две функции — ClearStack и SecureVar. Их я не использовал, просто не придумал как. Первая является костылём к парсеру (почитайте описание, у меня нет сил это описывать), вторая — фильтрует переменные из ГЕТ-
Опции просмотрщика PDF в Chrome
Многие, наверное, знают что у плагина Эдоуби для просмотра ПДФ есть специальные параметры, которые передаются фрагментом в урле. Эдоуби публиковала эти параметры в разных документах, а теперь они собраны в специальном ЭрЭфСи под номером 3778.
Я для интереса попробовал эти параметры в «Хроме» (там собственный просмотрщик, поэтому я не удивился бы, если бы они не сработали) и оказалось, что три из них всё же работают:
# увеличение на 50%
http://example.org/sample.pdf#zoom=50
# переход к странице №2
http://example.org/sample.pdf#page=2
# переход к главе «Chapter»
http://example.org/sample.pdf#nameddest=ChapterПодробнее можно прочитать ЭрЭфСи (в частности, в «zoom» можно указать ещё два параметра). Так же параметры можно комбинировать, это тоже работает — если указать их через амперсанд.
JBIG2
У нас в документообороте всё больше используется формат ПДФ, но с ним есть проблема — документы из некоторых сторонних организаций к нашим клиентам приходят на бумаге, по факсу
Пока мы такие документы помещаем в ПДФ сканами в хорошем качестве в формате ДжПЕГ. Увы, это довольно расточительно. К счастью, ПДФ поддерживает не один, а целых три формата графики — ДжПЕГ, ДжПЕГ 2000 и ДжБИГ2. На последний я возлагал большие надежды.
Идея этого формата очень интересна и проста. Если у нас есть скан документа, его значимую часть в основном занимают буквы и цифры, если найти при помощи распознавания одинаковые символы, то их можно закодировать по одному разу, а в остальных местах использовать просто ссылки на них. Текстовые сканы действительно ужимаются значительно (я пробовал), тем более, что формат двухбитный (только

К сожалению, у формата есть серьёзный недостаток, на который я тут же и наткнулся.
Алгоритм устроен так, что он подбирает не просто одинаковые символы, а похожие. Это разумно — принтеры печатают неравномерно
Посмотрите, у меня на скриншоте выше — оригинальный ПДФ (слева) и ПДФ, где графика перекодирована в ДжБИГ2. Как видно при кодировании были перепутаны «п» и «и», «п» и «н». Для слов в документах может это ещё и не смертельно, но для цифр в суммах — беда.
Мне неясно можно ли
Остаётся ещё вариант с ДжПЕГом 2000, но с ним возможно, выигрыш будет столь незначительным, что затраты на конвертацию просто не окупятся.
99 бутылок пива на «Электронике МК85»

56. БЭЙСИК «Электроники МК85». Как я уже сказал, Бейсик (в руководстве он называется «БЭЙСИК») на «МК85» отличается странностями. Например, там фиксированное количество переменных с определёнными (только однобуквенными именами), причём только одна строковая переменная может содержать не более 30 символов (её имя — символ доллара), остальные могут содержать не более семи символов.
Или запись массивов — все числовые переменные, по сути, являют собой один массив. Индекс «один» массива с именем «А» совпадает с переменной «B», а индекс «два» переменной «B» — это переменная «D» и так далее. Так что вы можете использовать
Другая неприятная особенность — небольшой максимальный размер строки и программы и весьма ограниченный её размер.
В общем, представляю вашему вниманию программу «99 бутылок пива на стене», написанную на БЭЙСИКе микрокомпьютера «Электроника
10FOR B=99 TO 1 STEP -1
20A=B:GOSUB90:PRINT" of beer on the wall, ";:GOSUB90:PRINT"."
30PRINT"Take one down and pass it around, ";:A=B-1:GOSUB90
40PRINT" of beer on the wall."
50NEXT B
60PRINT"No more bottles of beer on the wall, no more bottles of beer."
70PRINT"Go to the store and buy some more, 99 bottles of beer on the wall."
80END
90GOTO100*(SGN(A-1)+2)
100PRINT"no bottles";:RETURN
200PRINT"1 bottle";:RETURN
300PRINT A;" bottles";:RETURNМикрокомпьютер имеет интересную особенность — длинные строки, не помещающиеся на экране, он автоматически прокручивает, а чтобы человек успел прочитать, после каждой строки происходит остановка интерпретатора, для продолжения надо нажать клавишу «EXE». Точка с запятой после оператора «PRINT» говорит интерпретатору, что строка не кончилась, что позволяет печатать длинные строки несколькими операторами.
Ещё одна особенность — программируемый символ. Один символ, имеющий специальный код можно нарисовать попиксельно. Завтра попробую побаловаться.
Itertools для PHP: продолжение
Библиотеку itertools для ПХП я всё же переписал: теперь на вход всем функциям можно передавать что угодно итерируемое. Из ограничений осталось только два: функция iter в моём варианте принимает только один аргумент, а у функций product и izip_longest последний аргумент является обязательным.
Вот, кстати, как переписывается программка итерстрип, написанная мной семь лет назад на «Пайтоне», теперь её можно повторить на ПХП почти
require 'itertools.php';
use function itertools\groupby, itertools\xrange, itertools\chain, itertools\repeat;
function ntrim($letter, $n=3, $replby=1)
{
$shrink_groups = function($letter, $n) {
foreach (groupby(chain(repeat('', $n), $letter, repeat('', $n))) as list($item, $grp)) {
$grp = iterator_to_array($grp);
yield $item || sizeof($grp) < $n ? $grp : '';
}
};
return array_slice(iterator_to_array(chain(...$shrink_groups($letter, $n))), $replby, -$replby);
}
print_r(ntrim(['', 1, 2, 3, '', '', '', '', 4, '', '']));Увы, на ПХП всё ещё не так компактно
Upsert в PostgreSQL
Ура, появился коммит, добавляющий к оператору INSERT «Постгреса» конструкцию, которая позволяет использовать его как upsert (в «Оракле» это конструкция MERGE, в «Майэскуэеле» — INSERT… ON DUPLICATE KEY UPDATE):
-- Обновить, если не удалась вставка
INSERT INTO distributors AS d (did, dname) VALUES (8, 'Anvil Distribution')
ON CONFLICT (did) DO UPDATE
SET dname = EXCLUDED.dname || ' (formerly ' || d.dname || ')'
WHERE d.zipcode != '21201';
-- Если не удалась вставка, ничего не делать
INSERT INTO distributors (did, dname) VALUES (10, 'Conrad International')
ON CONFLICT (did) WHERE is_active DO NOTHING;Itertools для PHP
Внимание! Библиотеку я модифицировал, описанные ограничения уже не соответствуют действительности!
В прошлом, программируя на «Пайтоне» я не раз восторгался модулем itertools и тем как «Пайтон» легко позволяет обращаться с генераторами — есть несколько способов, хорошее разнообразие.
В ПХП недавно генераторы тоже появились, в версии 5.5 есть ключевое слово yield, позволяющее их создавать. Вчера у меня появилась мысль познакомиться с ними поближе и я решил переписать библиотеку itertools на PHP. В интернете я уже встречал несколько таких попыток, но ни одна из этих реализаций не использовала генераторы.
Пример использования ниже (для запуска примера и работы библиотеки требуется ПХП 5.6 и выше):
require 'itertools.php';
use function itertools\islice, itertools\cycle, itertools\iter;
foreach (islice(cycle(iter("ABC")), 10) as $el) {
echo $el;
}От оригинальной библиотеки несколько отличий.
Если не заброшу библиотеку, то, возможно,
Моя маленькая лепта в PHP7

Читал исходники ПХП7, заметил небольшую ошибку в функции копирования потоков (stream), сегодня мой коммит перенесли в код.
PHP7: scalar hinting
Продолжаю потихоньку наблюдать за развитием ПХП 7. Сегодня с утра выписывал из репозитория и компилировал новую версию, заметил, что в языке появилось указание типов для скаляров — поддерживаются int, float, string и bool.
Выглядит всё довольно естественно:
<?php
class Test
{
public function __toString()
{
return "test";
}
}
function len(string $a): int {
return strlen($a);
}
echo len(new Test);
echo len(new stdClass); // Fatal error: Argument 1 passed to len() must be of the type string, object givenВ данном случае первый вызов ошибку не вызовет (в отличие от второго), так как у объекта есть магический метод __toString, который вызывается, когда объект требуется привести к строке, так что всё логично. Возможные преобразования описаны в документации и ничего нового там нет, всё в соответствии с текущей логикой языка.
Есть и второй режим работы — строгий. Его можно включить при помощи конструкции declare, которая должна быть первой конструкцией языка (теперь у неё приоритет даже над namespace) в файле:
<?php
declare(strict_types=1);
namespace Test;
class Test
{
public function __toString()
{
return "test";
}
}
function len(string $a): int {
return strlen($a);
}
echo len(new Test); // Fatal error: Argument 1 passed to Test\len() must be of the type string, object givenВ этом случае все проверки на тип становятся строгими и никаких неявных преобразований не допускается.
По правде говоря, мне больше импонирует второй режим — несколько лет программирования на «Гоу» и «Пайтоне» позволили оценить все прелести строгой типизации, но существующий код большого продукта привести за приемлемое к строгому режиму будет часто невозможно, на мой взгляд. Видимо поэтому создатели языка ограничили действие строгого режима рамками файла — новые части можно писать, не затрагивая поведение старых, это удобно.
99 бутылок: 𝄢 Cello
55. 𝄢 Cello. Попробовал попрограммировать с использованием библиотеки «libCello» — это набор макросов к Си, вводящих в язык кучу комплексных типов, простую работу с выделением и освобождением памяти под них,
В документации выглядело неплохо, но по факту оказалось, что библиотека недописана, а документация неполная и содержит ошибки. Шаг в сторону и программа вылетает. Например, она падает, если попытаться использовать возвращаемое лямбдой значение.
Пример ошибки в документации — функции в «стиле Си» не могут быть заданы внутри функции main, хотя на сайте указан пример, в котором утверждается обратное (и он не компилируется).
По идее, библиотека должна упрощать программирование, но в моём случае (я понимаю, «песня про пиво» не совсем та программа, в которой раскрывается эта библиотека) всё было ровно наоборот — на чистом Си я справился бы значительно быстрее.
Если бы её дописать и достойно задокументировать, вышло бы
#include "Cello.h"
int main() {
lambda(bottles, args) {
var beer = cast(at(args, 0), Int);
if_eq(beer, $(Int, 0)) {
print("no bottles");
} else {
if_gt(beer, $(Int, 1)) {
print("%d bottles", beer);
} else {
print("1 bottle");
};
}
return None;
};
for (var beer = $(Int, 99); gt(beer, $(Int, 0));) {
call(bottles, beer);
print(" on the wall, ");
call(bottles, beer);
println(".");
print("Take one down and pass it around, ");
sub(beer, $(Int, 1));
call(bottles, beer);
println(" of beer on the wall.\n");
}
println("No more bottles of beer on the wall, no more bottles of beer.");
println("Go to the store and buy some more, 99 bottles of beer on the wall.");
}Судя по репозиторию, автор интерес к своему детищу не потерял, будем надеяться, что
PostgreSQL: сортировка без учёта регистра
Мы переводим свои некоторые продукты с «Оракла» на «Постргрес» и постоянно сталкиваемся
test=# SELECT * FROM (SELECT 'Ба' UNION ALL SELECT 'бб' UNION ALL SELECT 'Бв') _ ORDER BY 1;
?column?
----------
Ба
бб
Бв
(3 строки)Ни в одном описании опыта такого же перехода я
test=# SELECT * FROM (SELECT 'Ба' AS str UNION ALL SELECT 'бб' UNION ALL SELECT 'Бв') _ ORDER BY str COLLATE "C";
str
-----
Ба
Бв
бб
(3 строки)Либо преобразовать строку к бинарному типу:
test=# SELECT * FROM (SELECT 'Ба' AS str UNION ALL SELECT 'бб' UNION ALL SELECT 'Бв') _ ORDER BY str::bytea;
str
-----
Ба
Бв
бб
(3 строки)Правда в последнем случае неясно сможет ли «Постгрес» использовать индекс — мы пока таких испытаний не проводили.
text-decoration-line

Есть такая договорённость в вебе — псевдоссылки (которые похожи на ссылки, но не уводят со страницы) подчёркивать не сплошной чертой, а прерывистой или точечной. Обычно такую черту делают бордюром снизу тега, но если настоящие ссылки сделаны через
Сейчас браузеры начинают поддерживать свойство
Оказалось, в данном случае — доверяй, но проверяй. «Сафари» (по крайней мере восьмая версия)
PHP7: функции → опкоды
Как известно, в ПХП7 некоторые функции будут заменены на опкоды для ускорения работы. Вызов функции — дорогая операция в этом интерпретаторе, а опкоды — дёшевы. Я заглянул в исходные коды (см. функцию zend_try_compile_special_func, если интересно) и нашёл там список функций, которые заменяются в текущей версии «семёрки».
Это strlen, все функции is_* (is_float, is_string и прочие), defined, call_user_func_array, call_user_func и assert.
Goracle
В прошлом году немного рассказывал о взаимодействии языка «Гоу» и «Оракла» — я тогда выбирал из двух драйверов и выбрал в итоге ГОракл. Всё в нём хорошо, да вот незадача — некоторые запросы не выполнялись, падало всё с невнятной ошибкой
Разбирательство привело меня к мысли, что дело в числовых значениях — если запрос возвращал только строки, всё было нормально. Я тогда преобразовал всё при помощи TO_CHAR, разбирая внутри «Гоу» строку в число и успокоился.
На прошлой неделе мне пришлось озаботиться увеличением производительности получившего и разобрался в проблеме чуть детальнее. Не скажу, что понимаю в чём дело (в код драйвера не смотрел), но выглядит всё так, как будто драйвер падает, если на месте числа из «Оракла» может прийти NULL. Например, агрегатные функции (я пробовал сумму и среднее) падения не вызывают. Опять же, если «защитить» все числа функцией NVL — тоже всё хорошо.
Так что если вы видите при использовании этого драйвера ошибки похожие на те, что ниже
ERR=&oracle.Error{Code:22060, Message:"[-1] ORA-22060: argument [2] is an invalid or uninitialized number
ORA-22060: argument [2] is an invalid or uninitialized numberПопробуйте обернуть все числовые возвращаемые значения функцией NVL, должно помочь.
PHP: бесполезная микрооптимизация
Годы назад, когда многие увлекались микрооптимизациями ПХП, была среди них и такая — двойные кавычки заменяли на одинарные, первые были немного медленее. Причина вполне понятна — в строке, окружённой двойными кавычками, разрешена интерполяция — вставка переменных прямо в строку.
Выигрыш был мал, как по мне, но многим и этой разницы было достаточно.
И вот недавно мне кинули ссылку, где утверждается, что много лет назад ситуация поменялась — все виды кавычек обрабатываются с одинаковой скоростью. Я провёл замеры — так и есть, по ссылке не обманули. Я тогда подумал, что с введением в ПХП
Сегодня проверил — примерно так оно и есть (ниже — опкоды ПХП):
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
2 0 E > ADD_STRING ~0 'double+'
1 ADD_VAR ~0 ~0, !0
2 ADD_STRING ~0 ~0, '+quotes'
3 ECHO ~0
4 4 > RETURNНет никакой разницы какой вид кавычек использован (в моём примере — двойные с переменной посередине) — внутреннее представление не различается.
Вывод такой: можно спокойно использовать двойные кавычки везде, кроме мест, где вам важно экранирование — там можно использовать одинарные, если это удобнее.
PHP7: spaceship operator
В ПХП7 вошёл новый оператор — «космический корабль». Я его помню со времён программирования на Перле. Наиболее частое применение он, по всей видимости, найдёт в сортировках:
usort($data, function ($o1, $o2) {
return $o1->value <=> $o2->value;
});Комбинация «меньше», «равно» и «больше» — и есть «космический корабль». Результат его выполнения следующий — если левое меньше, получается
Новый оператор сравнивает даже массивы и объекты, в RFC есть соответствующие примеры:
echo [1, 2, 3] <=> [1, 2, 1]; // 1
echo [1, 2, 3] <=> [1, 2, 4]; // -1
$a = (object) ["a" => "b"];
$b = (object) ["a" => "c"];
echo $a <=> $b; // -1
$a = (object) ["a" => "c"];
$b = (object) ["a" => "b"];
echo $a <=> $b; // 1
// only values are compared
$a = (object) ["a" => "b"];
$b = (object) ["b" => "b"];
echo $a <=> $b; // 0В общем,
Частичный индекс в «Оракле»
В «Оракле», в отличие от «Постгреса», частичных индексов в чистом виде нет, но есть возможность их сэмулировать — достаточно построить функциональный индекс и возвращать NULL на значениях, которые нам в индексе не нужны. Например:
CREATE INDEX resolution_notice_d_o_r ON resolution_notice(
CASE WHEN is_send_paper = 1 THEN org_id END,
TRUNC(rdate)
)Это немного видоизменённый реальный пример — таблица хранит море метаинформации, но нас намного чаще (в логике приложения) интересуют те записи, у которых флаг is_send_paper в значении «истина».
Чтобы «Оракл» узнал искомое в индексе, сравнение в запросе должно выглядеть так же. В общем, всё как в случае любого функционального индекса (лишнее я убрал):
SELECT …
FROM …
INNER JOIN resolution_notice rn ON …
WHERE dn.n = 0
AND CASE WHEN is_send_paper = 1 THEN dn.org_id END = :org_id
AND TRUNC(rn.rdate) BETWEEN :start_date AND :end_date
GROUP BY …
ORDER BY rdate DESC, …Мне стало интересно — если попробовать сунуть условие индекса в сам индекс, догадается ли «Оракл» дальше не дофильтровывать по нему? Ну, то есть, сделать
CREATE INDEX resolution_notice_d_o_r ON resolution_notice(
org_id,
TRUNC(rdate),
CASE WHEN is_send_paper = 1 THEN 1 END
)А в запрос добавить:
… CASE WHEN is_send_paper = 1 THEN 1 END = 1…Оказалось, «Оракл» не догадывается (у нас он

Несмотря на то, что в выборке по индексу значений с is_send_paper других значений, кроме единицы нет, «Оракл» всё равно дофильтровывает по этому значению.
Производительность PHP 7 на уровне HHVM?

Если изображённое на графике хоть отчасти правда, то я очень жду выхода ПХП 7 — куда больше, чем нового сезона «Игры Престолов». Автор графика сравнил производительность разных интерпретаторов ПХП на движке «ВордПресс».
По результату видно, что производительность новой версии находится на уровне HHVM!
Значительное отставание HHVM до четвёртого прогона объясняется «прогревом»
Насколько PHP работает быстрее без notices и warnings
Цитата со «СтекОверфлоу»:
У меня в закладках есть статья, в которой автор сделал некоторые замеры по этому поводу. Правда она на французском… вот ссылка (возможно
что-то вы в ней не пойдмёте): Ne faites pas d’erreur.Основные цифры, в помощь людям, не читающим
по-французски:
- 10 тысяч предупреждений, со включенными error_reporting и display_errors : 5162,76 мс
- то же, но настройка display_errors выключена: 136,18 мс
- то же, но error_reporting выключена тоже: 117,79 мс
- и, наконец, после исправления кода, после которого он перестал выдавать предупреждения: 19,51 мс
Это означает, что код на ПХП работает быстрее без сообщений/предупреждений/ошибок, даже если они не отображаются или не попадают