Developer's notes
33 subscribers
67 photos
4 videos
74 links
Пишу обо всём и ни о чём, могу и о программировании
Download Telegram
Пусть говорят!

Ни для кого не является секретом распространенное мнение, что программисты и прочие ИТшники разговаривать не умеют, ибо а) им не с кем, кроме компьютера б) просто не нужно. Видели бы эти люди количество совещаний в день рядового разработчика и тем более team leader’а… На самом деле, практически ни один серьезный программный продукт не может быть сделан “в одного” – подозреваю, что внутрикомандная коммуникация среди разработчиков превышает аналогичную среди других “белых воротничков”: бухгалтеров, юристов, маркетологов. Добавьте сюда популярные в индустрии разговоры о T-shaped skills, учтите, что в обычной среднестатистической продуктовой команде кроме программистов присутствуют такие роли как аналитики, тестировщики (возможно, дизайнеры и DevOps) – и, надеюсь, вы начнёте что-то подозревать.

“Им просто присылают задания, а они сидят и пишут код в соответствие с ними – просто и четко”, – могут сказать другие. И это – другое распространённое и неправильное мнение. Реальная жизнь и реальная разработка максимально далеки как от примеров Вышмата из моего предыдущего поста, так и от задач с Leetcode по следующим причинам:

а) всё, что можно сделать “по шаблону”, автоматически – уже давно так и делается, индустрия справилось с этим ещё до появления ChatGPT

б) Главная и основная загвоздка – а кто и как напишет это сверх подробное и точное ТЗ, что я только код быстро напишу по нему и сразу и все тесты пройду и в продакшн выложу?

Так же замечу, что, диаграмма классов в Confluence и документирование кода – это прекрасно, но все же не может полностью заменить созвон на тему “Что мы вообще хотим добиться и как нам не провести все выходные дебажа этот код”.

#job #IT #General #Около_ИТшное
👍1
Административное.

Хочется отметить: канал не носит сентиментально-лирического характера, технический контент скоро подвезут.
#today #administrative #this_channel
Состояние потока.

Жара только усиливается – пообещали нам +34 сегодня. Такая погода явно требует некой жест^ технического контента. И вспомнилось мне одно собеседование и тестовое задание на нём. Как я уже говорил: собеседования – это отдельный жанр (кринжа), при этом если вы разработчик и хотите получать адекватную зарплату – зло абсолютно неизбежное. Если учесть, что многие компании имеют 2ух – 6ти ступенчатые собеседования, то проведя достаточно лет в индустрии, есть шанс пройти собеседований не меньше, чем было экзаменов в Вузе.

Впрочем, в эту довольно известную и крупную российскую компанию, менявшую имя несколько раз, собеседования не шокировали числом и сложностью, всё было довольно стандартно, кроме тестового задания: созвон с HR, тестовое задание, созвон с командой, созвон с тех директор чего-то там. В итоге не взяли на последнем этапе – скорее всего нашли разработчика подешевле, никаких сильно отрицательных эмоций это не вызвало у меня, впрочем, положительных тоже. Поскольку собеседования, как известно, не покрыты NDA, ничто не сдерживает меня от опубликования тестового задания и его решения.

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

Нужно написать консольное приложение на C++, в нём должно быть 2 потока, один из них выводит ‘1’ в стандартный вывод, второй выводит ‘2’ в стандартный вывод, при это единицы и двойки всё время чередуются, переносов строк – нет, программа работает бесконечно. То есть вывод должен быть “12121212….”. Использовать потоки и средства синхронизации из стандартной библиотеки (std).

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

#include <iostream>
#include <thread>

using namespace std;

int main()
{

auto firstRoutine = [](){
while (true){
cout << '1';
}
};

auto secondRoutine = [](){

while (true){
cout << '2';
}
};

thread th1{firstRoutine};
thread th2{secondRoutine};


th1.join();
th2.join();

return 0;
}

#job #C++ #Около_ИТшное #interview #собесед #today #memories #c_plus_plus #IT
🔥2
Тяжелые хвосты

