May 8th, 2015

кукяй

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;
}
От оригинальной библиотеки несколько отличий.

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

Во-вторых, в «пакет» добавлены несколько полезных функций, которые входят в ядро «Пайтона» — iter, slice, enumerate и xrange, работают они так же, как и в «Пайтоне».

В-третьих, поскольку у ПХП нет возможности именовать параметры при вызове, у фунций product и izip_longest изменён порядок аргументов. От этого тоже можно было бы избавиться, потерей указаний типов в прототипе, но я, опять же, не стал этого делать.

Если не заброшу библиотеку, то, возможно, всё-таки откажусь от указания типов и приведу всё к более совместимому виду.

кукяй

Upsert в PostgreSQL

Ура, появился коммит, добавляющий к оператору INSERT «Постгреса» конструкцию, которая позволяет использовать его как upsert (в «Оракле» это конструкция MERGE, в «Майэскуэеле» — REPLACE):
-- Обновить, если не удалась вставка
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;
Счастье-то какое!