Куда оно летит?
Недавно вспоминал свой опыт работы в одной платежной системе, которая ныне уже, похоже, не существует. Работал я там много лет, явно больше, чем нужно, поэтому есть, что вспомнить. В этот раз хочу поговорить как меня и моего коллегу сделали 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
Недавно вспоминал свой опыт работы в одной платежной системе, которая ныне уже, похоже, не существует. Работал я там много лет, явно больше, чем нужно, поэтому есть, что вспомнить. В этот раз хочу поговорить как меня и моего коллегу сделали 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
Часть 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
Часть 3.
Итак, мы остановились на том, что команда, включая меня, успешно пилила проект и ничто, в целом, не омрачало нашу рабочую атмосферу. Тут, нужно заметить одну важную вещь: формально ни моя должность, ни должность моего коллеги разработчика не поменялись, мы оставались в том же самом полуразвалившемся отделе с кучей вялотекущих проектов на поддержке. И, опять же, формально все эти проекты так и оставались у нас на поддержке, конечно, буду честен, по факту, им уделялось очень мало времени, от силы процентов 10, но это важно для дальнейшего понимания событий.
Где-то в 2019ом году, состоялся первый релиз приложения. Как я уже упоминал, приложение – не настоящий стартап, иначе говоря, его функционал лежал вне основных интереса бизнеса, поэтому после релиза дальнейших задач по этому приложению не было (и не планировалось – это не было сюрпризом). Что несколько удивляло – так это реакция менеджмента: “Отлично, что вы с этим справились, мы подумаем, что дальше, а пока занимайтесь поддержкой своих проектов”.
Теперь, зная, что было дальше, я понимаю: нужно было уволиться в тот же момент спокойно (или не очень), пройдя собеседования на Android-senior в другие компании. На тот момент, лояльность компании, иллюзия что “они найдут чем заниматься” и так далее удержали меня от этого шага.
Спустя несколько месяцев, мы вернулись к разработке этого приложения: кое-что писали уже на Kotlin, активно продвигаемый Google, на нас же свалили и поддержку backend для этого приложения, так же (коряво) написанный на Kotlin, но…объем фич был уже ни тот, и за это время технологии модные ещё вчера стали устаревать. Да, до сих пор некоторые технологии из моего первого поста используются в реальных проектах, но в тренде прежде всего Kotlin, Coroutines, Clean Architecture…
В следующий раз, я точно закончу этот цикл! Будьте на связи
#job #IT #Android #Java #memories #ToBeContinued #working_days
Сравните это
Часть 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, это немного сократит нам код и на этом всё. Давайте перейдем к листингу:
В этом решении всё максимально прямолинейно: сделали именно, то, что написано, сначала итерируемся по всем буквам в строке считаем и запоминаем в dict, потом – идём по dict, сравниваем, что все частоты одинаковы, нужно учесть, что вначале count нулевой, и необходимо его инициализировать, встретив первое ненулевое значение.
Но вот загвоздка: работает не очень быстро, всего лишь лучше 27% других решений – мы тут явно не ради такого собрались!
#IT #c_plus_plus #leetcode #today #ToBeContinued
Часть 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
Не надо плавать
В прошлых постах я довольно подробно расписал подводные камни работы с числами с плавающей точкой, которые моделируют дробные(рациональные) числа. И тут есть интересный момент: это не единственный вариант работы с такими числами (Википедия не даст соврать), я и сам уже упоминал это, но… в C++ их нет ни как базового типа, ни как класса в стандартной библиотеке. Зато, в C++ есть вызывающая много споров возможность перегружать операторы, что подталкивает нас попробовать создать так называемые числа с фиксированной точке “на коленке”, сделав свой класс и перегрузив (хотя бы часть) арифметических операторов.
Тут прежде, чем изливать тонны кода, давайте остановимся на основах: почему это возможно и как это сделать. Итак, язык нам предоставляет базовые integral types, обычно 1,2,4, 8 байтовые со знаком или без (ну уж точно на x86-64 платформе это так) и предоставляет нам арифметические операторы для этих типов. Рассмотрим базовые моменты работы арифметических операторов на примере типов uint8_t, int8_t:
• Сложение для uint8_t происходит по модулю 256 т.е. 1+2 = 3, но 1+255=0
• Сложение знаковых чисел происходит той же самой инструкций ассемблера, т.е. для CPU нет никаких отрицательных чисел, границы этого типа -128…+127
• Перемножение двух int8_t дает результат типа int16_t
• Частное от деления int16_t \ int8_t дает результат типа int8_t, остаток отбрасывается
А теперь основная идея (или трюк, если хотите), я могу взять переменную типа int8_t и мысленно сказать, что она хранит не целое число…а например, число четвертей, то есть мысленно поставить точку после двух левых разрядов. Давайте на примере десятичной системы: я возьму какие-то числа, например 3.14 и 2.71 и скажу – “у меня есть 314 сотых, а также 271 сотая”. Если меня попросят их сложить или вычесть это можно сделать, не выходя за нотацию сотых прямо сразу: 314 + 271 = 585, 314 – 271 = 43. Давайте попробуем умножить, 314*271 = 85094 эм...это точно в сотых? Нет, это точно не в них – правильно будет вот так 851 т.е. сдвинуть на 2 разряда вправо. А причём тут сотые, и какие-то четверти, которые я упоминал ранее? Четверти – это одни четвертые (богатый русский язык), для двоичной системы, это тоже самое, что сотые для десятичной.
В следующий раз понемногу начну показывать код.
#IT #job #c_plus_plus #math #weird #ToBeContinued
В прошлых постах я довольно подробно расписал подводные камни работы с числами с плавающей точкой, которые моделируют дробные(рациональные) числа. И тут есть интересный момент: это не единственный вариант работы с такими числами (Википедия не даст соврать), я и сам уже упоминал это, но… в C++ их нет ни как базового типа, ни как класса в стандартной библиотеке. Зато, в C++ есть вызывающая много споров возможность перегружать операторы, что подталкивает нас попробовать создать так называемые числа с фиксированной точке “на коленке”, сделав свой класс и перегрузив (хотя бы часть) арифметических операторов.
Тут прежде, чем изливать тонны кода, давайте остановимся на основах: почему это возможно и как это сделать. Итак, язык нам предоставляет базовые integral types, обычно 1,2,4, 8 байтовые со знаком или без (ну уж точно на x86-64 платформе это так) и предоставляет нам арифметические операторы для этих типов. Рассмотрим базовые моменты работы арифметических операторов на примере типов uint8_t, int8_t:
• Сложение для uint8_t происходит по модулю 256 т.е. 1+2 = 3, но 1+255=0
• Сложение знаковых чисел происходит той же самой инструкций ассемблера, т.е. для CPU нет никаких отрицательных чисел, границы этого типа -128…+127
• Перемножение двух int8_t дает результат типа int16_t
• Частное от деления int16_t \ int8_t дает результат типа int8_t, остаток отбрасывается
А теперь основная идея (или трюк, если хотите), я могу взять переменную типа int8_t и мысленно сказать, что она хранит не целое число…а например, число четвертей, то есть мысленно поставить точку после двух левых разрядов. Давайте на примере десятичной системы: я возьму какие-то числа, например 3.14 и 2.71 и скажу – “у меня есть 314 сотых, а также 271 сотая”. Если меня попросят их сложить или вычесть это можно сделать, не выходя за нотацию сотых прямо сразу: 314 + 271 = 585, 314 – 271 = 43. Давайте попробуем умножить, 314*271 = 85094 эм...это точно в сотых? Нет, это точно не в них – правильно будет вот так 851 т.е. сдвинуть на 2 разряда вправо. А причём тут сотые, и какие-то четверти, которые я упоминал ранее? Четверти – это одни четвертые (богатый русский язык), для двоичной системы, это тоже самое, что сотые для десятичной.
В следующий раз понемногу начну показывать код.
#IT #job #c_plus_plus #math #weird #ToBeContinued
👍1🔥1
Не надо плавать
Часть 2
Поскольку код вышел на Хабре, и более-того, комментаторы и я сам нашли довольно большое количество ошибок и переусложненных моментов, хотелось бы тут остановиться ещё раз на идее.
В языке C++ есть типы double и float, реализованные в соответствии со стандартом IEEE-754.
Знаменитая картинка из Википедии говорит о том, что мы храним отдельно знак, степень 2ки (экспоненту), и мантиссу – считай наши двоичные цифры. Тут хочется напомнить, что целые либо беззнаковые числа хранятся абсолютно иначе: нет ни отдельно знака, ни степени, по сути, хранится только само число.
Смысл статьи на Хабре и потуг предыдущего поста состоит в том, что можно взять целое число произвольного размера (но из тех, что нативно поддерживается компилятором) и сказать, что теперь я трактую n правых разрядов как значение идущее после точки – дробную часть числа, а остальные разряды как левую часть числа, вот пример для 8 битного числа где 2 младших разряда я отдаю под дробную часть 0000 1101, это тоже самое что 11.01 (2) = 3.25 (10) .
Соответственно, написанный код – всего лишь является отражением этой идеи.
#today #IT #c_plus_plus #math #habr #ToBeContinued
Часть 2
Поскольку код вышел на Хабре, и более-того, комментаторы и я сам нашли довольно большое количество ошибок и переусложненных моментов, хотелось бы тут остановиться ещё раз на идее.
В языке C++ есть типы double и float, реализованные в соответствии со стандартом IEEE-754.
Знаменитая картинка из Википедии говорит о том, что мы храним отдельно знак, степень 2ки (экспоненту), и мантиссу – считай наши двоичные цифры. Тут хочется напомнить, что целые либо беззнаковые числа хранятся абсолютно иначе: нет ни отдельно знака, ни степени, по сути, хранится только само число.
Смысл статьи на Хабре и потуг предыдущего поста состоит в том, что можно взять целое число произвольного размера (но из тех, что нативно поддерживается компилятором) и сказать, что теперь я трактую n правых разрядов как значение идущее после точки – дробную часть числа, а остальные разряды как левую часть числа, вот пример для 8 битного числа где 2 младших разряда я отдаю под дробную часть 0000 1101, это тоже самое что 11.01 (2) = 3.25 (10) .
Соответственно, написанный код – всего лишь является отражением этой идеи.
#today #IT #c_plus_plus #math #habr #ToBeContinued
👍1
Книги
Были временаи получше, когда я был готов читать, что угодно в рамках жанров НФ и фэнтези. Были времена, когда я читал книги по условной Computer Science – и тогда "серьёзная" книга была на неделю, а художественная – на вечер.
Но, уже несколько лет как, настали времена, когда мне нужна особая причина, что б начать, а главное – закончить книгу. Действительно: читать книги по программированию? – Полноте, зачем? – Собес в Яндекс я прошёл уж 4 года как, полный список языков и технологий, который я там-сям применял утомит своей длиной любого HR...
С художественными книгами история более интересная: читать их можно, исходя из двух основных посылов, – удовольствия и ценного содержания. Ценного содержания в развлекательной литературе обычно немного, более того, книги ценного содержания, как правило, читать трудно.
Идём дальше, от чего же зависит удовольствие от чтения, по моему субъективному мнению? – От двух категорий: сюжета и языка (он же стиль).
Возможно, это неочевидно, но сюжет (также как и глубокий смысл) может практически отсутствовать – малые литературные формы слишком "малы", чтоб выдать читателю несколько запутанных сюжетных линий. Примеров таких сколько угодно: Московская Пасха, Смерть в доме – пируэтов в сюжете либо нет совсем, либо он один.
Язык, как таковой, – отсутствовать не может, более того, я не возьмусь систематически выписать какие характеристики он может иметь. Совершенно точно, что книгу с неудачным для меня стилем изложения я, скорее всего, брошу, невзирая на сюжет. Бывает такое, что язык самсокровище ярок и специфичен (Бегущая по волнам), бывает такое что он легок и весел, вытаскивая всё повествование.
#today #flood #superflood #books #opinion #ToBeContinued
Были времена
Но, уже несколько лет как, настали времена, когда мне нужна особая причина, что б начать, а главное – закончить книгу. Действительно: читать книги по программированию? – Полноте, зачем? – Собес в Яндекс я прошёл уж 4 года как, полный список языков и технологий, который я там-сям применял утомит своей длиной любого HR...
С художественными книгами история более интересная: читать их можно, исходя из двух основных посылов, – удовольствия и ценного содержания. Ценного содержания в развлекательной литературе обычно немного, более того, книги ценного содержания, как правило, читать трудно.
Идём дальше, от чего же зависит удовольствие от чтения, по моему субъективному мнению? – От двух категорий: сюжета и языка (он же стиль).
Возможно, это неочевидно, но сюжет (также как и глубокий смысл) может практически отсутствовать – малые литературные формы слишком "малы", чтоб выдать читателю несколько запутанных сюжетных линий. Примеров таких сколько угодно: Московская Пасха, Смерть в доме – пируэтов в сюжете либо нет совсем, либо он один.
Язык, как таковой, – отсутствовать не может, более того, я не возьмусь систематически выписать какие характеристики он может иметь. Совершенно точно, что книгу с неудачным для меня стилем изложения я, скорее всего, брошу, невзирая на сюжет. Бывает такое, что язык сам
#today #flood #superflood #books #opinion #ToBeContinued
🔥3
Дикость
Очень давно не писал технического контента – решил исправиться. То, что в том году разбирал относительно криптографии – не хочу сюда постить, так как мне, как минимум, нужен редактор формул, а как максимум, – вышло бы длинно и нудно.
Не имея под рукой интересных проблем, что б их описать, решил прибегнуть к помощи небезызвестного (и уже упоминавшегося в канале) сервиса Leetcode. Выбрал задачу Wildcard Matching, уровня hard, их тех, что раньше пробовал решить, но не сумел.
Итак, вот оригинальная формулировка:
Ограничения:
Сразу заметим пару моментов: во-первых, строка и образец могут быть достаточно длинны, что б сделать совсем наивные решения неэффективными, во-вторых, строка не может содержать '?' или '*', а в образце – не бывает экранирования, в-третьих, строка должна полностью соответствовать образцу.
Первая приходящая в голову мысль: давайте будем итерироваться по строке и образцу "одновременно", ведь пока мы не встретили "звездочку" – всё абсолютно очевидно. Действительно, представим себе ту же задачу, но символов '*' – нет. Получим следующий набросок решения:
Продолжение следует.
#today #c_plus_plus #leetcode #algo #ToBeContinued
Очень давно не писал технического контента – решил исправиться. То, что в том году разбирал относительно криптографии – не хочу сюда постить, так как мне, как минимум, нужен редактор формул, а как максимум, – вышло бы длинно и нудно.
Не имея под рукой интересных проблем, что б их описать, решил прибегнуть к помощи небезызвестного (и уже упоминавшегося в канале) сервиса Leetcode. Выбрал задачу Wildcard Matching, уровня hard, их тех, что раньше пробовал решить, но не сумел.
Итак, вот оригинальная формулировка:
Given an input string (s) and a pattern (p), implement wildcard pattern matching with support for '?' and '*' where:
'?' Matches any single character.
'*' Matches any sequence of characters (including the empty sequence).
The matching should cover the entire input string (not partial).
Ограничения:
0 <= s.length, p.length <= 2000
s contains only lowercase English letters.
p contains only lowercase English letters, '?' or '*'.
Сразу заметим пару моментов: во-первых, строка и образец могут быть достаточно длинны, что б сделать совсем наивные решения неэффективными, во-вторых, строка не может содержать '?' или '*', а в образце – не бывает экранирования, в-третьих, строка должна полностью соответствовать образцу.
Первая приходящая в голову мысль: давайте будем итерироваться по строке и образцу "одновременно", ведь пока мы не встретили "звездочку" – всё абсолютно очевидно. Действительно, представим себе ту же задачу, но символов '*' – нет. Получим следующий набросок решения:
bool isMatch(string s, string p) {
auto sIndex = 0;
auto pIndex = 0;
auto sLen = s.length();
auto pLen = p.length();
int starIndex = -1;
int matchedIndex = 0;
while (sIndex < sLen)
{
// simply match then we increment the both indexes
if (pIndex < pLen && (p[pIndex] == '?' || p[pIndex] == s[sIndex]))
{
pIndex++;
sIndex++;
continue;
}
return false;
}
//still possible to have an unfinished pattern here
//it matches if and only if the pattern is over
return pIndex == pLen;
}
Продолжение следует.
#today #c_plus_plus #leetcode #algo #ToBeContinued
👍2
Дикость продолжается
Кратко объясню идеи сниппета из прошлого поста:
Идём по строке и образцу, если оба символа совпадает, просто наращиваем оба индекса, если в образце стоит знак вопроса - делаем тоже самое, т.к. он совпадает с любым другим символом, при первом же не совпадении – рапортуем о неудаче. Всё предельно просто.
Ну теперь попробуем вернуть "звёздочки": по определению '*' соответствует нулю либо любому иному количеству символов...то есть – она не соответствует ничему – весь вопрос в том, чему соответствует символы за ней...Что если мы попробуем матчить остаток строки остатку шаблона, а если не сможем – просто нарастим индекс в строке? Вот примерно такой набросочек:
Это почти работает, в следующий раз – объясню в чём загвоздка и завершу этот маленький цикл.
#today #c_plus_plus #leetcode #algo #ToBeContinued
Кратко объясню идеи сниппета из прошлого поста:
Идём по строке и образцу, если оба символа совпадает, просто наращиваем оба индекса, если в образце стоит знак вопроса - делаем тоже самое, т.к. он совпадает с любым другим символом, при первом же не совпадении – рапортуем о неудаче. Всё предельно просто.
Ну теперь попробуем вернуть "звёздочки": по определению '*' соответствует нулю либо любому иному количеству символов...то есть – она не соответствует ничему – весь вопрос в том, чему соответствует символы за ней...Что если мы попробуем матчить остаток строки остатку шаблона, а если не сможем – просто нарастим индекс в строке? Вот примерно такой набросочек:
class Solution {
public:
bool isMatch(string s, string p) {
auto sIndex = 0;
auto pIndex = 0;
if (p.empty())
{
return s.empty();
}
return isMatchImpl(s, p, sIndex, pIndex);
}
bool isMatchImpl(const string& s, string& p, int sIndex, int pIndex)
{
while (sIndex < s.size() && pIndex < p.size())
{
if (p[pIndex] == '?')
{
++sIndex, ++pIndex;
continue;
}
if (p[pIndex] == '*')
{
while (pIndex < p.size() - 1 && p[pIndex + 1] == '*')
{
++pIndex;
}
return matchStar(s, p, sIndex, ++pIndex);
}
if (p[pIndex] == s[sIndex])
{
++sIndex, ++pIndex;
continue;
}
return false;
}
if (sIndex < s.size() && (pIndex = p.size()))
{
return false;
}
if (sIndex == s.size())
{
while(pIndex < p.size())
{
if (p[pIndex] != '*')
{
return false;
}
++pIndex;
}
return true;
}
return true;
}
bool matchStar(const string& s, string& p, int sIndex, int pIndexAtStop)
{
// there is nothing after the star
if (pIndexAtStop >= p.size())
{
return true;
}
auto stopCh = p[pIndexAtStop];
cout << "stopchar = " << stopCh << endl;
while (sIndex < s.size())
{
if (s[sIndex] == stopCh || stopCh == '?')
{
auto matched = isMatchImpl(s, p, sIndex + 1, pIndexAtStop + 1);
if (matched)
{
return true;
}
}
++sIndex;
}
return false;
}
}
Это почти работает, в следующий раз – объясню в чём загвоздка и завершу этот маленький цикл.
#today #c_plus_plus #leetcode #algo #ToBeContinued
👍1