<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>AVR-START.RU</title>
	<atom:link href="http://avr-start.ru/?feed=rss2" rel="self" type="application/rss+xml" />
	<link>https://avr-start.ru</link>
	<description>Электроника для начинающих</description>
	<lastBuildDate>Thu, 25 Dec 2025 23:22:06 +0000</lastBuildDate>
	<language>ru-RU</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>
	<item>
		<title>Репетитор С/C++, микроконтроллерам, электронике</title>
		<link>https://avr-start.ru/?p=5696</link>
					<comments>https://avr-start.ru/?p=5696#comments</comments>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Thu, 25 Dec 2025 23:15:24 +0000</pubDate>
				<category><![CDATA[Схемы]]></category>
		<guid isPermaLink="false">http://avr-start.ru/?p=5696</guid>

					<description><![CDATA[Привет! Для тех кто здесь в первый раз или не достаточно знаком с историей этого сайта. Я backend-разработчик и действующий программист. Открыт к обучению ребят, кто хочет разобраться в языке C/C++, микроконтроллерах и электронике. Разрабатывал высоконагруженные сервисы в Яндексе Разрабатывал мобильную ОС &#8212; Kaspersky OS Разрабатывал модели устройств для симулятора x86 платформ в Intel Писал [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Привет! Для тех кто здесь в первый раз или не достаточно знаком с историей этого сайта.<br></p>



<p>Я backend-разработчик и действующий программист. Открыт к обучению ребят, кто хочет разобраться в языке C/C++, микроконтроллерах и электронике.</p>



<span id="more-5696"></span>



<ul class="wp-block-list"><li>Разрабатывал высоконагруженные сервисы в Яндексе</li><li>Разрабатывал мобильную ОС &#8212; Kaspersky OS</li><li>Разрабатывал модели устройств для симулятора x86 платформ в Intel</li><li>Писал код, делал схемы, занимался разводкой плат, паял &#8212; все это в огромном количестве проектов на микроконтроллерах STM32.</li><li>Преподавал курс по С в OTUS</li><li>8+ лет в программировании</li></ul>



<p>Обучаю с нуля, а также помогаю  тем, кто уже имеет базу и хочет прокачать навыки. Формат занятий &#8212; лекция + практика. Разбираем реальные задачи, пишем код и обсуждаем ошибки.</p>



<p>Помогу:<br>&#8212; Объяснить сложные вещи простым языком<br>&#8212; Определить направление развития в IT <br>&#8212; Составить грамотное резюме<br>&#8212; Понять, на какие технологии делать упор <br>&#8212; Разобраться с темами, которые интересны именно вам</p>



<p>Мой стек: C/C++, QT, ООП, STL, Git, Linux.</p>



<p>Почта: avsttn@gmail.com</p>



<p>ТГ: @NullSysVoid</p>
]]></content:encoded>
					
					<wfw:commentRss>https://avr-start.ru/?feed=rss2&#038;p=5696</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Стоит или не стоит идти учиться на курсы?</title>
		<link>https://avr-start.ru/?p=5681</link>
					<comments>https://avr-start.ru/?p=5681#respond</comments>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Sat, 10 Feb 2024 23:28:21 +0000</pubDate>
				<category><![CDATA[Схемы]]></category>
		<guid isPermaLink="false">http://avr-start.ru/?p=5681</guid>

					<description><![CDATA[Прошел уже год, как я попал в образовательную систему, в связи с чем хотел бы поделиться опытом, кому стоит идти, кому не стоит и, главный вопрос, зачем идти. Первое, от чего бы хотел отгородиться: речь про образовательные системы, а не про курсы, в которых все от и до сделано одним человеком; здесь конкретно мой опыт, [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Прошел уже год, как я попал в образовательную систему, в связи с чем хотел бы поделиться опытом, кому стоит идти, кому не стоит и, главный вопрос, зачем идти.</p>



<span id="more-5681"></span>



<p>Первое, от чего бы хотел отгородиться:</p>



<ul class="wp-block-list"><li>речь про образовательные системы, а <strong>не</strong> про курсы, в которых все от и до сделано <strong>одним человеком</strong>;</li><li>здесь конкретно мой опыт, он может быть противоположным вашему;</li><li>пожалуйста, не пишите комментарии вида: &#171;а вот я проходил курс в ххх и там было бла бла бла&#187; &#8212; мне не нужен спам. Если курс отличный, то в рекламе он не нуждается</li></ul>



<p>Прежде чем идти дальше, предлагаю обсудить пару пунктов:</p>



<ul class="wp-block-list"><li>Платные курсы это в первую очередь бизнес.<br>Бизнес всегда нацелен на получение прибыли. Это не хорошо ни плохо. Просто нужно понимать, что обучение не является самоцелью. Даже если курс будет полной лажей, но туда будут приходить люди и приносить деньги, то никто и пальцем не шевельнет, чтобы что то изменить.</li><li>Ценник не влияет ни на что. Ни на качество, ни на сервис, т.е. заплатите ли вы 500р или 100 000р, это не гарантирует ровным счетом ничего. </li><li>Количество важнее качества. Проще вернуть деньги одному недовольному, чем разбираться с ним. Если в целом есть набор, то никто не почешется что то менять в процессе.</li></ul>



<p>Далее немного про иерархию. В курсе участвуют преподаватели, чаще всего их несколько. Так же есть методисты, кураторы, менеджеры и еще куча народу) У каждого своя роль, свои задачи.</p>



<p>К чему это все? К тому, что преподаватель не принимает решения по курсу, каким он будет. Да, его мнение имеет вес, но решения принимают совсем другие люди. Содержание курса, материалы, структуру могут разрабатывать совершенно разные люди. Довольно часто встречающаяся ситуация, когда курс сложен для новичков, но при этом бесполезен тем, кто владеет материалом выше базового уровня.<br></p>



<p>Теперь, попробуем разобраться зачем люди идут на курсы:</p>



<ul class="wp-block-list"><li>узнать что то новое;</li><li>трудоустройство</li><li>получить сертификат</li><li>получить мотивацию, потому что заставляют</li><li>познакомиться с единомышленниками</li><li>???</li></ul>



<p>Далее кто приходит на курсы:</p>



<ul class="wp-block-list"><li>полные новички</li><li>те, кто желает развиваться, но в начале пути</li><li>те, кто желает развиваться и имеет много опыта</li><li>собиратели сертификатов</li><li>те, кто пришел из смежных языков программирования</li></ul>



<p>Попробуем скомбинировать всю полученную ранее информацию.</p>



<p><strong>Какие проблемы могут быть у полного новичка:</strong><br>Независимо от того, на кого нацелен курс. Основная проблема будет в том, что скорее всего у вас не будет не только основных знаний, но и смежных, которыми априори считается что вы должны владеть. Базово &#8212; настроить компьютер и среду разработки под себя, написать минимальный код, уметь что то состряпать для себя. Освоить гит. Скорее всего, на все эти пункты уйдет много времени. Но, если вы сразу схватитесь за курс, то просто не будете успевать и забьете.<br></p>



<p>Все курсы, которые я видел, нелинейные. Сразу за hello world вам будут давать материал намного сложнее. Возможно, что то из смежных направлений, например про алгоритмы или про то как устройство архитектуры, или что то аналогичное. Проблема в том, что каждая из этих тем, это отдельное направление, к которому ваш мозг должен быть готов. Чтобы все это зашло и осталось место в оперативке в мозгу, нужно чтобы самые элементарные вещи не вызывали у вас боль.<br></p>



<p>Ютуб, гугль полны информацией для начинающих. На мой взгляд для того, чтобы поднять базу с полного нуля до минималки информации более чем достаточно.</p>



<p>Как бы я рекомендовал действовать в этом случае. Ознакомиться со структурой курса. У любого курса должна быть программа курса, т.е. оглавление со списком тем. Просто берем каждую из тем и тупо смотрим видосы, читаем про эту тему самостоятельно. Нет цели стать гуру темы, нужно ознакомиться, понять в целом применимость, крайне желательно пописать код на эту тему. Например, если речь про базы данных, то хотя настроить минимально базу и поделать запросы.</p>



<p><strong>Какие проблемы могут быть у тех, кто желает развиваться, но уже имеет много опыта. </strong><br>Речь идет про опытных разработчиков, которые варятся в сфере давно. </p>



<p>Проблема проста, конверсия полученного материала в полезные знания будет крайне низкая, по моим скромным прикидкам 5-10%. Обидно за то, что именно эти студенты доходят до конца, посещают все лекции, выполняют все домашние задания, но 90% негативных отзывов о курсе также оставляют они. Причина банальна, ожидания не оправданы.</p>



<p>Что можно тут порекомендовать? Стоят ли те 5-10% материала вложенных сил &#8212; решать лично вам. Может случиться, что именно эти знания принесут вам результат на собеседовании. Но в целом, смотреть записи выступлений с конференций, чтение специализированных книг, на мой взгляд принесут больше пользы. Просто найдите своего гуру, который вас вдохновляет, слушайте его и учитесь.</p>



<p><strong>Какие проблемы могут быть у тех, кто пришел с других языков программирования.</strong> <br>Тут хотелось бы тоже сделать ремарку. Если вы срочно меняете стек, то вы по адресу. В противном же случае, это бесполезно выкинутые деньги. Тут смысл простой, человек имеющий опыт других ЯП, пытается &#171;натянуть&#187; этот опыт туда, где совсем другие паттерны, совсем другие подходы.</p>



<p>В 100% случаев, что мне довелось видеть, эти люди не прогрессируют, начиная с первой по последнюю домашку, измений не видно. Очень частая реакция &#8212; &#171;я так делал в языке ххх и тут буду так же делать&#187; и плевать, что чтение файла вместо 2х секунд занимает 30.</p>



<p>Без юмора, перестроение мыслительного процесса это сложная задача, требующая времени и большого желания. Поэтому тут главная рекомендация, не заниматься решением задач, а копать вглубь, разобраться в мотивации того или иного подхода.</p>



<p>Если что то работает в одном ЯП, то это не означает, что оно будет также эффективно работать в другом, т.е. нужно разбираться именно с паттернами. Без этого вы ничего нового для себя не вынесете.</p>



<p><strong>Собиратели сертификатов</strong><br>Скажу честно, я тоже грешил этим. Исключительно мой опыт, но за все то время где меня собеседовали, всего один раз вскользь спросили, про курс по python.</p>



<p>Аналогично и в обратную сторону, я провел довольно большое количество собеседований в качестве собеседующего. Есть куча причин почему вам откажут или возьмут, но сертификат никогда на это не повлияет. Сертификат личная ачивка, ничего более.</p>



<p><strong>Проблемы тех, кто желает развиваться, но в начале пути</strong>.<br>Имхо, это вроде бы единственная категория, кто по адресу и кому материал может быть полезен.</p>



<p>И тут довольно частая история, когда студент сталкивается с серьезной задачей, героически ее преодолевает и&#8230; бросает курс. И это прям очень очень частая история. Прикол в том, что таких задач обычно не так много.</p>



<p>Тут можно порекомендовать тактику постоянства. Лучше взять подсказку, задать вопросы одногрупникам, поискать решения в сети, но дойти до конца, чем сломаться на одной задаче и бросить полностью.</p>



<p>Есть замечательный курс &#171;Программирование на С++&#187; от Александра Смаля, который я проходил несколько раз и каждый раз открывал для себя как заново. В первый раз, откровенно подглядывал в решения, т.к. мозг был просто не готов к тем трюкам, которые предлагались в качестве решения, но все же я прошел его до конца и это очень сильно пригодилось. Когда я вернулся спустя долгое время, то не смог понять что же там сложного, весь материал зашел как по маслу.</p>



<p>Теперь некоторые лайфхаки, которые можно предложить в качестве основы для принятия решения, о том, подходит ли вам курс или нет.</p>



<ul class="wp-block-list"><li>Топ 1, что вы должны сделать &#8212; ознакомиться со структурой курса. Любой курс предлагает оглавление бесплатно. На этом этапе уже можно понять, подходит ли вам материал. </li><li>Если на какую то тему выделено 1 занятие, значит вам толком ничего не успеют рассказать, будет типичная ознакомительная вода, которую можно точно также посмотреть на ютубе</li><li>Поискать домашки на гитхабе или гитлабе. Не секрет, что гитхаб полон решений домашек, с условиями задачи. Сразу можно сделать выводы нужно ли это вам или нет.</li><li>Понять кто преподаватель. Тут есть нюансы, препод может быть супер профи, но не уметь объяснять. Или просто быть душным. Или обладать сильно устаревшими знаниями, которые не имеют ничего общего с реальностью. Читаем описание курса, идем на ютуб и ищем кто такой этот препод, нравится ли вам его подача</li><li>Дополнительно, можно поискать видео лекций в сети и понять для себя, оправдает ли курс твои надежды или нет</li><li>Не рекомендую принимать решения только по отзывам. Даже если они реальные, то вряд ли вчерашний студент, сможет объективно оценить пользу от курса.</li><li>Узнать как проверяются домашние задания. Как человек, проверивший довольно большое количество дз вручную, я всегда старался дать максимально полную и подробную обратную связь. Считаю вообще это самым ценным, что можно получить от курса, но можно получить и противоположный эффект &#8212; поверхностные отписки. На мой взгляд, на начальном этапе куда важнее большее количество задач, но с автоматической проверкой, чем пара задач, но с ручной.</li></ul>



<p>Если вы уже взялись за курс</p>



<ul class="wp-block-list"><li>Ведите конспекты:<br>Минимально &#8212; записывать на бумажку. Слушаем лекцию, делаем заметки, о чем была лекция, какие темы затрагивались, какие фишки были рассказаны. Все в виде кратких тезисов.<br>Максимально &#8212; настоятельно советую, чтобы на выходе у вас появился какой то код в вашем репозитории, который использует материал из лекции.</li></ul>



<ul class="wp-block-list"><li>Обязательно пишите код, изучили про базу данных &#8212; накидываем проект, пытаемся что то сделать для себя. Цель &#8212; столкнуться с проблемами, чтобы задать вопросы преподам на следующей лекции.</li></ul>



<ul class="wp-block-list"><li>Делайте домашки. Цель домашки не в том, чтобы ее сдать. Цель столкнуться с проблемами и придумать их решение. Это максимально приближенная к работе ситуация.</li></ul>



<ul class="wp-block-list"><li>Будьте проактивным, читайте, задавайте вопросы, это ваша возможность, вы именно за это и заплатили, раскачивайте чат.</li></ul>
]]></content:encoded>
					
					<wfw:commentRss>https://avr-start.ru/?feed=rss2&#038;p=5681</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Перебор чисел</title>
		<link>https://avr-start.ru/?p=5659</link>
					<comments>https://avr-start.ru/?p=5659#respond</comments>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Mon, 12 Sep 2022 11:02:06 +0000</pubDate>
				<category><![CDATA[C++ roadmap]]></category>
		<guid isPermaLink="false">http://avr-start.ru/?p=5659</guid>

					<description><![CDATA[Кроме непосредственно тем по плюсам в роадмапе, есть еще маленькая строчка алгоритмы, которая включает в себя огромное количество информации и задач. Про бинарные деревья немного поговорили, сегодня про перебор чисел. Идея задачи получить все последовательности чисел/символов в определенном диапазоне. Например, получить все комбинации чисел 1, 2, 3. Результатом будет 27 комбинаций подобного вида (приведена только [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Кроме непосредственно тем по плюсам в роадмапе, есть еще маленькая строчка алгоритмы, которая включает в себя огромное количество информации и задач. Про бинарные деревья немного поговорили, сегодня про перебор чисел.</p>



<span id="more-5659"></span>



<p>Идея задачи получить все последовательности чисел/символов в определенном диапазоне. Например, получить все комбинации чисел 1, 2, 3. Результатом будет 27 комбинаций подобного вида (приведена только часть из них, но думаю идея понятна)</p>



<p>111
112
113
121
122
123
&#8230;
331
332
333</p>



<p>Прежде чем приступать к реализации, я бы хотел показать один прием, который упростит дальнейшее понимание задачи. Вспомним, как обычно мы делаем циклы. Например, выведем числа от 0 до 2</p>



<pre lang="cpp">
int max_i = 3;
for(int i = 0; i < max_i; i++)
{
     cout << i << " " << endl;
}
</pre>



<p>Теперь перепишем все это в рекурсивном виде. Пугаться не стоит, так как ничего нового мы не добавили, а всего лишь переписали в другом виде.</p>



<pre lang="cpp">
void helper(int i, int max_index)
{
    if (i == max_index)
    {
        return;
    }

    cout << "index = " << i << endl;
    helper(i + 1, max_index);
}

int main(int argc, char *argv[])
{
    helper(0, 3);
    return 0;
}
</pre>



<p>Попробуем разобраться детально по шагам. Как мы знаем, рекурсия это функция, которая вызывает сама себя. Главное в рекурсии - определить условие, когда мы выходим из функции, чтобы не было бесконечных вызовов. В нашем случае это if (i == max_index).</p>



<p>Теперь смотрим по шагам. </p>



<ol class="wp-block-list"><li>В функции main выполняется функция helper с параметрами 0 и 3, т.е. i = 0, i_max = 3. Все как в нашем for.</li><li>i равно 0 и не равно 3, т.е. из рекурсии не выходим. условие if (i == max_index) не выполняется</li><li>выводим в cout текущий i, он в этот момент равен 0</li><li>снова выполняем функцию helper но уже с параметрами 0+1 и 3, т.е. держим в уме что функция helper(0, 3) ждет своего часа, но именно в этой точке программы мы погружаемся в helper(1, 3)</li><li>снова идем мимо if (i == max_index) </li><li>выводим 1 на экран</li><li>погружаемся в helper(2, 3), но помним что helper(0, 3) и helper(1, 3) все еще не закончились, т.к. ждут когда остальные функции рекурсии выйдут.</li><li>снова идем мимо if (2 == 3)</li><li>выводим 2 на экран</li><li>Погружаемся в  helper(3, 3) и тут магия выполняется, т.е. выполняется if (i == max_index) и мы наконец то не погружаемся в рекурсию дальше.</li><li>Остается только закончить выполнение helper(2, 3), но так как там никакого кода больше нет, то просто выходим из этой функции и аналогично выходим из helper(1, 3) и helper(0, 3)</li></ol>



<p>Сложно? Когда первый раз этим обмазываешься - да. Но в конце концов ты смотришь только на вход в рекурсию и когда выход из нее, остальное не важно</p>



<p>Так и зачем же все эти страдания, если можно просто написать for и не мучаться? Ответ прост - масштабирование, к сожалению for требует от нас понимания входных параметров, в нашем случае нужно написать гибкое решение, когда не известно сколько чисел и какой длины нужно перебрать.</p>



<p>Итак, вернемся к нашей задаче, с помощью цикла выше мы будем задавать длину строки перебора. А вот хороший вопрос, сколько комбинаций будет в итоге, как это посчитать? Нужно разницу чисел, которые мы перебираем возвести в степень длины строки. Условно говоря мы перебираем числа от 0 до 2, соотвественно у нас 3 числа: 0, 1, 2 и длина строки, которую мы хотим получить 3, т.к. нас интересуют последовательности вида 0 0 0, 0 0 1, 0 0 2 и т.п. Итого 3 в степени 3, т.е. 27 комбинаций.</p>



<p>Теперь посмотрим на код, который нам позволит это сделать</p>



<pre lang="cpp">
void helper(vector<int>& res, int index, int max_index)
{
    if (index == max_index)
    {
        for(int i = 0; i < res.size(); i++)
            cout << res[i] << " ";
        cout << endl;
        return;
    }

    for(int i = 0; i < max_index; i++)
    {
        res[index] = i;
        helper(res, index + 1, max_index);
    }
}

int main(int argc, char *argv[])
{
    vector<int> res(3);
    helper(res, 0, 3);
    return 0;
}

</pre>



<p>Логика прежняя, за исключением небольшого нюанса с вектором, это заранее подготовленный вектор размером 3, заполненный нулями vector res(3);</p>



<ol class="wp-block-list"><li>Из main вызываем helper(res, 0, 3);</li><li>Идем мимо условия выхода из рекурсии так как наш цикл еще не закончен if (index == max_index)</li><li>Заходим в новый для нас цикл, for(int i = 0; i &lt; max_index; i++) он поможет на перебрать числа от 0 до 3 на каждом из уровней рекурсии.</li><li>Пока мы внутри helper(res, 0, 3) и i = 0, поэтому в res[0] = 0;</li><li>Погрузимся в рекурсию ниже т.е. helper(res, 1, 3);</li><li>Снова идет в цикл и присвоим в res[1] = 0; т.к. i = 0</li><li>Погрузимся в рекурсию ниже т.е. helper(res, 2, 3);</li><li>Аналогично в res[2] = 0; т.к. i = 0</li><li>Внутри helper(res, 3, 3); выводим на экран, то что находилось в res, а это напомню 0 0 0</li><li>Важно, что в этот момент мы выходим из рекурсии helper(res, 3, 3) и тут нужно вспомнить, а где мы находились до этого</li><li>А находились мы внутри helper(res, 2, 3); при этом напомню мы находимся внутри цикла где i = 0, т.е. цикл сделает i++ и начнется заново, т.е. в res[2] = 1; и снова погрузимся в helper(res, 3, 3); Просто магия какая то.</li><li>Внутри helper(res, 3, 3); снова распечатаем содержимое а это напомню 0 0 1</li></ol>



<p>Думаю дальше нет смысла расписывать, так как идея точно такая же.</p>



<p>А теперь еще немного магии, я всего лишь поменял, то что выводить и теперь та же самая программа умеет печатать все комбинации вида </p>



<pre lang="cpp">
a a a 
a a b 
a a c 
a b a 
a b b 
a b c 
</pre>



<pre lang="cpp">
void helper(vector<char>& res, int index, int max_index)
{
    if (index == max_index)
    {
        for(int i = 0; i < res.size(); i++)
            cout << res[i] << " ";
        cout << endl;
        return;
    }

    for(int i = 'a'; i <= 'c'; i++)
    {
        res[index] = i;
        helper(res, index + 1, max_index);
    }
}

int main(int argc, char *argv[])
{
    vector<char> res(3);
    helper(res, 0, 3);
    return 0;
}
</pre>



<p>Думаю на этом можно закончить, возможно кому то пригодится в задачах, где нужно перебрать все возможные последовательности или хотя бы прибавит понимания, как в принципе решаются подобные задачи.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://avr-start.ru/?feed=rss2&#038;p=5659</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Docker. Небольшой обзор</title>
		<link>https://avr-start.ru/?p=5653</link>
					<comments>https://avr-start.ru/?p=5653#respond</comments>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Sun, 11 Sep 2022 11:48:22 +0000</pubDate>
				<category><![CDATA[C++ roadmap]]></category>
		<guid isPermaLink="false">http://avr-start.ru/?p=5653</guid>

					<description><![CDATA[Сегодня затронем еще одну из тем из roadmap, из разряда optional, но с которой стоит хотя бы поверхностно ознакомиться. Речь идет про Docker, ниже будет краткое ознакомление . Итак, немного разберемся зачем нужен докер. Самый жизненный пример. Предположим есть несколько разработчиков, которые работают над одним проектом. Допустим, вы поработали локально над кодом, вы молодец, теперь [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Сегодня затронем еще одну из тем из roadmap, из разряда optional, но с которой стоит хотя бы поверхностно ознакомиться. Речь идет про Docker, ниже будет краткое ознакомление .</p>



<span id="more-5653"></span>



<p>Итак, немного разберемся зачем нужен докер. Самый жизненный пример. Предположим есть несколько разработчиков, которые работают над одним проектом. </p>



<p>Допустим, вы поработали локально над кодом, вы молодец, теперь нужно прогнать тесты. Логично для таких целей использовать отдельный компьютер, чтобы проверить что тест работает не только на вашей машине, но и на любой другой. Чтобы не покупать под каждого разработчика такой комп, берут отдельный сервак, достаточно мощный чтобы обслуживать одновременно кучу разработчиков. </p>



<p>Теперь предположим, два разработчика отправили свой код тестироваться на один и тот же сервер. При этом вполне возможна ситуация, что один из разработчиков модифицировал одну из библиотек. Поэтому, если второй разработчик воспользуется той же библиотекой, то вполне возможно, что его тесты будут работать не правильно. Таким образом нам нужно каким то образом изолировать выполнение тестов друг от друга.</p>



<p>Другой пример. Вы собрали программу, под определенное окружение, как это передать другому человеку, чтобы он не мучался, настраивая подобное окружение. Искушенный пользователь скажет, что для этого вполне может подойти виртуальная машина, например Virtual Box и будет по своему прав. Но тут есть два минуса, первое что образа огромные, второе производительность виртуальной машины довольно не высока.</p>



<p>Докер в свою очередь помогает решить подобные проблемы. И его вполне можно представлять себе, чем то похожим на виртуалку, но в отличие от нее от не эмулирует работу железа, а эмулирует только вызовы операционной системы, за счет чего в теории он будет работать быстрее, чем виртуальная машина.</p>



<p>В своей практике, я ежедневно пользуюсь docker при разработке и это удобно, так как я знаю что у других разработчиков будет точно такое же окружение, те же версии пакетов что и у меня, такие же библиотеки. Если случайно удалишь какую то директорию, то не поломаешь основную файловую систему.</p>



<p>Думаю, что приводить процесс установки и запуска hello world не имеет смысла. Предлагаю ознакомиться сразу с тем, как создать образ, который можно было бы перетащить на другой компухтер. Сразу обозначу, что пользуюсь Ubuntu 20.04 поэтому далее речь будет в этом контексте.</p>



<p>Создаем файл с именем Dockerfile, который будет описывать содержимое образа. Внутри него помещаем следущий текст</p>



<pre lang="cpp">
FROM ubuntu:20.04
</pre>



<p>Тут немного пояснений. Нам нужно указать какой образ будет использоваться в качестве базового, чтобы самим не создавать файловую систему с сисрутом. FROM как раз и говорит о том, что используй базовый образ ubuntu:20.04. </p>



<p>Базовый образ ubuntu:20.04 взялся не с потолка, добрый люди из canonical его подготовили руками за нас. Когда начнется сборка контейнера и локально этот файл не будет найден, то докер полезет в свою базу, найдет этот образ и заботливо его скачает за нас. После этого образ будет уже находиться локально. На сайте docker можно найти огромное количество уже заранее подготовленных образов на любой вкус и цвет.</p>



<p>После этого сохраняем файл и выполняем собираем контейнер</p>



<pre lang="cpp">
docker build -t test/ubuntu:v1 . 
</pre>



<p>И запускаем его</p>



<pre lang="cpp">
docker run -it test/ubuntu:v1
</pre>



<p>после этого коммандная строка будет вида</p>



<pre lang="cpp">
root@0b9:/#
</pre>



<p>Можем посмотреть через top список запущенных программ и увидим что то вроде </p>



<pre lang="cpp">
PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND                  
       1 root      20   0    4116   3388   2948 S   0.0   0.0   0:00.04 bash                     
       9 root      20   0    6100   3380   2880 R   0.0   0.0   0:00.00 top
</pre>



<p>Если мы захотим выйти из докера, то пишем</p>



<pre lang="cpp">exit</pre>



<p></p>



<p>Посмотреть список запущенных контейнеров можно через</p>



<pre lang="cpp"> docker ps -a</pre>



<p>Если мы захотим что то добавить в образ, например установить htop, то можно дописать в Dockerfile</p>



<pre lang="cpp"> RUN apt-get update && apt-get install -y htop</pre>



<p>И заново собрать через docker build. Надо отметить, что сборка контейнера это не сборка готового образа, который можно передать, так как образ ubuntu:20.04 может использоваться в куче разных контейнеров и будет жирно его копировать в каждый из образов, поэтому в контексте докера обычно используют сравнение с слоями. Поэтому стоит рассматривать Dockerfile, как некий рецепт, каждая строчка которого описывает какой из слоев нужно запустить. </p>



<p>Если мы хотим все это добро экспортировать на другую машину, то смотрим список доступных образов</p>



<pre lang="cpp">docker images</pre>



<p>Сохраняем его как tar архив и несем на другой комп</p>



<pre lang="cpp">docker save -o ./my.tar test/ubuntu</pre>



<p>На другом компьютере загружаем его</p>



<pre lang="cpp">docker load -i my.tar</pre>



<p>В кратце по минимуму это все, далее можно копать про объединение контейнеров в docker compose, но оставим эту тему на другой раз.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://avr-start.ru/?feed=rss2&#038;p=5653</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Lambda функции</title>
		<link>https://avr-start.ru/?p=5631</link>
					<comments>https://avr-start.ru/?p=5631#respond</comments>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Mon, 13 Jun 2022 11:00:11 +0000</pubDate>
				<category><![CDATA[C++ roadmap]]></category>
		<guid isPermaLink="false">http://avr-start.ru/?p=5631</guid>

					<description><![CDATA[Медленно, но верно закрываем еще одну тему из нашей roadmap. Начнем с простого примера. Допустим у нас есть массив v и нам понадобилось вывести все четные числа, т.е. если остаток от деления равен нулю. int main(int argc, char *argv[]) { std::vector v {1,2,3,4,5,6,7,9}; for (int i = 0; i < v.size(); i++) { if (v[i] [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Медленно, но верно закрываем еще одну тему из нашей roadmap.</p>



<span id="more-5631"></span>



<p>Начнем с простого примера. Допустим у нас есть массив v и нам понадобилось вывести все четные числа, т.е. если остаток от деления равен нулю.</p>



<pre lang="cpp">
int main(int argc, char *argv[])
{
    std::vector<int> v {1,2,3,4,5,6,7,9};

    for (int i = 0; i < v.size(); i++)
    {
        if (v[i] % 2 == 0)
        {
            std::cout << v[i] << endl;
        }
    }
    return 0;
}
</pre>



<p>Результат выполнения такой программы: 2, 4, 6</p>



<p>Можем ли мы как то улучшить данный код? Как вариант можно воспользоваться функцией из стандартной библиотеки std::all_of, которая перебирает все элементы, которые возвращают true. Утрировано синтаксис у нее такой:</p>



<pre lang="cpp">
std::all_of(первый элемент, последний элемент, что выполнить) 
</pre>



<p>С первыми двумя параметрами вроде все должно быть понятно, т.е. откуда начинать и до какого элемента перебирать. Третий параметр называется предикатом, по сути это просто функция, в которую будут по очереди передаваться элементы, начиная с первого и до последнего. Особенностью именно функции all_of будет то, что чтобы обойти все элементы, предикат должен возвращать true. </p>



<p>Итого у нас вырисовывается прототип функции, принимает один параметр на каждом шаге, в нашем случае это int и возвращает bool, в нашем случае это true. Перепишем код с учетом этих знаний.<br></p>



<pre lang="cpp">
bool is_even(int x)
{
    if (x % 2 == 0)
    {
        std::cout << x << endl;
    }
    return true;
}

int main(int argc, char *argv[])
{
    std::vector<int> v {1,2,3,4,5,6,7,9};
    std::all_of(begin(v), end(v), is_even);

    return 0;
}
</pre>



<p>В целом это выглядит более красиво и на этом можно было бы и закончить, но вот проблема в том, что у нас есть функция, которая вызывается единожды и нигде больше и не нужна по сути. И вот для таких случаев придумали лямбды.</p>



<p>Чтобы проще было понять, перепишем тот же код, но с помощью лямбда выражения. </p>



<pre lang="cpp">
int main(int argc, char *argv[])
{
    std::vector<int> v {1,2,3,4,5,6,7,9};

    auto is_even = [](int x) {
        if(x % 2 == 0)
        {
            std::cout << x << endl;
        }
        return true;
    };

    std::all_of(begin(v), end(v), is_even);

    return 0;
}
</pre>



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



<p>Ну и дабы совсем упороться и сделать код похожим на профессиональный, можно записать лямбду прям в месте вызова функции.</p>



<pre lang="cpp">
int main(int argc, char *argv[])
{
    std::vector<int> v {1,2,3,4,5,6,7,9};
    std::all_of(begin(v), end(v), [](int x) {
        if(x % 2 == 0) 
            std::cout << x << endl; 
        return true;
    });
    return 0;
}
</pre>



<p>Далее поговорим об еще нескольких деталях. Допустим наша задача, взять тот же вектор и умножить его на некоторое число. Для этого мы объявим его int val = 5. И тут важно осознавать, что лямбда функция ведет себя как отдельная, т.е. val это локальная переменная main и для функции она не может быть видимой. Чтобы исправить это мы указываем в квадратных скобках, что val требуется захватить, т.е. чтобы внутри lambda она стала видимой. С учетом этих знаний перепишем код, чтобы умножить каждый элемент массива на число.</p>



<pre lang="cpp">
int main(int argc, char *argv[])
{
    std::vector<int> v {1,2,3,4,5,6,7,9};
    int val = 5;

    std::all_of(begin(v), end(v), [val](int x) {
        x *= val;
        std::cout << x << endl;
        return true;
    });
    return 0;
}
</pre>



<p>Так как в функцию мы передаем int x, т.е. по значению то на экран выведутся числа умноженные на 5, но сам исходный массив не изменится. Если мы хотели бы изменить исходный массив, то должны были бы передавать значения по ссылке int &amp;x.</p>



<p>И последнее что можно упомянуть, что захват можно делать не только по конкретному значению val, а можно захватить все переменные в области видимости, т.е. для захвата всех переменных по ссылке предыдущий код можно было бы переписать так:</p>



<pre lang="cpp">
std::all_of(begin(v), end(v), [&](int x) {
    x *= val;
    std::cout << x << endl;
    return true;
});
</pre>



<p>В этом случае внутри лямбды можно было бы изменять переменную val, если бы это потребовалось.</p>



<p>Для захвата по значению указываем [=], т.е. изменяя внутри функции val снаружи оно не изменится.</p>



<pre lang="cpp">
std::all_of(begin(v), end(v), [=](int x) {
    x *= val;
    std::cout << x << endl;
    return true;
});
</pre>



<p>Итого, могу сказать про себя лично, долгое время я отвергал использование лямбд, в первую очередь потому что код с ними читается чаще хуже. </p>



<p>Отношение сильно поменялось, когда начал решать задачки на время. Когда ты знаешь какие функции из библиотеки алгоритмов могут пригодиться в данном случае, то как бы использование лямбд совместно с той или иной функцией даже не вызывает вопросы. Поэтому пробуйте, изучайте, применяйте.<br></p>
]]></content:encoded>
					
					<wfw:commentRss>https://avr-start.ru/?feed=rss2&#038;p=5631</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