Жара спала, и в целом, наступила суббота на наш замечательный перенаселённый город.  Вспомнилось мне, что когда-то я усиленно учился теории вероятности и всякой статистике, однако занудствовать прям с формулами мне не хочется, давайте лучше немного порассуждаем. Для этого возьмём очень наглядную и физическую вещь – рост взрослого мужчины (старше 18 лет), вспомните ваших знакомых, и просто людей, которых вы видите на улице, предполагаю, что их рост будет, в основном, варьироваться между 160 и 195 сантиметрами, причём пограничные случаи будут довольно редки (предполагается, что вы не зависаете в компании баскетболистов).  Можно встретить человека выше или ниже? Да, определённо, особенно если живёте в городе-миллионике и ездите на метро, а часто ли? –  Ну не то, чтобы. Вот вам статья на тему, что людей сильно выше совсем немного, учитывая, что на планете живёт несколько миллиардов человек.

Надеюсь, к этому моменту все осознали, что рост, в основном, размазан в определённом диапазоне, а теперь я вам скажу, что это всё давно формализовано и изложено в виде тех самых скучных формул, что я не буду тут упоминать, и называется Нормальным или Гауссовым распределением. В трёх словах его суть в следующем: если по оси X отложить рост, а по оси Y отложить процент, как часто данный рост встречается в популяции, то получится колоколообразная фигура с максимумом у среднего значения, более того, с крайне высокой вероятность рост не отклоняется дальше определённой величины. А вот тут для нас уже всё нарисовали, листайте до слов “Height is normally distributed”. И да…с большой вероятностью в Вузе вас пытались этому научить даже на нетехнической специальности.

А все ли величины распределены согласно этому распределению?  –  Очевидно, что нет – иначе почему учебник математической статистики такой толстый и не заканчивается на нормальном распределении.  А если серьёзнее, можно рассмотреть другую очень жизненную величину – доходы отдельно взятого человека. Может показаться, что величина эта не случайна – и действительно, для каждого отдельного человека она зависит от множества факторов, но мы эти факторы не изучаем, поэтому считаем, что она случайна.  Попробуем тут порассуждать примерно, как про рост: и так, вы знаете собственную зарплату, можете предположить, что люди в той же должности и отрасли получают примерно столько ж (умножить поделить на 3), а вот там элитный район в городе – там, видимо, доходы побольше, а вот люди ездят на теслах – у них ещё побольше, ну и все мы знаем про существование списка Форбс. Тут важно заметить, что учителей и врачей довольно много – миллионы, элитные районы и поселки явно поменьше, на теслах, Бентли и прочая ездят ещё поменьше, а список Форбс это меньше трёх тысяч людей на всём свете. И тут я опять не открываю Америку – всё давно посчитано и написано, просто посмотрите на первую картинку — там максимум концентрируется в районе низких значений и потом хвост бесконечно уходит вправо. Если применить на пример выше сотни миллионов людей получают несколько сот долларов в месяц, но потом всё реже и реже идут экземпляры с более высоким доходом, заканчивая единичными случаями почти 20 миллиардов в месяц. А если применить на пример с ростом, получалось бы примерно так: большинство людей в районе 170 см, но можно найти хоть и редко ростом с Эйфелеву башню, Эмпайр-стейт-билдинг, от Земли до Плутона…

Подобные распределения называются - распределениями с тяжелыми хвостами, когда-то я читал книжку "Черный Лебедь" – не дочитал, бросил на середине, потому что к тому моменту я уже тер. вер. знал :)

#today #flood #education #math #probability_theory #books
🔥1
А есть ли вероятность?

Выходные продолжаются, как и наша рубрика “Тервер для самых маленьких”. Если вы открывали и пробежались глазами через эту ссылку в прошлой статье, то могли заметить довольно-таки очевидные вещи: рост зависит от многих факторов, включая генетические и нет, средний рост разный в разных поколениях, в разных регионах мира средний рост сильно отличается – жители Черногории высокие даже для европейцев, жители Таиланда – довольно низкие. Это означает, что, если б мы взяли данные по одному регионы, а желательно ещё и моноэтнические и только людей одного и того же года рождения, то получили бы тот же самый “колокол”, но более узкий. Кстати, ширина “колокола” обусловлена так называемым среднеквадратичным отклонением – СКО (квадратным корнем из дисперсии). Воздействие же множества довольно разнообразных факторов – причина, приводящая к нормальному распределению роста.

