job4j.ru - Практика. Ревью. Результат.
771 subscribers
75 photos
186 links
О жизни в IT:
- Языки: Go, Rust, Java, Kotlin
- Технологии: PostgreSQL, Kafka
- Инфраструктура: Docker, Kubernetes
- Soft Skills: общение, интервью, карьера
Download Telegram
Бойко́т переводной литературе

Часто слышу, что ребята ругают переводную техническую литературу. Ранее я к этому относился спокойно и особо проблем не замечал. В целом не с чем было сравнить.
В моей личной библиотеке есть много книг, и переводных, и оригинальных. И я обратил внимание, что все оригинальные книги я прочитываю всегда до конца. Я имею в виду и на русском, и на английском. А переводные часто оказываются отложенными. Так и случилось с книгой по Apache Kafka. Я купил её, наверное, в 2019 году и бегло полистал.
А сейчас, когда я пишу курс по Apache Kafka, мне крайне нужна эта книга. Поэтому я её взял и стал читать от начала до конца.
Первое, на что я обратил внимание, — это то, что иногда я не могу уловить смысл предложений. Это странно. Я понимаю каждое слово, но не могу понять смысл предложения. Эта книга — перевод, поэтому я нашёл оригинал и сразу понял, что проблема в переводе.

Если провести аналогию в программировании, то любой перевод — это слой абстракции.
То есть, чтобы читать переведённую книгу, мне нужно выполнить три действия:
1. Понять русский текст.
2. Адаптировать его под английские термины.
3. Понять, что хотел донести автор оригинальной статьи.

Также я обратил внимание, что книгу в оригинале я прочитаю в разы быстрее, чем перевод. Это связано и с тем, что я описал выше, а также со способом восприятия иностранного языка. Если взять классическую схему обучения чтению, то освоение чтения начинают с элементарной части — буквы. Что в целом неверно, но это уже другая история.
Если мы учим второй язык, то элементарной частью для нас будет слово. Поэтому, когда я читаю иностранную литературу, то текст воспринимается словами или даже цепочками слов (chunk). Почему этого нет с переведённой статьёй? Потому что перевод — это интерпретация. А так как в 100% случаев переводчик — это не программист, то мы получаем дословный перевод, который мозг отказывается воспринимать.

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

А как быть? Во-первых, если вы ещё не работаете в IT, то используйте любую литературу, которая вам поможет преодолеть барьер собеседования. А вот если вы уже на этой стороне, то я крайне советую начать учить английский.

Вы скажете: "Да, все так говорят, учи английский, а он не учится". Да, я тоже озадачился: а как тут помочь? Про это расскажу в следующей статье.

ПС. Я не фанатик английского языка, поэтому не техническую литературу с удовольствием читаю в переводе, так как там не требуется точности в терминах. Такой же подход и для авторских статей: например, автор выражает своё мнение об англоязычной статье и приводит какие-то куски переведённого текста — то тоже прочитаю с удовольствием.
👍14
Жаркий марафон - алго собес

Так как на собеседованиях продолжают активно давать задачи на алгоритмы. Предлагаю сделать летний марафон.
Одна задачу в неделю. Публиковать задачу буду тут в понедельник.
Задачи буду брать только с собеседований. а не случайные задачи.
Это увеличить шанс, что она будет на собеседовании тоже.

Решения можете скидывать: ссылки, либо из платформы, либо с github.

Задание для этой недели: Подмассив с их максимальной суммой | Алгоритм Кадане

https://job4j.ru/profile/topics/206/task_code/566/new_task

Как решать? В описании есть раздел объяснение. Сначала в уме попробуйте найти решение. А потом сравните с предложенным в объяснении, а только после этого пишите. Я советую писать прямо на платформе, потому что так же будет и в реальном собеседовании.
❤11🔥7
Песочница для Go

Сегодня решил сам решить задачу по поиску подмассива с максимальной суммой на платформе.

https://job4j.ru/profile/topics/206/task_code/566/new_task

Поправил несколько неудобных моментов.
1. Нельзя запустить main. Для отладки на платформе это критично.
2. Нужно использовать мышку, чтобы запускать код.

