<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Posts on Aleksandr Zubarev</title><link>https://zualex.com/posts/</link><description>Recent content in Posts on Aleksandr Zubarev</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><copyright>This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.</copyright><lastBuildDate>Fri, 29 Oct 2021 17:25:01 +0300</lastBuildDate><atom:link href="https://zualex.com/posts/index.xml" rel="self" type="application/rss+xml"/><item><title>Конспект: Learning How To Learn или как научиться учиться</title><link>https://zualex.com/posts/summary-course-learning-how-to-learn/</link><pubDate>Fri, 29 Oct 2021 17:25:01 +0300</pubDate><guid>https://zualex.com/posts/summary-course-learning-how-to-learn/</guid><description>Ссылка на курс: Learning How to Learn: Powerful mental tools to help you master tough subjects | Coursera
Два режима мышления Существует два режима мышления: сфокусированный (focused) и рассеянный (diffuse).
Сфокусированный режим хорошо известен - это состояние, когда сосредоточенно работаем над чем то.
В рассеянном режиме мозг не концентрируется на чем то конкретном, тем самым позволяя охватить изучаемый предмет в широком смысле. Этот режим включается, например, перед сном, в душе, на прогулке, при занятии спортом.</description><content type="html"><![CDATA[<p>Ссылка на курс:
<a href="https://www.coursera.org/learn/learning-how-to-learn">Learning How to Learn: Powerful mental tools to help you master tough subjects | Coursera</a></p>
<h2 id="два-режима-мышления">Два режима мышления</h2>
<p>Существует два режима мышления: сфокусированный (focused) и рассеянный (diffuse).</p>
<p><img src="/images/summary-course-learning-how-to-learn/focused_and_diffuse_mode.jpg" alt="Два режима мышления"></p>
<p>Сфокусированный режим хорошо известен - это состояние, когда сосредоточенно работаем над чем то.</p>
<p>В рассеянном режиме мозг не концентрируется на чем то конкретном, тем самым позволяя охватить изучаемый предмет в широком смысле. Этот режим включается, например, перед сном, в душе, на прогулке, при занятии спортом. В этом состоянии могут возникнуть спонтанные идеи и мысли, о которых возможно раньше даже не думали.</p>
<p>И есть ощущение, что чем больше находимся в сфокусированном режиме, тем лучше, но нет. Если требуется сделать сложную задачу, то для эффективного решения лучше переключаться между двумя режимами. Выделить сфокусированный отрезок времени, а потом отпустить ситуацию и переключиться на что-то другое. Вроде бы переключившись, перестаём решать задачу, но наш мозг в рассеянном режиме в фоне продолжает работу над задачей. Вспомните ситуацию на экзамене, когда не могли решить задачу, но как только выходили из кабинета, то решение сразу же приходило в голову. Это нечто похожее.</p>
<p>Можно привести еще пример повара, который сначала берётся за сложное блюдо, разогревает сковородку, кладёт рыбу и переключается на что-то другое, нарезает лук и зелень и т.д. Пока он делает маленькие задачи, крупная всё равно готовится.</p>
<h2 id="чанк-памяти">Чанк памяти</h2>
<p>Чанк (кусок) памяти - это некая единица информации, которую ранее изучали и пытались понять. Также чанк памяти - это превращение идеи, концепции или действия в одну простую мысль. Не нужно больше помнить все мелкие детали. Чанк - это как одеваться по утрам. Словно это одна простая мысль: &ldquo;нужно одеться&rdquo;. Но под этой простой мыслью на самом деле кроется последовательность действий.</p>
<p><img src="/images/summary-course-learning-how-to-learn/chunck.jpg" alt="Чанк памяти"></p>
<p>Чтобы сформировать чанк, нужно:</p>
<ul>
<li>Сосредоточить внимание на изучаемой информации.</li>
<li>Понять основную идею.</li>
<li>Понять контекст, то есть не только как, но и когда использовать эту информацию.</li>
</ul>
<p>Можно спутать формирование чанка с бездумным заучиванием, но нет, необходимо понять информацию и контекст использования. Вот это понимание подобно клею, который скрепляет чанки памяти между собой.</p>
<h2 id="эффект-установки">Эффект установки</h2>
<p>Формировать чанки это хорошо, но есть также и обратная сторона. Бывает такое, что наша первоначальная мысль, идея, или заученный чанк, которая уже хорошо укрепилась в нашей голове, мешают лучшей идее или решению быть найденными. Другими словами, наша первоначальная мысль выстраивает своего рода колею, которая мешает перестроиться и выйти на другой путь, в новое место, где может быть найдено более правильное решение.</p>
<p>Вот эта ситуация называется <a href="https://en.wikipedia.org/wiki/Einstellung_effect">Einstellung (от нем. - установка)</a> или эффект установки.</p>
<p><img src="/images/summary-course-learning-how-to-learn/einstellung.jpg" alt="Эффект установки"></p>
<p>Один из способов бороться с установками это изучение других дисциплин, возможно даже из других областей знаний. Когда чередуем между несколькими предметами или дисциплинами можно сделать интересные новые связи между чанками в разных областях. Но если развивать экспертизу только в одной дисциплине можно получить глубокие знания, но также можно стать глубоко укоренившимися в своём привычном образе мышления.</p>
<h2 id="прокрастинация">Прокрастинация</h2>
<p><img src="/images/summary-course-learning-how-to-learn/procrastination.jpg" alt="Прокрастинация"></p>
<p>Теперь о прокрастинации. Люди прокрастинируют, когда нужно сделать что-то не совсем приятное. Если думать о неприятном, то в голове активируются центры болевой чувствительности, и как следствие, переключаем внимание на что-то более приятное.</p>
<p>У прокрастинации много общих черт с зависимостью:</p>
<ul>
<li>Приносит временную радость и облегчение, спасая от реальности.</li>
<li>Легко себя обмануть. Придумываем иррациональные оправдания, которые внешне звучат разумно.</li>
</ul>
<p>Не всякая прокрастинация - зло. Возможно так организм реагирует на усталость и действительно стоит отдохнуть и прокрастинировать.</p>
<p>Но если нужно побороть прокрастинацию, то стоит сосредоточиться не на конечном результате (продукте), а на самом процессе. Например, сделать домашнее задание это продукт. Процесс, это когда говорим себе: &ldquo;я собираюсь потратить 20 минут на работу&rdquo;. В итоге может получиться, что втягиваешься в работу и тратится куда больше времени на полезное дело.</p>
<p>Этот метод работает потому, что именно результат вызывает болевые ощущения, которые вынуждают прокрастинировать. Например, <a href="https://ru.wikipedia.org/wiki/%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%BF%D0%BE%D0%BC%D0%B8%D0%B4%D0%BE%D1%80%D0%B0">техника Pomodoro</a> поможет сосредоточиться на процессе.</p>
<p>Еще один из способов побороть прокрастинацию - это найти способ вознаградить себя за хорошие привычки в обучении.</p>
<h2 id="память">Память</h2>
<p>Память - одна из частей процесса обучения и накопления опыта. Память состоит из кратковременной и долговременной памяти. Кратковременная память - это наша рабочая память, которая условно состоит из 4-х слотов. Объем долговременной памяти колоссален. Благодаря постоянной практике изучаемая информация переходит из рабочей памяти в долговременную.</p>
<p><img src="/images/summary-course-learning-how-to-learn/memory.jpg" alt="Память"></p>
<p>Время от времени, желательно делать паузы в учебе, позволяя нашему мозгу консолидировать знания, чем стараться выучить все за один раз.</p>
<p>Иногда случается ситуация, когда не можем что-то понять. Понятные прежде вещи внезапно начинают сбивать с толку. Подобный мыслительный упадок происходит, когда сознание перестраивает процесс восприятия, создавая при этом более солидную основу. Сознание обрабатывает информацию. В этот момент может показаться будто делаете шаг назад в обучении. Но после такого периода временной фрустрации можно обнаружить, что делаете неожиданный рывок вперед.</p>
<h2 id="приемы-и-советы">Приемы и советы</h2>
<ul>
<li>Заниматься спортом или переключаться на физическую активность. Тем самым мозг может включиться в рассеянных режим.</li>
<li>Постоянная практика.</li>
<li>Не выбирать для практики заведомо легкие вещи.</li>
<li>Делать паузы в обучении.</li>
<li>Не стараться выучить всё сразу за один раз.</li>
<li>Простое перечитывание малоэффективно, лучше просто повторить своими словами, не заглядывая в источник информации.</li>
<li>Избегать постоянного подчеркивания и выделения текста, это может привести к иллюзии, что усвоил информацию.</li>
<li>Сосредоточиться на процессе, а не на результате.</li>
</ul>
<h2 id="заключение">Заключение</h2>
<p>Мы способны производить значительные изменения в мозге, изменяя наш процесс мышления. Одним из ключей к успеху в обучении - это упорство и принять на себя ответственность за свое обучение.</p>
<p>Помните, что всегда будут те, кто критикует или пытается приуменьшить прилагаемые вами усилия или ваши достижения. Гордитесь тем, кто вы есть. И в особенности качествами, которые делают вас непохожим на других.</p>
]]></content></item><item><title>Copy-on-write в PHP</title><link>https://zualex.com/posts/copy-on-write-php/</link><pubDate>Tue, 27 Apr 2021 14:14:23 +0300</pubDate><guid>https://zualex.com/posts/copy-on-write-php/</guid><description>Copy-on-write или копирование при записи — один из способов управлением памятью. Но перед тем как давать какие-то определения, предлагаю рассмотреть пример:
function handle(array $array) { $result = []; // ... return $result; } $largeArray = getLargeArray(); handle($largeArray); В данном примере есть функция handle. В эту функцию передаётся массив большого размера. По умолчанию в PHP передача аргументов происходит по значению. Это означает, что если изменить значение аргумента внутри функции, то вне функции значение всё равно останется прежним.</description><content type="html"><![CDATA[<p>Copy-on-write или копирование при записи — один из способов управлением памятью. Но перед тем как давать какие-то определения, предлагаю рассмотреть пример:</p>
<pre tabindex="0"><code>function handle(array $array) {
    $result = [];
    // ...
    return $result;
}

$largeArray = getLargeArray();
handle($largeArray);
</code></pre><p>В данном примере есть функция handle. В эту функцию передаётся массив большого размера. По умолчанию в PHP передача аргументов происходит по значению. Это означает, что если изменить значение аргумента внутри функции, то вне функции значение всё равно останется прежним. Другими словами внутри функции используется копия переменной, но для создания копии требуется выделить память.</p>
<p><strong>Вопрос: в целях оптимизации стоит ли передать аргумент по ссылке handle(array &amp;$array)?</strong></p>
<p>На самом деле ответ зависит от того, что происходит внутри функции handle.</p>
<h2 id="чтение-аргумента">Чтение аргумента</h2>
<p>Представим, что функция handle только читает значения из $array.</p>
<pre tabindex="0"><code>function handle(array $array) {
    $result = [];
    foreach($array as $row) {
        $result[] = $row['id'];
    }
	
    return $result;
}
</code></pre><p>В данном случае внутри функции handle не произойдет копирования переменной $array. Переменные $array и $largeArray ссылаются на одну и ту же запись zval.</p>
<p>zval - если упрощенно, то это контейнер, в котором хранится переменная.</p>
<pre tabindex="0"><code>$value = 'string'; // создается контейнер zval_1
$copyValue = $value; // используется контейнер zval_1

$value = 'new string'; // создается контейнер zval_2

unset($value); // удаляется zval_2
unset($copyValue); // удаляется zval_1
</code></pre><p>При создании новой переменной $value создается новый контейнер zval, для простоты обозначим, как zval_1. На следующей строке переменной $copyValue присваивается значение $value. Интуитивно может показаться, что в этот момент будет выделена память, но на самом деле $copyValue только лишь ссылается (не путать со ссылками PHP, которые начинаются со знака &amp;) на тот же zval контейнер, что и $value. Новый контейнер будет создан только в том случае, если переменную $value или $copyValue изменить.</p>
<h2 id="модификация-аргумента">Модификация аргумента</h2>
<p>Теперь рассмотрим ситуацию, когда в функции handle переменная $array модифицируется.</p>
<pre tabindex="0"><code>function handle(array $array) {
    unset($array[0]);

    return $array;
}
</code></pre><p>В данном случае произойдет копирование переменной, то есть создание нового контейнера zval с выделением памяти.</p>
<h2 id="copy-on-write">Copy-on-write</h2>
<p>Суть подхода сopy-on-write или копирование при изменении заключается в том, что при чтении переменных используется общая копия, в случае изменения переменной — создается новая копия.</p>
<h2 id="замеры-памяти">Замеры памяти</h2>
<p>Проведём тест, попробуем увидеть, что при чтении аргумента используется общая копия, а при модификации выделяется память.</p>
<pre tabindex="0"><code>&lt;?php

function printMemory(string $header) {
    $memoryPeak = memory_get_peak_usage();
    
    echo $header . PHP_EOL;
    echo 'Peak usage: ' . round($memoryPeak / 1024) . 'KB of memory ' . PHP_EOL . PHP_EOL;
}

function handleRead(array $array) {
    $result = [];
    foreach($array as $row) {
        $result = 1; // чтобы выделенная память на $result не повлияла на замер
    }
	
    return $result;
}

function handleWrite(array $array) {
    unset($array[0]);

    $result = [];
    foreach($array as $row) {
        $result = 1; // чтобы выделенная память на $result не повлияла на замер
    }
}

$largeArray = range(0, 500000);
printMemory('create $largeArray');

handleRead($largeArray);
printMemory('handleRead');

handleWrite($largeArray);
printMemory('handleWrite');
</code></pre><p>Результат для PHP 7.4 - 8.0:</p>
<pre tabindex="0"><code>create $largeArray
Peak usage: 16764KB of memory

handleRead
Peak usage: 16765KB of memory

handleWrite
Peak usage: 33153KB of memory
</code></pre><p>Из результата видно, что при изменении аргумента, была выделена память, а при чтении нет.</p>
<h2 id="передача-объекта-в-качестве-аргумента">Передача объекта в качестве аргумента</h2>
<p>Стоит рассмотреть случай передачи объекта в качестве аргумента. И посмотреть, применяется ли для данного случая механизм copy-on-write. Рассмотрим следующий пример:</p>
<pre tabindex="0"><code>function printMemory(string $header) {
    $memoryPeak = memory_get_peak_usage();
    
    echo $header . PHP_EOL;
    echo 'Peak usage: ' . round($memoryPeak / 1024) . 'KB of memory ' . PHP_EOL . PHP_EOL;
}

$object = new stdClass;
$object-&gt;list = range(0, 500000);

function handle(stdClass $stdClass) {
    unset($stdClass-&gt;list[0]);
}

echo 'Before: ' . count($object-&gt;list) . PHP_EOL;

handle($object);

echo 'After: ' . count($object-&gt;list) . PHP_EOL;
</code></pre><p>Результат:</p>
<pre tabindex="0"><code>Before: 500001
Memory before handle
Peak usage: 16764KB of memory 

After: 500000
Memory after handle
Peak usage: 16765KB of memory 
</code></pre><p>В данном примере, переданный объект модифицируется внутри функции. Следуя, описанной выше логике, то в методе должна создаться копия $stdClass. Но если посмотреть на результат выполнения скрипта, то видно, что $object изменился, несмотря на то, что передавали переменную по значению, а память, по сути, осталось неизменной.</p>
<p>Складывается впечатление, что объект передаётся по ссылке, а не по значению, но это не совсем верно. На самом деле при передаче объекта в качестве аргумента передаётся только ID объекта. Содержимое объекта хранится отдельно и доступ можно получить по ID. Из-за этого, если что-то изменить внутри объекта, то это доступно и внутри функции и вне функции. Более детально можно прочитать документации PHP: <a href="https://www.php.net/manual/ru/language.oop5.references.php">Объекты и ссылки</a>.</p>
<p>То есть в данном примере не нужно передавать объект по ссылке, так как передается всего лишь ID объекта.</p>
<h2 id="вывод">Вывод</h2>
<p>В подавляющем большинстве не нужно передавать аргумент по ссылке. Так как редко оперируем большими по памяти переменными. И аргументы в большинстве случаев используются только на чтение. Если речь идет про передачу объектов, то и в этом случае не нужно передавать аргумент по ссылке, так как передаётся только идентификатор объекта.</p>
<p>Другими словами не нужно сейчас бежать, и срочно что-то менять в вашей коде и в вашем подходе. Продолжаем писать код как, обычно, но уже более осознанно.</p>
<h2 id="источники">Источники</h2>
<ul>
<li><a href="https://www.phpinternalsbook.com/php5/zvals/memory_management.html">PHP Internals Book - Memory management</a></li>
<li><a href="zotero://select/items/_SN9JCSZQ">Henry. 2011. <em>Henry @ Web Apps: PHP copy on write - how PHP manages variable memory</em></a></li>
<li><a href="https://www.php.net/manual/ru/language.oop5.references.php">PHP - Объекты и ссылки</a></li>
</ul>
]]></content></item><item><title>PHP производительность array_merge в цикле</title><link>https://zualex.com/posts/php-array_merge-performance-in-a-loop/</link><pubDate>Wed, 24 Mar 2021 11:47:29 +0300</pubDate><guid>https://zualex.com/posts/php-array_merge-performance-in-a-loop/</guid><description>Если использовать array_merge в цикле, то phpStrom + плагин Php Inspections (EA Extended) даёт подсказку: [EA] 'array_merge(...)' is used in a loop and is a resources greedy construction. Стало интересно, насколько сильно влияет на произовдительность использование array_merge в цикле и какие есть альтернативные решения.
Рассмотрим пример. Имеется список товаров следующей структуры:
$products = [ ['id' =&amp;gt; 1, 'tags' =&amp;gt; ['tag_1', 'tag_2', 'tag_3']], ['id' =&amp;gt; 2, 'tags' =&amp;gt; ['tag_1', 'tag_2', 'tag_3']], ]; Нужно получить список всех тегов.</description><content type="html"><![CDATA[<p>Если использовать array_merge в цикле, то phpStrom + плагин <a href="https://plugins.jetbrains.com/plugin/7622-php-inspections-ea-extended-">Php Inspections (EA Extended)</a> даёт подсказку: <code>[EA] 'array_merge(...)' is used in a loop and is a resources greedy construction.</code> Стало интересно, насколько  сильно влияет на произовдительность использование array_merge в цикле и какие есть альтернативные решения.</p>
<p>Рассмотрим пример. Имеется список товаров следующей структуры:</p>
<pre tabindex="0"><code>$products = [
    ['id' =&gt; 1, 'tags' =&gt; ['tag_1', 'tag_2', 'tag_3']],
    ['id' =&gt; 2, 'tags' =&gt; ['tag_1', 'tag_2', 'tag_3']],
];
</code></pre><p>Нужно получить список всех тегов.</p>
<p>В экспериментах решил проверить скорость скрипта при количестве в 10, 250, 500, 1000, 2000, 5000, 10000, 50000 товаров, и также изменять количество тегов внутри каждого товара: 1, 3, 6 тегов.</p>
<h2 id="объединение-массивов">Объединение массивов</h2>
<p>Выделил следующие способы объединить два и более массивов в один:</p>
<ul>
<li>array_merge</li>
<li>Оператор &hellip;</li>
<li>Оператор +</li>
<li>array_replace</li>
<li>foreach</li>
</ul>
<p>Рассмотрим каждый из способов с примерами кода.</p>
<h4 id="использование-array_merge">Использование array_merge</h4>
<pre tabindex="0"><code>$tags = [];
foreach($products as $product) {
    $tags = array_merge($tags, $product['tags']);
}
</code></pre><p><a href="https://gist.github.com/zualex/39b3dfe9503b69468307f420089d0c42">https://gist.github.com/zualex/39b3dfe9503b69468307f420089d0c42</a></p>
<h4 id="распаковка-массива-через-оператор-">Распаковка массива через оператор &hellip;</h4>
<pre tabindex="0"><code>$tags = [];
foreach($products as $product) {
    $tags[] = $product['tags'];
}
$tags = array_merge(...$tags);
</code></pre><p><a href="https://gist.github.com/zualex/c1bd85a1c2fee5b4c362953211d976ea">https://gist.github.com/zualex/c1bd85a1c2fee5b4c362953211d976ea</a></p>
<h4 id="использование-оператора-">Использование оператора +</h4>
<p>При использовании оператора + если совпадают индексы, то в результирующем массиве будут только элементы из массива слева от оператора. Чтобы этого избежать потребуется переделать структуру хранения товаров, чтобы в списке тегов был уникальный индекс.</p>
<pre tabindex="0"><code>$products = [
    ['id' =&gt; 1, 'tags' =&gt; ['tag_1' =&gt; tag_1', 'tag_2' =&gt; 'tag_2', 'tag_3' =&gt; 'tag_3']],
    ['id' =&gt; 2, 'tags' =&gt; ['tag_1' =&gt; tag_1', 'tag_2' =&gt; 'tag_2', 'tag_3' =&gt; 'tag_3']],
];

$tags = [];
foreach($products as $product) {
    $tags += $product['tags'];
}
</code></pre><p><a href="https://gist.github.com/zualex/902a6f9e9d1f23a7da7cdd3bebf72812">https://gist.github.com/zualex/902a6f9e9d1f23a7da7cdd3bebf72812</a></p>
<h4 id="использование-array_replace">Использование array_replace</h4>
<p>В данном примере тоже потребуется использовать структуру хранения товаров, как при использовании оператора +.</p>
<pre tabindex="0"><code>$tags = [];
foreach($products as $product) {
    $tags = array_replace($tags, $product['tags']);
}
</code></pre><p><a href="https://gist.github.com/zualex/b656a5f12992f96edaacf1bb22cd0bf7">https://gist.github.com/zualex/b656a5f12992f96edaacf1bb22cd0bf7</a></p>
<h4 id="использование-foreach">Использование foreach</h4>
<pre tabindex="0"><code>$tags = [];
foreach($products as $product) {
    foreach($product['tags'] as $tag) {
        $tags[] = $tag;
    }
}
</code></pre><p><a href="https://gist.github.com/zualex/0d101fb8b48701352117cfc253be2762">https://gist.github.com/zualex/0d101fb8b48701352117cfc253be2762</a></p>
<h2 id="анализ-производительности">Анализ производительности</h2>
<p>Результаты прогонов в этой таблице: <a href="https://docs.google.com/spreadsheets/d/1Va7pg5iaPbXMxkbcQ5sOTco0d316IUzLCOcHq1CrzRo/edit?usp=sharing">https://docs.google.com/spreadsheets/d/1Va7pg5iaPbXMxkbcQ5sOTco0d316IUzLCOcHq1CrzRo/edit?usp=sharing</a></p>
<p>Эксперименты показали, что для PHP 8.0 и 7.4 нет существенной разницы между версиями. Из-за этого примеры будут для PHP 7.4.</p>
<p><img src="/php-array_merge/PHP_7.4_....png" alt="График PHP 7.4 &hellip;">
Для 6 тегов нет расчетов из-за memory limit во время прогона тестов.</p>
<p><img src="/php-array_merge/PHP_7.4_+.png" alt="График PHP 7.4 +">
<img src="/php-array_merge/PHP_7.4_foreach.png" alt="График PHP 7.4 foreach"></p>
<p>Как видно из графиков, <code>...</code>, <code>+</code>, <code>foreach</code> имеют линейную зависимость.</p>
<p><img src="/php-array_merge/PHP_7.4_array_merge.png" alt="График PHP 7.4 array_merge">
<img src="/php-array_merge/PHP_7.4_array_replace.png" alt="График PHP 7.4 array_replace"></p>
<p><code>array_merge</code>, <code>array_replace</code> - видна квадратичная зависимость. В экспериментах данные имеются только для 10000.</p>
<h3 id="сравним-вместе">Сравним вместе</h3>
<p>Теперь попробуем совместить графики, чтобы увидеть общую картину. Сравнивать буду для PHP 7.4 с использованием 3-х тегов.</p>
<p>График для кол-во от 10 до 500 товаров.
<img src="/php-array_merge/PHP_7.4_10_500.png" alt="График PHP 7.4 всё вместе от 10 до 500 товаров"></p>
<p>Как видно, уже начиная от 250, а может и ранее (в экспериментах делал тесты для 10, 250, 500 товаров), <code>array_merge</code> и <code>array_replace</code> отрабатывают дольше всех.</p>
<p>Теперь если посмотреть для 50000 товаров.
<img src="/php-array_merge/PHP_7.4_10_50000.png" alt="График PHP 7.4 всё вместе от 10 до 50000 товаров">
<code>array_merge</code> и <code>array_replace</code> в рамках теста, смог замерить только для 10000 товаров. Разница существенна, на порядки.</p>
<h3 id="а-что-с-памятью">А что с памятью?</h3>
<p><img src="/php-array_merge/PHP_7.4_memory.png" alt="График PHP 7.4 память">
С памятью всё ок, все подходы практически одинаково используют память.</p>
<h3 id="самый-быстрый-подход">Самый быстрый подход</h3>
<p><img src="/php-array_merge/7.4_compare_fast.png" alt="PHP 7.4 сравнение быстрых подходов">
В данных эксперимента самым быстрым оказался обычный foreach, но разница столь не существенна, что думаю не стоит делать разницы между <code>...</code>, <code>+</code> и <code>foreach</code> .</p>
<h2 id="а-если-без-цикла">А если без цикла?</h2>
<p>Для полноты картины посмотрим на те же подходы, если без цикла объединить 3 больших массива по 100000 элементов в каждом.</p>
<pre tabindex="0"><code>$big1 = range(0, 100000);
$big2 = array_fill(100000, 100000, 'string');
$big3 = range(200000, 100000);
</code></pre><h4 id="без-цикла-array_merge">Без цикла array_merge</h4>
<pre tabindex="0"><code>$result = array_merge($big1, $big2, $big3);
</code></pre><p><a href="https://gist.github.com/zualex/a9f536006c1a24f7c4d4888077dc9fc0">https://gist.github.com/zualex/a9f536006c1a24f7c4d4888077dc9fc0</a></p>
<h4 id="без-цикла-распаковка-массива-через-оператор-">Без цикла Распаковка массива через оператор &hellip;</h4>
<pre tabindex="0"><code>$result = [...$big1, ...$big2, ...$big3];
</code></pre><p><a href="https://gist.github.com/zualex/a35cd71c38480658a1bbaf6559c4c2fe">https://gist.github.com/zualex/a35cd71c38480658a1bbaf6559c4c2fe</a></p>
<h4 id="без-цикла-оператор-">Без цикла оператор +</h4>
<pre tabindex="0"><code>$result = $big1 + $big2 + $big3;
</code></pre><p><a href="https://gist.github.com/zualex/6e6d9d584cc979b59da517f9078e6559">https://gist.github.com/zualex/6e6d9d584cc979b59da517f9078e6559</a></p>
<h4 id="без-цикла-array_replace">Без цикла array_replace</h4>
<pre tabindex="0"><code>$result = array_replace($big1, $big2, $big3);
</code></pre><p><a href="https://gist.github.com/zualex/4e770098f40f2f101138dda7251f32ca">https://gist.github.com/zualex/4e770098f40f2f101138dda7251f32ca</a></p>
<h4 id="без-цикла-foreach">Без цикла foreach</h4>
<pre tabindex="0"><code>$result = [];
foreach ($big1 as $key =&gt; $value) {
    $result[$key] = $value;
}
foreach ($big2 as $key =&gt; $value) {
    $result[$key] = $value;
}
foreach ($big3 as $key =&gt; $value) {
    $result[$key] = $value;
}
</code></pre><p><a href="https://gist.github.com/zualex/4e770098f40f2f101138dda7251f32ca">https://gist.github.com/zualex/4e770098f40f2f101138dda7251f32ca</a></p>
<h3 id="анализ-данных-без-циклов">Анализ данных без циклов</h3>
<h4 id="скрость">Скрость</h4>
<p><img src="/php-array_merge/PHP_7.4_Without_loop_Speed.png" alt="График PHP 7.4 без цикла скорость"></p>
<h4 id="память">Память</h4>
<p><img src="/php-array_merge/PHP_7.4_Without_loop_Memory.png" alt="График PHP 7.4 без цикла память (KB)"></p>
<p>Как видно, <code>array_merge</code> быстрее всех справляется с объединением больших массивов без использования циклов. По памяти Чуть экномичнее <code>foreach</code> и <code>array_replace</code></p>
<h2 id="вывод">Вывод</h2>
<p><code>array_merge</code> и <code>array_replace</code> не нужно использовать в циклах, даже в маленьких. При решении данной задачи лучше выбрать  <code>...</code>, <code>+</code> или <code>foreach</code>. Но стоит не забывать отличия в работе <code>array_merge</code> и <code>+</code>.</p>
<p>Но если стоит задача объединить массивы, когда нет циклов, то лучше использовать <code>array_merge</code>.</p>
<h2 id="ресурсы">Ресурсы:</h2>
<ul>
<li><a href="http://langtoday.com/?p=393">Разница между array_merge и + (оператор плюс) в PHP</a></li>
<li><a href="https://www.kindacode.com/article/merging-arrays-in-php-7/">[PHP 7] Merging arrays with array_merge() and spread syntax</a></li>
<li><a href="https://github.com/dseguy/clearPHP/blob/master/rules/no-array_merge-in-loop.md">No Array_merge In Loops</a></li>
<li><a href="https://github.com/zendframework/zendframework/issues/5716">Zendframework Issues #5716: Zend\Loader\ClassMapAutoloader - Performance improvement</a></li>
<li><a href="https://github.com/php/php-src/blob/master/ext/standard/array.c#L3544">Исходный код array_merge: php-src/ext/array.c -&gt; PHPAPI int php_array_merge</a></li>
</ul>
]]></content></item><item><title>Из тимлида в разработчики</title><link>https://zualex.com/posts/team-leader-to-developer/</link><pubDate>Mon, 07 Sep 2020 09:00:00 +0300</pubDate><guid>https://zualex.com/posts/team-leader-to-developer/</guid><description>Предыстория Начну с того, как я стал тимлидом.
Откликнулся на вакансию разработчика, пригласили на собеседование. Собеседование было обычное, ничем не отличающееся от других моих собеседований, но в конце начали задавать вопросы связанные с управлением командой. На все эти вопросы отвечал по интуиции, что то мог ответить исходя из опыта.
В итоге собеседование прошел успешно и получил оффер на роль старшего разработчика, но после 3-х месяцев испытательного срока моя роль превращается в тимлида.</description><content type="html"><![CDATA[<h2 id="предыстория">Предыстория</h2>
<p>Начну с того, как я стал тимлидом.</p>
<p>Откликнулся на вакансию разработчика, пригласили на собеседование. Собеседование было обычное, ничем не отличающееся от других моих собеседований, но в конце начали задавать вопросы связанные с управлением командой. На все эти вопросы отвечал по интуиции, что то мог ответить исходя из опыта.</p>
<p>В итоге собеседование прошел успешно и получил оффер на роль старшего разработчика, но после 3-х месяцев испытательного срока моя роль превращается в тимлида.</p>
<p>Я был удивлён этому, так как ожидал, что буду разработчиком, а если и стану тимлидом, то спустя время, после того как погружусь в процессы компании и добьюсь результата.</p>
<p>Чем больше я думал о тимлидстве, тем тимлидство больше думало обо мне. Я загорелся этой идеей. Также я брал в расчет, что у меня будет время для адаптации и, что за 3 месяца в роли разработчика смогу более менее плавно переключиться на роль тимлида.</p>
<p>Как итог я принял оффер.</p>
<h2 id="развитие-событий">Развитие событий</h2>
<p>В начале было всё хорошо. Я подружился с командой, у нас не было тимлида в команде. Роль тимлида временно замещал руководитель отдела. В целом задачи были понятны, успел за первую неделю выложить на продакшен задачу. Мой фокус был на разработку.</p>
<p>Но спустя 2 недели руководитель отдела перешел в другой отдел, а на его место пришел другой руководитель. Уже на следующий день я общался с новым руководителем, и он сказал, давай тимлидь. Времени подумать у меня толком не было и возможно выбора у меня тоже не было. Я согласился в экспресс режиме превратиться в тимлида.</p>
<h2 id="кто-такой-тимлид">Кто такой тимлид?</h2>
<p>Кто такой тимлид? Мне на этот вопрос четкого ответа не дали.</p>
<p>В разных компаниях своё представление, кто такой тимлид, и мне хотелось определиться с тем, что от меня требуется. Самое основное, что понял, то я должен взаимодействовать с заказчиками, отвечать за сроки и развивать команду.</p>
<p>Меня плавно подключали к чатам, к регулярным созвонам. Заказчики были довольны, команда тоже рада, что после смутных времен, наконец у команды появился тимлид.</p>
<h2 id="первые-проблемы">Первые проблемы</h2>
<p>Всё было хорошо буквально первую неделю, потом начали вылезать проблемы. И одна из проблем была связана с разработчиком, который временами пропадал и делал имитацию бурной деятельности. Так продолжалось около месяца, было то всё нормально, то опять плохо. Всё это время были попытки понять корневую проблему, но мне так это не удалось. В итоге он уволился, он нашел себе другую работу.</p>
<p>Следующая проблема заключалась в том, что с каждым днём у меня всё меньше и меньше уходило времени на разработку. Я сам понимал, что у тимлида в приоритете команда, но всё таки планировал примерно 50% времени уделять на программирование. Но физически на это времени не хватало, каждый день был расписан в созвонах, а между созвонами решал проблемы команды.</p>
<p>Третья проблема была в том, что у заказчиков было ожидание, что я буду разрабатывать, чтобы скорость команды выросла. Но как выше уже написал, на разработку у меня времени не хватало, у меня получалось выделять в среднем около 30 минут в день. В попытках оптимизации своего времени, в итоге после анализа своих дней, я пришел к выводу, что если ничего не менять и оставить всё как есть, то на разработку у меня остаётся 10 минут в НЕДЕЛЮ!</p>
<h2 id="выход-из-ситуации">Выход из ситуации</h2>
<p>В роли тимлида я уже 2 месяца и понимаю, что от меня требуется невозможное.</p>
<p>Возможно так у всех тимлидов, но я себе честно признался, что не такие были у меня ожидания от тимлидства. Также понял, что я хочу расти в техническую сторону, а не в менеджмент. Обдумав всю ситуацию, решил этот момент прояснить с моим руководителем. В итоге мы пришли к тому, что я фокусируюсь на разработке, а на роль тимлида найдут кого то из отдела. Спустя неделю нашелся желающий возглавить нашу команду. Вот так я превратился обратно в разработчика.</p>
<h2 id="вывод">Вывод</h2>
<ul>
<li>Тимлид это рост в менеджерскую ветку, где с каждым днем программирование своими руками становится всё меньше и меньше</li>
<li>Совмещать менеджмент и разработку сложно</li>
<li>Ожидания и реальность от роли тимлида скорее всего не совпадут, так как даже в рамках одной компании, каждый тимлид уникален и обрастает своим списком обязанностей</li>
</ul>
<p>Выводы возможно выглядят очевидными, но скорее всего в момент получения оффера я не размышлял о плюсах и минусах быть тимлидом, а пытался убедить себя, что это мне нужно.</p>
<p>В любом случае считаю (ну или опять пытаюсь убедить себя🙂), что данный опыт очень полезен был для меня, и скорее всего к роли тимлида я еще вернусь, но позже.</p>
]]></content></item><item><title>Мои жизненные принципы</title><link>https://zualex.com/posts/my-life-principles/</link><pubDate>Thu, 15 Aug 2019 19:12:42 +0300</pubDate><guid>https://zualex.com/posts/my-life-principles/</guid><description>Цени время Деньги можно заработать, а потраченное впустую время не вернуть. Не позволяй слишком много тратить время на то, что не приносит тебе пользы.
Постоянно развивайся Вкладывай время и усилия в своё развитие, это окупится.
Будь честным Выстраивай доверительные отношения, даже если тебя обманывают, всё равно продолжай быть честным.
Будь пунктуальным Уважай чужое время и не опаздывай. Видишь, что не успеваешь, предупреди.
Будь профессионалом На работе будь профессионалом в своем деле, тебе за это платят деньги.</description><content type="html"><![CDATA[<h2 id="цени-время">Цени время</h2>
<p>Деньги можно заработать, а потраченное впустую время не вернуть. Не позволяй слишком много тратить время на то, что не приносит тебе пользы.</p>
<h2 id="постоянно-развивайся">Постоянно развивайся</h2>
<p>Вкладывай время и усилия в своё развитие, это окупится.</p>
<h2 id="будь-честным">Будь честным</h2>
<p>Выстраивай доверительные отношения, даже если тебя обманывают, всё равно продолжай быть честным.</p>
<h2 id="будь-пунктуальным">Будь пунктуальным</h2>
<p>Уважай чужое время и не опаздывай. Видишь, что не успеваешь, предупреди.</p>
<h2 id="будь-профессионалом">Будь профессионалом</h2>
<p>На работе будь профессионалом в своем деле, тебе за это платят деньги. Выполняй задачи отлично.</p>
<h2 id="не-конфликтуй-на-работе">Не конфликтуй на работе</h2>
<p>Помни, у каждого есть своё мнение. Никто не должен быть точно таким же, как и ты. Старайся находить компромисс.</p>
<h2 id="будь-командным-игроком">Будь командным игроком</h2>
<p>Не иди против команды. Индивидуальные усилия малы, по сравнению с силой всей команды.</p>
<h2 id="управляй-ситуацией">Управляй ситуацией</h2>
<p>Старайся избегать ситуации, когда ты “жертва”, бери ситуацию под контроль. Например, боишься, что тебя уволят, и останешься без денег, накопи финансовую подушку безопасности.</p>
<h2 id="не-переживай-о-том-что-думают-другие">Не переживай о том, что думают другие</h2>
<p>К мнению других людей нужно прислушиваться, но не переживать. Они не в контексте твоей ситуации. Чем больше людей знают о тебе, тем больше мнений можно услышать. И если честно, то многим всё равно на тебя.</p>
<h2 id="соблюдай-work-life-balance">Соблюдай work-life balance</h2>
<p>Люби работу, но не в ущерб своему отдыху и личной жизни.</p>
]]></content></item><item><title>Как я не попал в Яндекс</title><link>https://zualex.com/posts/how-i-failed-the-interview-in-yandex/</link><pubDate>Wed, 26 Jun 2019 20:41:57 +0300</pubDate><guid>https://zualex.com/posts/how-i-failed-the-interview-in-yandex/</guid><description>Эта статья о том, как обычный PHP разработчик готовился к собеседованию и чуть не попал в Яндекс в сентябре 2018 года. Перед тем как описывать сам процесс прохождения собеседования, я бы хотел рассказать, что происходило за месяц до собеседования.
За месяц до собеседования В этот момент я еще не думал о Яндексе и Яндекс обо мне. Я желал просто сменить свою текущую работу. Своё желание о смене работы я заранее огласил своему руководству и со спокойной душой обновил резюме, начитался как проходить собеседования и стал искать работу.</description><content type="html"><![CDATA[<p>Эта статья о том, как обычный PHP разработчик готовился к собеседованию и чуть не попал в Яндекс в сентябре 2018 года. Перед тем как описывать сам процесс прохождения собеседования, я бы хотел рассказать, что происходило за месяц до собеседования.</p>
<h2 id="за-месяц-до-собеседования">За месяц до собеседования</h2>
<p>В этот момент я еще не думал о Яндексе и Яндекс обо мне. Я желал просто сменить свою текущую работу. Своё желание о смене работы я заранее огласил своему руководству и со спокойной душой обновил резюме, начитался как проходить собеседования и стал искать работу. Новую работу нашел довольно быстро.</p>
<p>На новом месте я успел проработать примерно 2 недели. Прошел онбординг и в целом всё устраивало. Но в этот момент мне пишет СТО с предыдущей работы и спрашивал меня, знаю ли хороших Python разработчиков. Я в шутку ему ответил: &ldquo;Нет, не знаю, но могу сам попробовать&rdquo;. В итоге оказалось, что к СТО обратились знакомые из Яндекса, и он решил спросить меня, так как знал моё увлечение к Python.</p>
<p>Я стал размышлять над предложением. Я понимал, что завалю собеседование, возможно с позором, что возможно буду самым худшим, который когда либо собеседовался в Яндексе. Ведь даже Python не является моим основным инструментом, использовал только для своих проектов. Я понимал, что возможно могу поставить крест на текущей работе, где успел проработать 2 недели. Но я решил, что лучше попробовать, лучше получить хоть какой-то опыт, проверить свои силы, чем просто спасовать. В итоге я согласился на предложение.</p>
<h2 id="подготовка-к-собеседованию">Подготовка к собеседованию</h2>
<p>После переписки с СТО с предыдущей работы началась моя подготовка к собеседованию в Яндекс. Со мной связалась HR. Она рассказала про все преимущества, какие есть в Яндексе. Рассказала, как проходит само собеседование, и само собеседование назначили в Москве через 2 недели.</p>
<p>Далее мне предстояло пообщаться с СТО на текущей работе, чтобы отпросится на собеседование, так как собеседование было в будни и плюс в Москву нужно было добраться на поезде. Первая реакция СТО была немного агрессивная, он подумал, что я решил сменить работу и что мне тут не нравится. В итоге, когда я ему всё честно объяснил, что основная моя мотивация это проверка моих скиллов, то он понял и предложил помощь. Он в итоге меня встретил в Москве и подвез до собеседования, за это я ему очень благодарен.</p>
<p>Возвращаясь непосредсвенно к моей подготовке, то меня ждали две тяжелые недели, за это время я успел:</p>
<ul>
<li>Прочитать книгу A Bite of Python</li>
<li>Каждый день по 2 часа решал задачки на HackerRank</li>
<li>Выучил наизусть 5-6 основных алгоритмов сортировки</li>
<li>Тренировался писать код на листочке</li>
</ul>
<p>На всё это требовалось много времени, примерно по 4 часа в день выделял на подготовку. Чем ближе к собеседованию, тем больше понимал, что как много я не знаю. Но в последние два дня решил себя не мучить и дать голове отдохнуть и переварить всю полученную информацию.</p>
<h2 id="собеседование">Собеседование</h2>
<p>В Московском офисе меня ждало 4 секции, то есть 4 собеседования с 12:00 и до 18:00:</p>
<pre tabindex="0"><code>13:00 - 14:00 – Первая секция с написанием кода 
14:00 - 15:00 – Вторая секция с написанием кода 
15:00 - 16:00 - Обед с hr
16:00 - 17:00 - Менеджерская секция
17:00 - 18:00 - Финальная секция с кодом
</code></pre><p>Сразу оговорюсь, я не буду писать ответы в виде кода на свои задачи. Так как мои решения могут быть не самыми лучшими или вообще неправильными.</p>
<h3 id="первая-секция-с-написанием-кода">Первая секция с написанием кода</h3>
<p>Первая секция была довольна простая, и это было хорошо для разогрева. Мы много общались про Python. Потом мне дали посмотреть код, и я должен был провести code review. Сказать где ошибка закралась, сказать свои пожелания и улучшения, которые можно сделать. И в конце уже была задача:</p>
<blockquote>
<p>Нужно написать функцию, которая принимает путь к директории и возвращает, сколько занимает места на диске переданная директория.</p>
</blockquote>
<!-- raw HTML omitted -->
<p>Символические ссылки нужно игнорировать.<br>
Отслеживать inode - если inode уже была, больше учитывать её не нужно.</p>
<!-- raw HTML omitted -->
<p>Эту задачу я сделал, но не учел ряд моментов, которые отобразил в подсказке. Интервьюер стал мне задавать наводящие вопросы “а что если” и в итоге смог дойти до решения. Моё решение выглядело примерно как тут: <a href="https://stackoverflow.com/a/12984676/7772173">https://stackoverflow.com/a/12984676/7772173</a></p>
<p>Итог: секция успешная</p>
<h3 id="вторая-секция">Вторая секция</h3>
<p>Вторая секция с кодом была труднее. Сразу дали задачку:</p>
<blockquote>
<p>Нужно написать функцию, у которой есть два аргумента:</p>
<ul>
<li>Первый аргумент массив уникальных идентификаторов баннеров</li>
<li>Второй аргумент целое число K</li>
</ul>
<p>Функция должна вернуть массив, содержащий K уникальных баннеров</p>
</blockquote>
<p>Эту задачу мы разбирали всю секцию. Моё первое решение было довольно простое: на каждом шаге от 1 до K вычисляем рандомный индекс из входящего массива, если индекс уже встречался, то еще раз перезапускаем рандом. Решение плохое, так как если K стремится к длине входящего массива, то рандом будет всё чаще и чаще вызываться.
Мы долго общались, я предлагал разные алгоритмы и структуры данных. И в итоге у меня получилось озвучить удовлетворяющий требованиям алгоритм. Код написал на листке бумаги и на удивление написал его без ошибок.</p>
<!-- raw HTML omitted -->
<p>Нужно удалять элементы из входящего массива, когда через рандом находим нужный элемент. Но удаление из массива O(n), чтобы удалить за O(1) нужно найденное значение поменять местами с последним элементом в массиве. И тогда удалить последний элемент массива можно за O(1).</p>
<!-- raw HTML omitted -->
<p>После завершения этой секции у меня остались смешанные чувства. С одной стороны я смог дойти до правильного решения, а с другой стороны мне потребовались подсказки и скорее всего, должна была быть еще одна задача, но мы не успели по времени.</p>
<p>Итог: секция успешная</p>
<h3 id="третья-секция">Третья секция</h3>
<p>Менеджерская секция была для меня небольшим отдыхом от задач. Я общался с людьми из той команды, которая хотела меня видеть. Были вопросы о моём опыте, о моих целях и планах. Также были вопросы, как я подхожу к выполнению задач, например: <code>Что ты делаешь, если видишь, что не успеваешь сделать задачу в срок?</code></p>
<p>Итог: секция успешная</p>
<h3 id="четвертая-секция">Четвертая секция</h3>
<p>Финальная секция с кодом была самой сложно и самой важной. Меня предупредили, что если я не пройду эту секцию, то меня в любом случае не возьмут.</p>
<p>Первая задача было следующая:</p>
<blockquote>
<p>Нужно написать функцию, которая принимает две строки, а в результате возвращает True или False.</p>
<p>Например:</p>
<ul>
<li>если str_1 = ‘ctr’ и str_2 = ‘catrow’, то нужно вернуть True</li>
<li>если str_1 = ‘ctr’ и str_2 = ‘cartow’, то нужно вернуть False</li>
</ul>
</blockquote>
<p>Я знал, что есть целый класс алгоритмов для поиска подстроки в строке. Но я ни одного не знал. Но на удивление со второй попытки мне удалось изложить алгоритм, который понравился интервьюеру.</p>
<!-- raw HTML omitted -->
<p>Мой алгоритм использовал счетчик, который указывал на индекс первой строки. И в конце нужно сверить счетчик и длину первой строки, если счетчик меньше, чем длина строки, то возвращаем False, иначе True</p>
<!-- raw HTML omitted -->
<p>Вторая задача была следующая:</p>
<blockquote>
<p>Дается массив каждый элемент, которого состоит из двух чисел - координаты X и Y. Этот массив это облако точек на координатной плоскости.
Нужно написать функцию, которая возвращает True, если через это облако точек можно провести вертикальную асимптоту, и если все точки симметричны относительно этой асимптоты.</p>
</blockquote>
<p>С этой задачкой я был в тупике. Я не мог даже предоставить простого решения. Но в итоге с помощью интервьюера получилось хоть какое-то решение. Далее нужно было его оптимизировать и тут у меня были сложности. Иногда я путался в вычислении сложности алгоритма, не мог подобрать нужные структуры данных. Но под конец интервью я смог на словах проговорить оптимизированных алгоритм, но не успел написать реализацию.</p>
<!-- raw HTML omitted -->
<p>Чтобы вычислить координату X вертикальной асимптоты, нужно найти середину между максимальным и минимальным X в облаке точек.
Нужно учесть, что точки могут накладываться друг на друга.
Если у точки совпадает координата X с координатой вертикальной асимпоты, то точку можно не учитывать, так как она симметрична относительно себя.
Можно облако точек преобразовать в хеш таблицу, где ключ это X, а значение тоже хеш-таблица, где ключ Y, а значение счетчик, показывающий кол-во точек в этой координате. Далее работать с этой хеш-таблицей и удалять симметричные точки, если хеш-таблица в конце пустая, значит все точки симметричные.</p>
<!-- raw HTML omitted -->
<p>Итог: секцию провалил</p>
<h2 id="после-собеседования">После собеседования</h2>
<p>После собеседования меня встретила HR. Она пообещала как можно скорее дать результаты собеседований. У меня были ощущения, что я не прошел собеседование. Последняя и важная секция выдалась плохой для меня. Сами результаты собеседований мне сообщили довольно быстро, спустя 3 дня. И как ожидалось, завалил последнюю секцию. Общий мотив, то что мне нужно прокачивать алгоритмы.</p>
<h2 id="вывод">Вывод</h2>
<p>Меня не взяли. Я прекрасно понимаю, что крупным компаниям лучше не взять хорошего программиста, чем взять плохого. Но я доволен этим опытом. Я действительно горд собой. За 2 недели подготовки мне получилось многое узнать, и в итоге чуть не попал в Яндекс. Это был челендж для меня, и я считаю, что с ним справился.</p>
]]></content></item></channel></rss>