А есть ли другие величины, распределённые согласно нормальному распределению? Конечно! И очень много, особенно, это касается настоящих физических величин: результатов экспериментов, измерений, результатов работы производственных линий, да хоть линии производства мороженного, объема кофе из кофемашины при одинаковых настройках и т.д. Если вдруг кто не верит, что даже заводские продукты ежедневного потребления немного варьируются, то вот ссылка. Другое дело, что, когда погрешность (3 * СКО), много меньше номинального значения (среднего значения) конечному потребителю не стоит и волноваться – так в приведенной ссылке они обещают, что-то вроде +- 5 грамм на килограмм. Обратите внимание, я написал +-, это не значит, что, если вы купите 100 пакетов молока, вы получите недовесок или перевесок в 500 грамм, а вот настоящий результат тут – это тема для отдельной статьи.

Тут можно заодно упомянуть наивное заблуждение, что в “точных” науках всё абсолютно точно. Нет – все физические величины всегда идут с погрешность, максимум, что возможно сделать это ограничить эту величину при соблюдении определённых условий. И касается это не только пищевых продуктов, но и высокотехнологичных производств: производство CPU требует миллиардов долларов, да и каждое отдельное изделие стоит несколько сот долларов, тем не менее производитель не может штамповать все процессоры с одинаково хорошими заданными характеристиками даже на одной и той же линии, вместо этого постфактум их тестируют после чего классифицируют кто тут Core-i5, ну а кто Core-i3.

#today #flood #math #education #probability_theory
🔥2
Куда оно летит?

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

Шёл далекий 2018ый год, Android был популярен примерно, как сейчас, приложения под него не писал только ленивый, компания, где мы работали тоже их писала, причём довольно активно, наш же проект на C++ подходил к жизненной стадии излёта – поддержка, поддержка, новых фич очень мало и не планируется. Всё закономерно: есть такое понятие жизненный цикл продукта, а программное обеспечение – тоже продукт, но об этом поговорим как-нибудь в другой раз. Компания занимала тогда весьма спокойную позицию по поводу таких проектов, которую можно описать словами: если они хотят работать, пусть работают как могут, мы их увольнять не будем (но и зарплату повышать – тоже). Соответственно, когда в ходе очередной беседы с менеджментом поступило предложение позаниматься Android (без предварительно имеющегося опыта), конечно, я и мой коллега тут же согласились.

Конкретика, звучала тоже довольно интересно: разработать с нуля приложение в несколько десятков экранов, взаимодействующая с backend и так далее, и кстати, — вот вам аналитик/прожект менеджер в придачу и “наставник” – опытный программист Android с флагманского проекта компании. Огонь! Описания функционала и предметной области тут не будет – это не так важно.

Глянув документацию по Java и код в соседнем проекте, осознал, что Java – это C++ из которого выкинули всё самое сложное (жду ваших гневных комментариев), и вся сложность в особенностях Android SDK: жизненный цикл Activity, жизненный цикл Fragment, разветвлённые переходы между экранами… Наставник тут помог: “вам нужно Moxy, Cicerone, RxJava, Retrofit и, конечно, Dagger”. Сказано – сделано, не все эти технологии выглядят очевидно для разработчика на C++, но, в принципе, не rocket science и по сравнению с голым AndroidSDK разработка упрощалась.

Заметка получается длинной, поэтому продолжим в следующий раз.

#job #IT #Android #Java #memories #ToBeContinued #interesting_tasks
👍1🔥1
Куда оно летит?

Часть 2.

В прошлый раз мы остановились на самом начале процесса разработки. Помня свой успешный (а иногда не очень) опыт из Desktop разработки, мы с коллегой начали с разработки архитектуры, было желание сделать сначала “каркас”, а потом планомерно его наполнять экранами, логикой и так далее, были там и более интересные технологические моменты: библиотека на C++ для обработки звука, кое-что там втягивали стороннее для обработки фото и тд.