Поэтому добавил.
1. Если в коде есть main, то можно его запустить.
2. Запустить можно через кнопку "Запустить" или сочетанием клавиш Shift+Enter.

Так же переделал реализацию песочницы. Мы готовим курс по GoLang и там тоже нужна песочница. В JDK 17< настройки можно было указать через SecurityManager, в GoLang такого нет. Поэтому все переделал на Docker.

Если кто желает попробовать наш курс по GoLang, то пишите мне @parsentev.
Будете фокус группой. Единственное условие у вас должен быть опыт работы хотя бы год, Обучение бесплатное.

https://job4j.ru/profile/topics/206/task_code/566/new_task
👍5❤1
Завершил Курс по DevOps

Добавил важные уроки.
Сбор логов | Loki [#505404]
AlertManager | Loki | ERROR [#505405]
AlertManager | Prometheus | Health check [#505406]

Далее я соберу отзывы, пожелания и потом доработаю материал.
Например, я рассматриваю Jenkins, а ребята говорят, что лучше GitLab развернуть. В новой версии тогда на нем сделаю.
Не стал добавлять темы с GitOps, ArgoCD, Flux, потому что очень много вышло.
В целом эти решения тяжеловесные, даже не смотря на то, что я разворачивал минимальные приложения.

Так же вместо ELK, я просто взял Loki и сделал все через Prometheus + Grafana вышло в разы проще.

Описание курса тут https://job4j.ru/profile/courses/35

Немного своих впечатлений о процессе создания этого курса "Работает - не лезь". В программировании я вижу код и могу даже залезть внутрь приложения. А в DevOps я ограничен логами. Поэтому порой приходилось сидеть по нескольку часов, чтобы осознать, где я что-то прописал не так. Еще один важный момент - это совместимости версий в K8s. Все сервисы разворачиваются в кластере K8s через Helm. Часто было так, что приложение не стартовало, потому что ему не хватало настроек в этом Helm. Лезешь в лог, разбираешься для чего это нужно, потом обновляешь. Поэтому фраза "работает не лезь" - этот как раз про DevOps )).
👍17💯6
Go, как второй язык

А вы знали, что в GoLang всего 25 ключевых слов? Это в два раза меньше, чем в Java. Предположим, что и учить там в два раза меньше. Однако одна из первых фраз в книге Go 100:
Go is simple, but not easy. Это важно понимать, если вы только на пути в IT. На собеседовании по GoLang можно встретить классическую задачу на указатели.

Что выведет код и почему? В целом, я считаю, что задачи на то, что выведет код и почему, неуместны, так как у нас есть компилятор и тесты, чтобы не гадать, что он выведет. Конечно, это не касается многопоточных вещей.

type Person struct {  
Name string
}

func changeName(p *Person) {
p = &Person{
Name: "Alice",
}
}

func main() {
person := &Person{
Name: "Bob",
}
fmt.Println(person.Name)
changeName(person)
fmt.Println(person.Name)
}


В GoLang есть настоящие указатели, как в C++. Они не такие, как в Java, то есть это реальный указатель на участок памяти. Чтобы создать указатель, нужно начать переменную с символа амперсанда (&). А чтобы записать в переменную указателя, нужно использовать звездочку (*).

Код выше выведет Bob, Bob. Почему? Потому что функция changeName записывает новый указатель в локальную переменную. Вопрос: как поправить код, чтобы вывел Bob, Alice?

Или, как в Java, обратиться к полю:

func changeName(p *Person) {  
p.Name = "Alice"
}


Но это не интересно, а можно сказать, что возьми адрес указателя и запишу туда новый объект:

func changeName(p *Person) {  
*p = Person{
Name: "Alice",
}
}


Это не значит, что язык плохой, что позволяет такое делать, а то, что он так же будет требовать много времени на изучение, как и любой другой язык. Однако все эти нюансы — это только особенности языка, которые по сравнению с элементами предметной области крайне небольшие.

Изначально я думал, что с Go решают какие-то крутые задачи по оптимизации, но на самом деле оказалось, что решают такие же задачи, как и в Java. То есть стек технологий и решений один в один как в Java: Docker, K8s, Redis и даже Kafka. Там же шаблоны отказоустойчивости, Retry, Circuit Breaker, Timeout, Outbox. Есть и другие проекты, например, по pentest и blockchain, но их крайне мало, около 40 вакансий.

С другой стороны, если посмотреть на курсы по Go, то можно увидеть корреляцию с Java: Go занимает меньше времени. Поэтому мы и решили проверить эту гипотезу с Михаилом.
Можно ли за 3-4 месяца перейти с Java на Go, при условии наличия коммерческого опыта и без потери в зарплате?

Для примера, в Go есть только слайсы и словари. Все другие структуры вам нужно реализовать самому, то есть нет необходимости учить Java Collection API.

Если у вас есть желание принять участие в нашем эксперименте по Go, пишите мне @parsentev.
👍8
Курс Apache Kafka | Задача с собеседования

Я переключился на курс по Apache Kafka.
Добавил новый урок.

Архитектура Kafka [#505452]

В нем рассмотрел настройку разделов в теме.

Часто на собеседовании можно услышать вопрос, о том, что будет если у нас один раздел в теме и два потребителя?
Пишите ответы в комментариях .

👉 Описание курса https://job4j.ru/profile/courses/21
🔥3
job4j.ru - Практика. Ревью. Результат. pinned «Кафка для линейного программиста За этот месяц уже несколько человек жаловались, что застряли на уровне мидл. На самом деле это распространённая ситуация. Как говорится, под лежачий камень вода не течёт. Расскажу эту ситуацию на примере английского языка.…»
Внутренний диалог и иностранный язык

Продолжу тему иностранного языка. Как вы знаете, я активно практикую йогу, потому что она помогает решать самые разные задачи в физическом и ментальном плане. В физическом плане всё понятно: асаны в йоге можно сравнить с любимыми спортивными упражнениями, но в спорте нет ментальных практик. Если упростить, йога — это концентрация на объекте наблюдения, например на дыхании, или, наоборот, на мыслительном процессе, но без погружения в него.

Попробуйте сами непрерывно понаблюдать за дыханием — через короткий промежуток времени вы поймаете себя на мысли, что уже не наблюдаете за дыханием, а думаете о других вещах.

На самом деле процесс переключения крайне важен, так как он позволяет найти опору в мыслительной деятельности. Наш ум всегда находится в потоке мыслей — это его природа. Как река не может стоять на месте, так и ум не может молчать, но мы можем им управлять.

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

Тот же феномен работает и с иностранным языком: если мы читаем, мыслительный диалог идёт на этом языке. Поэтому, если есть возможность, читайте вслух.

Этот же приём используется при чтении программного кода.

Посмотрите на пример кода на Rust:

struct SeaCreature {
noise: String,
}

impl SeaCreature {
fn get_sound(&self) -> &str {
&self.noise
}
}

fn main() {
let creature = SeaCreature {
noise: String::from("blub"),
};
println!("{}", creature.get_sound());
}


Я уверен на 100%, что вы смогли прочитать и понять этот код. Обратите внимание, что ваш мозг интерпретировал его по-своему, даже без знания синтаксиса Rust. Вам, конечно, не понятны все нюансы, но общий смысл вы уловили: описать структуру данных, дать ей функцию, создать объект этой структуры и вызвать функцию.

А теперь вернёмся к английскому языку. У меня появилась идея создать серию уроков, которая позволит читать английские книги и статьи в оригинале. Составить такой материал через ChatGPT несложно. Для чтения Reddit нужен словарный запас около 4000–6000 слов — это вполне реально, ведь многие IT-термины либо вовсе не переводятся (например, «commit»), либо имеют аналогичное звучание (например, «transaction»).

Однако встал вопрос: что делать с внутренним диалогом? Проблема в том, что мы будем неправильно произносить английские слова, но я пришёл к выводу, что это не так важно. Главное — отказаться от дословного перевода, как в примере с Rust-кодом, и сосредоточиться на понимании смысла предложения, а не на буквальном переводе.

Я знаю, что среди моей аудитории есть лингвисты. Если у кого-то есть желание принять участие в этом проекте, пишите мне @parsentev.
🔥12
String в Java и Go

Как вы знаете, задача на работе и задача на собеседовании — это две совершенно разные вещи.
Напомню, что с Михаилом мы решили провести эксперимент с языком Go (GoLang). Эксперимент крайне простой: за 4–5 месяцев проработать материал по Go и вкатиться в Go-разработку без потери зарплаты.

Я сразу разделил это на два процесса: реальные задачи на работе и вопросы/задачи для собеседования.

В этой статье мы поговорим о вопросах, задаваемых на собеседовании.

Вас когда-нибудь спрашивали на собеседовании по Java о том, как устроен класс String? Думаю, нет, разве что о String Pool. А вот в Go это обязательный вопрос.

Давайте разберёмся.

В Go строка (string) — это неизменяемая (immutable) последовательность байтов, обычно кодирующих UTF-8. Под капотом строка представлена простым «хэдером» из двух машинных слов:

// в пакете reflect есть аналогичная структура:
type StringHeader struct {
Data uintptr // указатель на первый байт в памяти
Len int // длина в байтах
}


Символы в кодировке UTF-8 могут занимать от 1 до 4 байтов. Поэтому в Go есть тип rune: он позволяет прочитать нужное количество байтов, чтобы получить соответствующий символ из таблицы Unicode:

name := "Hello, 世界"
for i, r := range name {
fmt.Println(i, string(r), r)
}


Здесь i указывает на начало руны.

Если в строку попадают байты, не составляющие корректную UTF-8-последовательность, Go при разборе рун заменяет такой фрагмент на специальный символ � (U+FFFD), а длина такого «руна-элемента» считается равной 1 байту:

b := []byte{0xff, 0xfe, 0x41} // 0x41 == 'A'
s := string(b)

fmt.Printf("len(bytes) = %d, len(runes) = %d\n",
len(b),
utf8.RuneCountInString(s),
)

// При range по строке
for i, r := range s {
fmt.Printf("idx %d: rune %U '%c'\n", i, r, r)
}


А как обстоят дела в Java?

В классическом варианте строки в Java представлены массивом 16-битных символов (UTF-16):

public final class String {
private final char[] value;
private int hash;
// ...
}


Начиная с Java 9 появился механизм оптимизации Compact Strings:

public final class String {
private final byte[] value;
private final byte coder; // 0 — LATIN1, 1 — UTF16
private int hash;
// ...
}


- Строки, содержащие только символы из Latin-1 (U+0000…U+00FF), хранятся по 1 байту на символ.
- Как только встречается хотя бы один символ за пределами Latin-1, используется UTF-16 (2 байта на кодовую единицу).

В Java тоже есть методы для работы с кодовыми точками: String.codePointAt() и String.codePoints().
❤1
Алго-марафон | 2 неделя

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

Примерно нужно решить 150 задач за 4–5 месяцев:
- Около 40 задач уровня easy.
- Около 100 задач уровня medium.
- И 10 задач уровня hard.

Выуживать задачи из YouTube оказалось неблагородным делом, ведь в 99 % это те же задачи с LeetCode. Поэтому я буду просто брать задачи оттуда.

Для задач easy и medium используем стандартный набор структур данных и алгоритмов:
- Два указателя
- Скользящее окно
- Бинарный поиск
- Хэш-таблица
- Стек
- Очередь
- Рекурсия
- Итерация
- Сортировка
- Префиксные суммы
- Жадные алгоритмы
- Битовые операции
- Динамическое программирование (простые задачи)
- BFS
- DFS

Всё это есть у нас в курсах: https://job4j.ru/profile/courses/5, https://job4j.ru/profile/courses/32 с теорией, задачами и проверкой менторами.

Поэтому на этой неделе нужно решить 10 задач уровня easy. Я приведу здесь ссылки на LeetCode, а завтра — аналоги на нашей платформе:
- https://leetcode.com/problems/two-sum/
- https://leetcode.com/problems/reverse-linked-list/
- https://leetcode.com/problems/valid-parentheses/
- https://leetcode.com/problems/merge-two-sorted-lists/
- https://leetcode.com/problems/best-time-to-buy-and-sell-stock/
- https://leetcode.com/problems/maximum-subarray/
- https://leetcode.com/problems/valid-anagram/
- https://leetcode.com/problems/invert-binary-tree/
- https://leetcode.com/problems/move-zeroes/
- https://leetcode.com/problems/intersection-of-two-arrays/

Кстати, я решаю их на Go, потому что в нём есть только массивы и карты, то есть остальные структуры приходится реализовывать самостоятельно (например, LinkedHashMap или HashSet). Я также настоятельно рекомендую всем, кто участвует в нашем эксперименте с Go, прорешать эти задачи именно на Go.

Свои решения вы можете оставить в комментариях под этим постом.
🔥8
В Go за 5 месяцев. Горизонтальное масштабирование карьеры

Недавно читал про эксперимент ребят из Польши. Они фанатеют языком Go и хотели создать настолько плотный обучающий материал, чтобы за 10 часов перейти на Go в коммерческом проекте:
https://threedots.tech/post/we-quit-our-jobs/

Мне хотелось бы повторить их эксперимент, но в реалистичные сроки.

Изначально я разделил обучение на два параллельных процесса. Первый — то, что мы делаем на работе: микросервисы, REST, gRPC, Kafka, Docker, Kubernetes, Redis. Второй — то, что нужно для собеседования. Это два отдельных навыка.

Разберём, что нужно для работы:

1. Базовый синтаксис, ООП, ФП, структуры данных.
Учебный проект: телеграм-бот ToDo List с использованием PostgreSQL. Это позволит покрыть все основные темы языка, кроме корутин.
2. Корутины.
Учебный проект — классический граббер сайтов, например поиск вакансий на hh.ru.
3. Микросервисы.
Один сервис (gRPC + REST) общается со вторым через Kafka. JWT + Redis для авторизации и хранения сессий.

Разбор задач и вопросов на собеседовании (параллельный процесс, а не после первой части):

1. Алгоритмы и структуры данных: 40 easy, 100 medium, 10 hard.
2. Вопросы по корутинам и управлению памятью.
3. Системный дизайн.

Опыт работы?! Всё просто: в резюме указываем, что переписали какие-то микросервисы в своём проекте на Go — и они стали в разы быстрее. Поэтому хочется писать весь проект на Go, главное — самостоятельно переписать.

Эта схема подходит тем, кто уже работает с микросервисами.

Оцениваю такое обучение примерно в 5 месяцев ежедневных занятий, но никак не за 10 часов.

Зачем учить ещё один язык, решающий те же задачи, что и Java?
Во-первых, уровень абстракции: в Go вы ближе к системному программированию. Это особенно ценят на собеседованиях, например при вопросах о реализации строк или слайсов в Go.
Во-вторых, количество вакансий, на которые вы можете претендовать, сразу увеличивается вдвое. Стек остаётся одинаковым, меняется только базовый язык. Все концепции ООП, ФП, структуры данных, реляционные базы данных остаются такими же.

По сути это горизонтальное масштабирование вашей карьеры: резюме отличается только основным языком (Java или Go), а весь стек остаётся одинаковым.
👍14🔥1
Опять Кадане

Выше я привел список из 10 задача. Все задачи мне попадались раньше, кроме https://leetcode.com/problems/best-time-to-buy-and-sell-stock/

Сначала я оценил, как бы ее сделал сам и кроме прямого перебора больше решение не придумал. Пошел смотреть предлагаемые решения и как оказалось, то опять тут алгоритм Кадане. Сходу его применить тут не смог.

Для тренировки сделал еще и задачи на поиск максимального подмассива.

Пока в голову этот алгоритм не залез. Для этого случая, мы ищем локальным минимум. Если мы нашли новый, минимум, то идем считать уже от него. Время получается линейное.

в случае с подмассивом, все тоже самое, только мы оцениваем сумму, а не одно число.

В целом пост о том, что я тоже делаю эти задачи, чтобы поддержать тех кто тоже решить в нем принять участие.
👍9✍3
Channel name was changed to «IT | Java | Kotlin | Go | Rust»
@Transation | Hibernate

У нас в курс https://job4j.ru/profile/courses/12 есть тестовое задание, где мы используем напрямую Hibernate.

https://github.com/MikhailChrn/job4j_cars/blob/10d435a795963ee839be915242001170ef0fdb39/src/main/java/ru/job4j/cars/repository/CrudRepository.java

Есть сервисы, которые не атомарно записывают данные.
Я попросил ученика сделать их атомарными. Я знаю, что это не тривиально в этом задании, потому что подобная проблема есть в Go.

Решение ученика.

https://github.com/MikhailChrn/job4j_cars/blob/10d435a795963ee839be915242001170ef0fdb39/src/main/java/ru/job4j/cars/service/impl/RegularPostService.java#L130-L140

Вопрос, можно ли так делать? Если нет, то как сделать правильно?

Решение пишите в комментариях к этому посту.
Алго-марафон | 3 Неделя

Продолжаю марафон. Иду по плану 10 задач в неделю. Знакомые задачи решаются быстро. а вот задачи на динамическое программирование вызывают сложности. С ними прямо зависаю на несколько часов.
👍4
Задачи на эту неделю

https://leetcode.com/problems/climbing-stairs/
Темы: динамическое программирование

https://leetcode.com/problems/rotate-array/
Темы: массив, циклические сдвиги, реверс

https://leetcode.com/problems/third-maximum-number/
Темы: массив, сортировка или трёхкомнатный учёт

https://leetcode.com/problems/plus-one/
Темы: массив, симуляция сложения

https://leetcode.com/problems/pascals-triangle/
Темы: массив, динамическое программирование

https://leetcode.com/problems/intersection-of-two-linked-lists/
Темы: односвязный список, два указателя

https://leetcode.com/problems/linked-list-cycle/
Темы: односвязный список, два указателя (черепаха и заяц)

https://leetcode.com/problems/symmetric-tree/
Темы: дерево, рекурсия или BFS

https://leetcode.com/problems/single-number/
Темы: массив, побитовое XOR

https://leetcode.com/problems/majority-element/
Темы: массив, голосование Бойера–Мура или хэш-таблица

Черканите, в комментариях, кто еще пробует со мной этот марафон.
👍4
Шаблон Request - Reply

Добавил урок в курс https://job4j.ru/profile/courses/21
Request-Reply шаблон [#505464]

У кого есть желание может попробовать решить задание тоже.

В этом уроке мы поговорим об асинхронной природе Kafka.

Сообщение, отправленное продюсером, попадает в брокер, и единственное, что мы можем получить в ответ, — это подтверждение о регистрации в брокере. О механизме доставки сообщений мы поговорим в другом уроке.

А теперь представьте ситуацию: у вас есть сервис A, который принимает от клиентов запрос через REST API. Ему нужны данные от сервиса B, который в вашей архитектуре общается только асинхронно, то есть через Kafka. Возникает неприятная ситуация конфликта двух идеологий: синхронного и асинхронного общения.

Очевидное решение такой проблемы — заблокировать синхронный вызов до момента прихода асинхронного ответа. Эта идея выражена в шаблоне Request-Reply. В Spring Cloud Kafka есть готовая реализация этого шаблона, но мы будем реализовывать её самостоятельно.

Сначала рассмотрим простую ситуацию без очередей.

* Thread — клиент REST API.
* Thread — асинхронный клиент Kafka.
* ReqReply — контейнер, который блокирует Thread REST API на заданное время. Если в это время приходит ответ от Thread Kafka, контейнер разблокируется и возвращает ответ.

package ru.job4j.kafka.reqreply

import java.util.concurrent.FutureTask

class ReqReply(val timeout: Long) {
private val monitor = Object()
private var message = ""
private var received = false

fun send(): String {
synchronized(monitor) {
if (!received) {
try {
monitor.wait(timeout)
} catch (e: InterruptedException) {
Thread.currentThread().interrupt()
return "Interrupted while waiting"
}
}
return if (received) message else "Happened timeout $timeout"
}
}

fun receive(text: String) {
synchronized(monitor) {
received = true
message = text
monitor.notifyAll()
}
}
}

fun main() {
val reply = ReqReply(10)
val task = FutureTask({
reply.send()
})
Thread(task).start()
Thread({
Thread.sleep(500)
reply.receive("Send message")
}).start()
println(task.get())
}


Напомню, что здесь происходит:
— wait переводит нить в режим ожидания и освобождает монитор.
— notifyAll переводит все нити, ожидающие на мониторе, в состояние RUNNING.

Задание:

1. Доработайте код, приведённый выше, так, чтобы он работал с ключом correlationId:


   val correlationId = UUID.randomUUID().toString()
val reply = ReqReply(10)
val task = FutureTask({
reply.send(correlationId)
})


2. Добавьте тесты (три сценария):

* send(correlationId = 1), receive(correlationId = 1)
* send(correlationId = 1), таймаут
* send(correlationId = 1), receive(correlationId = 2), таймаут

3. Загрузите код в репозиторий и оставьте ссылку на изменения.
👍6🔥1
Channel name was changed to «Job for Java»
Алго-марафон | 4 Неделя

Я продолжаю решать алгоритмы на Leetcode, но скажу, что переоценил свои усилия. За прошлую неделю решил только три задачи на динамическое программирование. Как я и говорил, они у меня вызывали сложности, потому что я в них не особо разобрался.

Сегодня перечитывал раздел о динамическом программировании в книге "Грокаем алгоритмы". Вроде бы написано довольно просто, но всё равно вызывает сложности в понимании.

Хочу предложить вам свой вариант разбора алгоритмов с динамическим программированием.

Начну с того, как определить задачу с ДП (динамическим программированием, далее по тексту):

* Имеет оптимальные подзадачи — задачу можно разбить на несколько меньших, которые решаются отдельно.
* Есть повторяющиеся вычисления — одна и та же подзадача решается несколько раз.
* Зависит от предыдущих решений — решение задачи зависит от решений предыдущих подзадач (перекрывающиеся подпоследовательности или выборы).

Рассмотрим задачу с вычислением числа Фибоначчи.
Само определение чисел Фибоначчи говорит о том, что у нас есть повторяющиеся вычисления.
Число Фибоначчи вычисляется как сумма n - 1 и n - 2.
У чисел Фибоначчи есть начальные значения: f(0) = 0, f(1) = 1, f(2) = 1.

И теперь главное правило: когда мы начинаем решать задачу на ДП, мы начинаем её с массива dp, где будут храниться промежуточные результаты.

Второй обязательный элемент — это инициализация начального состояния. В данном случае мы заполняем ячейки 0, 1, 2 изначально известными значениями.

Теперь задача сводится к заполнению массива.

https://leetcode.com/problems/fibonacci-number/description/

Попробуйте решить её на Java.

А я приведу пример на Go:

func fib(n int) int {
if n == 0 {
return 0
}
if n == 1 || n == 2 {
return 1
}
dp := make([]int, n + 1)
dp[0], dp[1], dp[2] = 0, 1, 1
for i := 3; i <= n; i++ {
dp[i] = dp[i - 1] + dp[i - 2]
}
return dp[n]
}


Особенность этой задачи в том, что нам изначально известна зависимость последовательности.
Посмотрите на другой пример, где тоже изначально известна зависимость последовательности:
https://leetcode.com/problems/pascals-triangle/?envType=problem-list-v2&envId=dynamic-programming.

Идем по шагам:

1. Создаем массив. В данном случае он будет двумерным.
2. Инициализируем начальные состояния.
3. Вычисляем последующие значения на основании предыдущих.

В задачах посложнее вам нужно будет самостоятельно найти закономерность последовательности:
https://leetcode.com/problems/min-cost-climbing-stairs/description/?envType=problem-list-v2&envId=dynamic-programming.

Давайте начнем с начальных этапов:

1. Создаем массив.
2. Инициализируем начальные состояния. 0 шагов — стоимость 0, 1 ступенька — cost(1), 2 ступеньки — cost(2).
А теперь нужно посчитать стоимость движения по 3 ступеньке. Она будет равна стоимости самой ступеньки плюс стоимости ступеньки, с которой мы пришли.
И тут есть нюанс: мы можем шагнуть либо с предыдущей ступеньки, либо с предпредыдущей.
Так как нам нужна минимальная стоимость, мы должны выбрать тот путь, который будет дешевле.

Этот раздел я также добавлю в наш курс: https://job4j.ru/courses/32.
Изначально его не добавил, так как на собеседованиях не встречал, но потом вспомнил, что было собеседование, где нужно было вычислить количество способов размена купюры монетами. У нас есть подобная задача в разделе "Базовый синтаксис", которая решается через жадный алгоритм, но для ответа на возможное количество способов нужно использовать решение через ДП.
🔥5
Из Java в Go за 5 месяцев | Пошаговый план

https://youtu.be/c5xlrhRQrVE?si=d-bsrlmYSwUuxAft
🔥6
Топ 5 заблуждений о GoLang

В этой статье хотел бы рассказать о 5 заблуждениях о языке Go. Я сам попался на эти мифы, когда только начал изучать Go.

Go — это простой язык

Это самый большой миф о языке. Причем его распространяют повсеместно, и это часть маркетинга компании, причем крайне успешно. Я, например, тоже попался на это, но теперь понимаю, что Go совсем не легкий язык. С первого взгляда действительно язык выглядит компактным: базовые конструкции программирования (условия, циклы), три структуры данных (массив, слайс и карта), не до ООП, не до ФП и козырь горутины.

Но когда начинаешь писать что-то более сложное, чем задачи с LeetCode, то понимаешь, что в языке есть куча нюансов, которые необходимо учитывать, иначе код просто не будет работать. Причем на нем очень легко написать так, что понять причину ошибки будет крайне сложно.

Небольшой пример из моего проекта — антиспам-бот:

func NewConnect(path string) (*sqlx.DB, error) {  
cfg, err := load(path)
if err != nil {
return nil, &ConfigError{message: fmt.Sprintf("failed to load config from %s", path)}
}
cnn, err := sqlx.Connect(cfg.driverName, cfg.dsn)
if err != nil {
return nil, &DbError{message: fmt.Sprintf("failed to connect to %s", err.Error())}
}
return cnn, nil
}

db, err := database.NewConnect(".env")
defer func() {
if err := db.Close(); err != nil {
log.Printf("error closing db: %v", err)
}
}()
if err != nil {
log.Printf("Error connecting to database: %v", err)
return
}


Если при создании подключения что-то пошло не так, указатель на db будет nil, а в функции defer мы пытаемся вызвать у него метод, что приведет к panic.

Go — выразительный и лаконичный

Как и многие, мне хотелось бы, чтобы для написания кода требовалось меньше усилий, а результат был бы больше. Именно поэтому несколько лет назад я начал использовать Kotlin. Если вы не пробовали писать на Kotlin, то после Java вы сразу заметите, насколько меньше нужно писать кода.

Я думал, что то же самое будет с Go. И действительно, первые программы на LeetCode выглядели компактными. Но когда я перешел к написанию своего проекта, dionea, я сразу понял, что писать придется много. Причем иногда код прямо повторяется, особенно при работе с объектами error.

if err != nil {
return fmt.Sprintf("occurred error %v", err)
}


Пример — функция-обертка для транзакций:

func tx(db *sqlx.DB, logic func(tx *sql.Tx) error) error {  
tx, err := db.Begin()
if err != nil {
return err
}

var logicErr error
defer func() {
if logicErr != nil {
rbErr := tx.Rollback()
if rbErr != nil {
log.Printf("Error during rollback: %v", rbErr)
}
}
}()
logicErr = logic(tx)
if logicErr != nil {
return logicErr
}
logicErr = tx.Commit()
if logicErr != nil {
return logicErr
}
return nil
}


Аналогичный код на Java:

public <T> T tx(Function<Session, T> command) {
Session session = sf.openSession();
Transaction transaction = null;
try {
transaction = session.beginTransaction();
T rsl = command.apply(session);
transaction.commit();
return rsl;
} catch (Exception e) {
if (transaction != null) {
transaction.rollback();
}
throw e;
} finally {
session.close();
}
}


В Go нет ООП

Для меня этот пункт был самым интересным в начале моего пути в Go. Как же решаются вопросы без ООП? Как оказалось, все там есть — и ООП, и ФП, и даже процедуры.

Главный аргумент: как же ООП без наследования?

Оно тоже есть через embedded struct. Причем даже с ромбической проблемой. Но в Go есть философия: «Вот так делать не надо». Самое забавное, что в своих Java и Kotlin проектах я тоже придерживаюсь принципа замены наследования на композицию. А в учебных проектах настоятельно прошу студентов избегать использования наследования.
👍7