Наша команда постепенно наполнялась: присоединились дизайнер, пара бэкендеров, сильно позже – тестировщик. Работать было интересно и комфортно – с моим напарником, другим разработчиком C++/Android никаких конфликтов никогда не бывало, задачи мы пилили сами, как считали справедливым, с дизайнером бывали абсолютно типичные моменты: дизайн бывал настолько красивый (сложный) – непонятно, точно ли нужно тратить трудодни на его реализацию – решали с участием упомянутого в прошлый раз аналитика/прожект менеджера, обладающего крайне высокими компетенциями и знаниями (здесь нет никакого сарказма). Разработчиками backend были двое студентов (возможно, вчерашних), из всей команды они выделялись своей незаинтересованностью в результате и срывом сроков – бывало трудновато, но опять же, всё в пределах нормы.

Замечу, что работать в любом проекте с нуля, совсем ни то же самое, что приходить в проект на других стадиях жизненного цикла. И дело тут не в атмосфере стартапа – её на самом деле не было, приложение нужно было разработать ни “как можно быстрее, что б отбить денег”, а к фиксированному сроку, ТЗ было практически фиксировано с первого дня и менялось очень мало – редкий случай. Дело тут в том, что гораздо интереснее самому вырабатывать архитектуру и потом работать, зная её особенности.

Коммиты успешно заливались в git, задачки пилились и брались в темпе, который нравился прожект-менеджеру, приложение стабильно прибавляло экраны, запросы, обработку ошибок, а Scrum не обрастал “религиозными встречами” – всё шло прекрасно.

Продолжим в следящий раз: опять не хватило пространства выдать сюжетный поворот.

#job #IT #Android #Java #memories #ToBeContinued #working_days
👍1
Куда оно летит?

Часть 3.

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

Где-то в 2019ом году, состоялся первый релиз приложения. Как я уже упоминал, приложение – не настоящий стартап, иначе говоря, его функционал лежал вне основных интереса бизнеса, поэтому после релиза дальнейших задач по этому приложению не было (и не планировалось – это не было сюрпризом). Что несколько удивляло – так это реакция менеджмента: “Отлично, что вы с этим справились, мы подумаем, что дальше, а пока занимайтесь поддержкой своих проектов”.

Теперь, зная, что было дальше, я понимаю: нужно было уволиться в тот же момент спокойно (или не очень), пройдя собеседования на Android-senior в другие компании. На тот момент, лояльность компании, иллюзия что “они найдут чем заниматься” и так далее удержали меня от этого шага.

Спустя несколько месяцев, мы вернулись к разработке этого приложения: кое-что писали уже на Kotlin, активно продвигаемый Google, на нас же свалили и поддержку backend для этого приложения, так же (коряво) написанный на Kotlin, но…объем фич был уже ни тот, и за это время технологии модные ещё вчера стали устаревать. Да, до сих пор некоторые технологии из моего первого поста используются в реальных проектах, но в тренде прежде всего Kotlin, Coroutines, Clean Architecture

В следующий раз, я точно закончу этот цикл! Будьте на связи

#job #IT #Android #Java #memories #ToBeContinued #working_days
Неожиданные проблемы нативных типов

Недавно попалась на глаза статья про редкие языки программирования и была там фраза, что мол в Cobol есть отдельный тип для представления денег…и я вспомнил, что когда-то, уже очень давно, я и мои коллеги, тоже сталкивались с проблемами представления денежных сумм в C++. Казалось бы – ерунда какая? В C++ (Java, C# – туда же) есть типы float и double для представления чисел с запятой, почему бы их не использовать? Потому что они не подходят: float уже на вот таком примере “сломается”:

    float a = 0.01;
float b = 999999.00;
float sum = a + b;
std::cout << sum << std::endl;


Выведя 999999.

С double надо искать пример посложнее, но, поверьте, когда его найдут у вас в программе – будет не до смеха. Тут можно отметить пару основных недостатков: большинство дробей типа 0.1, 0.2 и т.д. не имеют конечного представления в двоичной системе, при сложениях и вычитаниях эти числа “выравниваются” внутри что б быть представлены с той же самой степень, а число знаков, выделенных под мантиссу – конечно, можно тут потерять одно из чисел совсем, как в примере выше.

Как же хранить денежные величины в C++ – легко, храните их (в Сберегательной кассе) как целое – число копеек, используя long long int, перегрузите соответствующие операторы конструкторы и так далее. Забавно, что в Standard C++ library нет ничего подходящего для решения проблемы, как и в Qt. В sql есть, например, decimal.

Тут можно заметить, что основные типы данных в C++, по сути, те же, что в языке Си, в котором они те же, что дает архитектура процессора – т.к. язык Си – это высокоуровневый ассемблер, то есть байт, 2 байта, 4, возможно 8 для целых типов, и 4, 8 байтов для чисел с плавающей запятой. Так оно и тянется с тех пор… В тоже время, те же double/float неплохо подходят для инженерных расчетов, если бы в примере выше мы бы считали силу тока, движущий момент, любую другую физическую величину, никого бы не взволновала потеря одной сотой, на фоне величины числа a – эта ошибка вписывается в допустимую погрешность.

#job #IT #c_plus_plus #math
👍1
Спасибо копипасту

В программисткой среде известный метод “скопировать-и-вставить” с одной стороны считается чем-то, вроде, дурного тона – с другой стороны все так делают. Действительно: какой нормальный человек будет заново изобретать то, что он уже написал и работает если можно это скопировать и, вот тут самое главное – поправить и адаптировать – так что б это не выглядело копипастом. Ещё раз: речь не идёт про код в стиле макароны, индуский код, незнание SOLID и так далее – речь про то, что это рабочая методика где-то глубоко до открытия merge request и прохождения code-review. В наши дни многие уже умудряются писать код с помощью ChatGpt – на этом фоне про копипаст как нечто плохое и вовсе пора забыть...

Однако, я хотел вспомнить случай, когда копипаст мне помог решить задачу, сэкономив недели, а может быть месяцы ковыряния в спецификации. Дело было во времена моего участия в проекте из поста: пару месяцев уже прибывая там, я взял очередной баг, формулировка у него была что-то навроде: “flex layout не обрабатывает какой-то пограничный случай вот картинка как надо, вот что у нас” – дополнительная инфа от других разработчиков была – вот смотри тут в коде ссылка на спецификацию, ты почитай её. Ладно…звучит, как бага на пару строк кода, конечно, осталось найти куда эти строки вставить и сообразить, что там написать, ну в плохом варианте это неделя или две.

До того момента я про flex layout знал чуть меньше, чем ничего: HTML я глубоко не занимался, в Qt свои layouts, к тому же, мне никогда не нужно было их создавать – только использовать готовые, к слову, flex layout это совсем не QGridLayout. Глянув ещё раз пример, что должно быть и что было, глянув кратко спецификацию, я решил, что проще всего будет отдебажить, почему тут неправильно считается, это заняло дня 3: я нашёл, где поправить на пару строк и довольный открыл пулл реквест…Каково же было моё удивление узнать, что на самом деле, я должен был сделать не маленький фикс, а реализовать вот этот алгоритм из спецификации.

Тут мне взгруснулось…дело явно на месяц(ы), спецификация написана несколько пространным языком, не всё ясно как приземлять на то, что я имею уже в коде, конечно, это возможность стать “уникальным” специалистом по этой микро-нано области, но оно мне зачем? Очень быстро понял, что единственный рабочий вариант тут для меня – найти, где это уже реализовано, причём, понятно, что это должно быть реализовано. Нашёл исходной код проекта WebKit там довольно быстро нашёл именно этот алгоритм – тут дело уже пошло веселее: из WebKit можно было “перевести” в наш проект, надо было только учесть, что в WebKit всё аккуратно, размазано по своим классам, а у нас было свалено куда придётся, но это не важно. Был именно момент “copy-paste” , когда я скопировал кусок кода оттуда и потом просто адаптировал его, после чего уже успешно прошёл код-ревью и закрыл задачу…

#job #c_plus_plus #IT #web #HTML
🔥1
Сравните это

Часть 1.

Сегодня будет длинный пост, ещё и с кодом, так получается – ничего не могу поделать с этим.

Итак, я взял простенькую задачку с Leetcode: нужно написать одну функцию, принимающую на вход строку длинной от 1 до 1000 символом, символ – буква латинского алфавита в нижнем регистре т.е. a…z, функция должна вернуть true если все буквы встречаются в строке одинаковое количество раз, т.е. “a”, “ab”, “azzabb” – примеры входных данных на которых нужно вернуть true, а “aza”, “bzcc” – примеры на которых нужно вернуть false. Что удобно на Leetcode – можно просто написать решение нажать Run, Submit – и увидеть, работает оно или нет, и если да – то оно сравнит его скорость с другими решениями.

Задача понятная, задача простая…первым шагом нужно посчитать частоты встречающихся в слове букв и сохранить их. Вторым шагом нужно проверить, что они все одинаковы, т.е. выглядит так, что нужно написать 2 цикла, первый будет сложность O(n), где n – длина строки, второй тоже O(1) – потому, что букв в английском алфавите всего 26.

Тут зададимся вопросом, что мы, собственно говоря, можем и должны всегда стараться использовать в языке C++ - правильно, STD, а конкретно – его контейнеры и алгоритмы, тут и Leetcode нам подсказывает, изначально выдавая в наброске решения входным параметром функции типа std::string, а не бестолковый const char* времён Си. Но, что собственно может std::string для решения этой задачи – а ничего…range-based for завезли в C++ 11, это немного сократит нам код и на этом всё. Давайте перейдем к листингу:

    bool areOccurrencesEqual(string s) {
map<char, int> dict;
for (const auto& ch: s)
{
auto it = dict.find(ch);

if (dict.end() == it)
{
dict[ch] = 1;
}
else
{
++it->second;
}
}

int count = 0;

for (const auto& [_, value]: dict)
{
if (count != value && count)
{
return false;
}

if (!count)
{
count = value;
}
}

return true;
}


В этом решении всё максимально прямолинейно: сделали именно, то, что написано, сначала итерируемся по всем буквам в строке считаем и запоминаем в dict, потом – идём по dict, сравниваем, что все частоты одинаковы, нужно учесть, что вначале count нулевой, и необходимо его инициализировать, встретив первое ненулевое значение.

Но вот загвоздка: работает не очень быстро, всего лишь лучше 27% других решений – мы тут явно не ради такого собрались!

#IT #c_plus_plus #leetcode #today #ToBeContinued
1👍1
Я белыми, противник сдался после этого хода, до этого я чувствовал, что вообще проигрываю: он давил пешками я ходил вокруг – из-за положения его ладьи и ферзя никак не мог пролезть к королю...

#chess #flood
👍1👏1
Сравните это

Часть 2.

В прошлый раз, мы пришли с решением, работающим, но не очень быстрым. Что же можно легко и быстро поменять, в надежде ускорить его? Правильно – используемый для dict контейнер – попробуем вместо map, которая внутри себя красно-чёрное дерево, использовать хэш-таблицу, то есть unordered_map. Не буду приводить полный листинг тут – я только изменил map на unordered_map.

Этого уже оказалось достаточно , что б стать лучше 67 процентов других решений. . А нужен ли вообще тут настоящий ассоциативный контейнер? Очевидно, что нет: букв всего 26, в конкретном слове их может быть меньше, но больше – взять неоткуда. Более того, если глянуть на таблицу ASCII все эти буквы (сюрприз-сюрприз) идут подряд и представляют собой однобайтовые целые, иначе говоря, в Си/С++ я могу сделать такой вот трюк: ch-'a', и тогда для ‘a’ я получаю индекс равный нулю, для ‘b’ – одному и так далее. То есть: использовать простой массив длинной 26. Попробуем с простым “деревянным” Сишным массивом, опять меняем только объявление:

dict: int dict[26] = {0};


Уже лучше, чем 74 процента ответов. Сишный массив – некрасиво, в настоящей разработке лучше их вовсе избегать, а что же использовать там для массива – конечно, vector – излюбленная тема всех собесов по плюсам. Изменим наш тип на vector и сразу скажем ему, что будет ровно 26 элементов – дабы ничего не замедлилось на его любимых переалокациях. Не буду приводить полный листинг: поменял только объявление dict на это:
vector<int> dict(26);


Всё – “провалился” в 0мс, тут оставим вопрос, а что, если я нажму Submit несколько раз с тем же самым решением – кому интересно попробуйте сами.

Тут, для тех немногих, кто дочитал до этого момента, у меня сюрприз: идея поста родилась у меня позавчера, когда я читал документацию языку по Elixir, собственно, на первой странице там вот такой код:
ex> "Elixir" |> String.graphemes() |> Enum.frequencies()
%{"E" => 1, "i" => 2, "l" => 1, "r" => 1, "x" => 1}


И захотелось мне, сравнить его с тем, как это пишется на C++, собственно задачу я нагуглил под условия…Да, этот кусочек на Elixir делает не совсем то, что в задаче, вот эквивалентный код:
 def are_occurrences_equal(s) do
s |> String.graphemes() |> Enum.frequencies()
|> Map.values() |> Enum.uniq() |> Enum.count() == 1
end

Написан только с помощью гугл и доков – потому что Эликсир я не знаю, только пытаюсь учить время от времени. Нужны ли тут комментарии насчёт выразительности, отсутствия явных циклов, низкоуровневых трюков и прочего? К сожалению, я не знаю насколько у меня быстрое решение на Эликсире, потому что Leetcode говорит, что слишком мало решений вообще у него есть (а может только моё одно?).

#IT #c_plus_plus #Elixir #leetcode
Я белыми, в ходе размена пешек и легких фигур в центре поля, я смог поставить ферзя на одну диагональ со слоном: оппонент, пропустил этот момент, поставив под угрозу моего коня - как итог, ход ферзём на А7 с шахом, и мат следующим ходом

#chess #flood
Сложите это, пожалуйста

Написав эту заметку про типы с плавающей точкой, вспомнил, что был и другой интересный случай с этими числами, правда, из практики собеседований, но не суть. Вопрос звучал примерно так: дан массив чисел типа double, как лучше его отсортировать (по возрастающей или убывающей), чтобы просуммировать его с большой точностью.

В этом вопросе, с точки зрения, обывателя или программиста, не вникшего в тонкости работы с этими числами, прекрасно абсолютно всё. Наверняка в школе, вы слышали о том, что перестановка мест слагаемых суммы не меняет (хотя бы в такой формулировке), если этому верить, значит задача лишена смысла. Но раз спрашивают, и я об этом пишу, наверное, смысл есть, но как так?

Начну издалека: когда нас в детстве учат устному счёту 1, 2, 3…нас учат натуральным числам N и их свойствам - числам, которыми я могу посчитать количество предметов, далее, “добавляя” к ним 0 и отрицательные числа, мы получаем Z - целые числа, “присоединяя” к ним обыкновенные дроби мы получаем множество Q – множество рациональных чисел , наконец, решая квадратные и не только уравнения и рассматривая другие конструкции, мы получаем множество вещественных чисел R (примеры, корень из двух, экспонента и так далее). Эту цепочку можно было бы продолжить: комплексные числа, дуальные, кватернионы, а в вещественных выделить алгебраические и трансцендентные…Но я не буду об этом говорить, потому что, они все не имеют отношения к тем числам, что мы имеем в C++ как базовый типы. Об этом немножечко позже.

Непосредственно с числами, даже каким-то образом со способом их определения, связанны основные арифметические операции: сложение, вычитание, умножение, деление. На эту заметку нам хватит сложения: даже эти операции не так уж элементарны…особенно деление. Бинарная операция сложения (т.е. принимающая два аргумента) для множеств N, Z, Q, R (на самом деле и все остальные как я помню, но это не важно) обладает свойствами коммутативности и ассоциативности. Коммутативность — значит всего лишь, что a+b = b+a, совсем просто, а вот ассоциативность, посложнее: a + (b+c) = (a+b) + c. Иными словами, операция у нас бинарная, мы умеет сложить 2 числа, а вот если написать, что мы складываем 3 числа, то можно сначала сложить первые 2, или последние 2, но результат будет одинаков. Если вы думаете, что это какая-то глупость и так всегда, то посмотрите на вычитание: (1 – 2) – 3 = -4, но 1 – (2 – 3) = 0 (вычитание не ассоциативно). На самом деле Z – это кольцо, Q, R – это поле, но мы с этим не будем работать.

В C++ (и во многих остальных ЯП), есть различные операторы, конечно, включая и арифметические, не буду тут давать формального определения оператора в С++, понятно, что это некоторая синтаксическая сущность, тут важно, что для всех операторов стандарт языка предоставляет информацию об их ассоциативности, а именно, либо слева направо, либо справа налево. Большинство слева направо, но нас интересует только operator +.

Прошу ещё раз заметить: в C++ a+b+c это (a+b) + c, то есть в математике, скобки я могу ставить, как угодно, а компилятор, видя выражение без скобок, неявно поставит их таким образом, то есть, на самом деле, нет там никаких скобок, а есть ассемблерные инструкции (картинки с https://godbolt.org/ будут под постами)

#IT #job #c_plus_plus #math #weird #interview
Продолжение

Теперь вернемся, к тому какие встроенные типы для чисел в C++ мы имеем – integra и floating point…Первые мы пропускаем, насчёт вторых зададимся вопросом, вот в верху я представил множество различных чисел из алгебры – к чему относятся floating points? Правильный ответ: float и double способны представить некоторые рациональные числа. На этом всё, да, никаких настоящих вещественных чисел они не могут представить, корень из двух будет аппроксимирован некоторым рациональным приближением и так далее. Но, выше я писал, что рациональные числа обладают коммутативностью и ассоциативностью по сложению? Да…но… Уже случай из предыдущего поста, явно не ложится в наше пониманием рациональных чисел, демонстрируя, некоторое “поглощение”. Сложение чисел с плавающей точкой имеет свойства отличные от общеалгебраических, в результат их представления и реализации арифметических операций над ними. При этом, CPU, как и целочисленными значениями, складывает по 2 числа с плавающей точкой за одну инструкцию, было бы странно, если б мы сломали коммутативность: это был бы очевидный баг.

Я утверждаю следующее: можно найти тройку чисел с плавающей точкой (одновременно все float или double), такие что (a+b) +c не равно a + (b +c).
И сейчас я объясню, как я собираюсь их искать. Опять апеллирую к примеру с поглощением малого числа из прошлого поста, идея сводится к тому, что для начала мне нужно найти пару чисел, “большое” число a, и “малое” число b. Причём, число a “поглощает” число b, но число 2*a уже не будет поглощаться… Ещё раз: поскольку это поглощение основано на том, что при сложении числа выравниваются (представляются с одинаковыми экспонентами), малое число должно быть настолько малым, что б выровняться за правый край мантиссы. А если я умножу его на 2 (сдвину на разряд влево) оно учтётся пройдёт по последнему разряду мантиссы и учтётся.

Так же, я хочу числа с простым бинарным представлением, число a=1, число b…подсмотрим сюда, мантисса у нас 23 бита, т .е. я предполагаю, что нужно сдвинуть единицу направо на 24 разряда, иначе говоря мантисса у нас опять 1, а экспонента -24 (минус 24). Да нам удобнее для числа b использовать так называемую научную нотацию, да ещё и наполовину в 16ричной системе счисления…
Вот листинг, берем float т.к. он короче, и берем format т. к. он обещает “умный вывод”, с нулями только если они имеют смысл, и собираем всё с C++20:

#include <iostream>
#include <format>

int main()
{
float a = 1.0;
float b = 0x1.p-24;

std::cout << std::format("{}", a+b) << std::endl;
std::cout << std::format("{}", (a + b) + b) << std::endl;
std::cout << std::format("{}", a + (b + b)) << std::endl;

return 0;
}


Вывод будет:
1
1
1.0000001

То есть: во-первых, тройку чисел я нашёл, во-вторых, мы разобрались, что изначальная задача действительно имеет смысл, в-третьих, мы получили ответ на неё, а именно, нужно отсортировать от меньшего числа к большему (в силу ассоциативности слева направо и всех остальных выкладок).

#IT #job #c_plus_plus #math #weird #interview
Целочисленное сложение 3 чисел
#IT #job #c_plus_plus #asm