Интересный вопрос. Хотя я не уверен, что и после карантина маски останутся столь же массовыми
Forwarded from Shady Bytes
Когда закончится корона, станут ли маски новым массовым предметом одежды по всему миру?
Anonymous Poll
32%
Да
46%
Нет
22%
Хз
#prog #article
Конец эпохи, можно сказать. Дедфуд официально объявил, что устал от C++ и вообще выгорел.
It's RESF time!
habr.com/ru/post/497114/
Конец эпохи, можно сказать. Дедфуд официально объявил, что устал от C++ и вообще выгорел.
habr.com/ru/post/497114/
Блог*
#prog #article Конец эпохи, можно сказать. Дедфуд официально объявил, что устал от C++ и вообще выгорел. It's RESF time! habr.com/ru/post/497114/
#prog #article
(По ссылке из статьи)
Может ли апгрейд gcc с gcc-9 до gcc-10 сломать софт? Разумеется, может. Как ни странно, больше всего проблем принёс сам факт инкремента версии... Но об этом подробнее в самой статье.
trofi.github.io/posts/213-gcc-10-in-gentoo.html
(По ссылке из статьи)
Может ли апгрейд gcc с gcc-9 до gcc-10 сломать софт? Разумеется, может. Как ни странно, больше всего проблем принёс сам факт инкремента версии... Но об этом подробнее в самой статье.
trofi.github.io/posts/213-gcc-10-in-gentoo.html
#prog #моё
Люди, которые пишут на Go, в качестве преимущества этого ЯП выставляют его простоту. Согласно аргументации сторонников Go, отсутствие навёрнутых возможностей приводит к тому, что все программисты пишут понятный код. Ну, с тем, что Go простой, действительно спорить не приходится, а вот вывод из этого факта лично мне кажется сомнительным. Почему же? Что ж, начну издалека.
У меня есть один знакомый (попросивший его не называть), который говорил мне, что является программистом средней руки и большую часть времени пишет весьма приземлённый код. В качестве доказательства он прислал кусок своего кода:
В оригинальной программе на Go не так уж и много сущностей, и все из них можно выучить, пройдя официальный тур по языку. Rust же, как язык, безусловно сложнее, чем Go, и в моём варианте куда больше элементов:
* ссылки (в том числе и ссылки на String)
* различие между
* замыкания
* четыре различных комбинатора итераторов (включая
* тип
* оператор
* явно обозначенная мутабельность
* явное копирование строк
С другой стороны, этот вариант куда лучше (на мой безусловно пристрастный взгляд) передаёт смысл функции. В нём построчно записано следующее: взять строки из
Люди, которые пишут на Go, в качестве преимущества этого ЯП выставляют его простоту. Согласно аргументации сторонников Go, отсутствие навёрнутых возможностей приводит к тому, что все программисты пишут понятный код. Ну, с тем, что Go простой, действительно спорить не приходится, а вот вывод из этого факта лично мне кажется сомнительным. Почему же? Что ж, начну издалека.
У меня есть один знакомый (попросивший его не называть), который говорил мне, что является программистом средней руки и большую часть времени пишет весьма приземлённый код. В качестве доказательства он прислал кусок своего кода:
func getIDsForTextSearch(sortedIDs []string, spottedIDsWithDocCount map[string]int, paging paging) ([]string, int) {
var countDocsNeedForRequest int
var countIDsNeedToShow int
sortedByDateSpottedIDs := []string{}
IDsForSearch := []string{}
for _, k := range sortedIDs {
if _, wasSpotted := spottedIDsWithDocCount[k]; wasSpotted {
sortedByDateSpottedIDs = append(sortedByDateSpottedIDs, k)
}
}
if len(sortedByDateSpottedIDs) < paging.Offset + paging.Limit {
countIDsNeedToShow = len(sortedByDateSpottedIDs)
} else {
countIDsNeedToShow = paging.Offset + paging.Limit
}
for i := paging.Offset; i < countIDsNeedToShow; i++ {
id := sortedByDateSpottedIDs[i]
IDsForSearch = append(IDsForSearch, id)
countDocsNeedForRequest += spottedIDsWithDocCount[id]
}
return IDsForSearch, countDocsNeedForRequest
}
Конечно, имена по энтерпрайзной моде усложняют понимание этого кода, но я смог уловить его суть — другое дело, что для этого мне потребовалось переписать его на Rust (место для вашего саркастического комментария о моих умственных способностях). Как оказалось, смысл этого кода таков: взять из sortedIDs
строки, содержащиеся в spottedIDsWithDocCount
, и выделить из этой последовательности окно длиной paging.Limit
, начиная с paging.Offset
-го элемента, попутно просуммировав связанные с этими id количества. Код достаточно прямолинейный и неисправимо последовательный, так что киллер-фича Go — горутины — здесь не пригодится. Не буду утомлять вас промежуточными версиями переписывания и покажу сразу конечный результат:fn get_ids_for_text_search(
sorted_ids: &[String],
spotted_ids_with_doc_count: &HashMap<String, usize>,
paging: Paging,
) -> (Vec<String>, usize) {
sorted_ids
.iter()
.filter_map(|s| {
let &count = spotted_ids_with_doc_count.get(s)?;
Some((s, count))
})
.skip(paging.offset)
.take(paging.limit)
.fold((Vec::with_capacity(paging.limit), 0), |(mut ids, total), (s, count)| {
ids.push(s.clone());
(ids, total + count)
})
}
В оригинальной программе на Go не так уж и много сущностей, и все из них можно выучить, пройдя официальный тур по языку. Rust же, как язык, безусловно сложнее, чем Go, и в моём варианте куда больше элементов:
* ссылки (в том числе и ссылки на String)
* различие между
Vec<String>
и &[String]
* замыкания
* четыре различных комбинатора итераторов (включая
fold
, понимание которого у многих новичков почему-то вызывает проблему)* тип
Option
* оператор
?
* явно обозначенная мутабельность
* явное копирование строк
С другой стороны, этот вариант куда лучше (на мой безусловно пристрастный взгляд) передаёт смысл функции. В нём построчно записано следующее: взять строки из
sorted_ids
, выделить из них те, которые лежат в spotted_ids_with_doc_count
, пропустить первые paging.offset
из них, взять первые paging.limit
из них (выделив, таким образом, интересующий нас диапазон) и затем неразборчивое бормотание, получив список всех строк и сумму всех связанных с ними значений. Здесь практически нет явных изменений переменных, процесс описан скорее в терминах трансформации потока данных.👍2
Является ли это единственным достоинством нового варианта? Отнюдь.
Оригинальный код собирает промежуточный слайс — в моём варианте собирается только конечный результат (причём память выделяется заранее).
Оригинальный код дважды делает поиск в мапе по итоговым строкам — здесь же поиск осуществляется единожды.
Оригинальный код проходит по всему слайсу
Оригинальный код рассчитывает на то, что в
Все эти итераторы в итоге успешно компилируются в единый цикл, так что эффективность в рантайме не страдает. Можно ли применить те же техники оптимизации для Go? Конечно. Вот код, вторично переведённый на другой язык:
Подведём итоги. У множества задач есть сложность, которую нельзя просто убрать. При дизайне языка программирования можно на него выделить разный бюджет сложности. Создатели Go намеренно этот бюджет урезали, создатели Rust сильно ограничивать не стали (что логично, учитывая, что Rust долго развивался эволюционно, без изначального видения идеального ЯП — и развивается и по сей день). Последствия этих выборов достаточно ожидаемы: в Go сложность задачи размазывается по достаточно объёмному коду, в то время как Rust позволяет использовать сложность языка для того, чтобы получить в итоге относительно простое решение. Кто-то может сказать, что перемещения сложности туда-сюда погоды не делают, и итоговая сложность остаётся той же. Я не могу с этим согласится. Да, Rust относительно сложен (уж точно сложнее Go — и это не хвастовство, а просто констатация факта), но язык, равно как и стандартная библиотека для него, учится один раз, в то время как последствия излишней простоты дизайна языка приходится разгребать в коде постоянно. Лично для меня подобная однократная инвестиция, которая впоследствии пригодится множество раз, видится более разумным выбором — но вы, разумеется, вольны со мной не согласиться.
Спасибо, что пришли на мою лекцию TED
Оригинальный код собирает промежуточный слайс — в моём варианте собирается только конечный результат (причём память выделяется заранее).
Оригинальный код дважды делает поиск в мапе по итоговым строкам — здесь же поиск осуществляется единожды.
Оригинальный код проходит по всему слайсу
sortedIDs
— в ржавом варианте обход заканчивается, когда набирается достаточно подходящих строк.Оригинальный код рассчитывает на то, что в
sortedByDateSpottedIDs
будет как минимум countIDsNeedToShow
строк — в варианте ниже итоговый вектор просто будет содержать меньше paging.limit
строк, если их не хватает (это, кстати, можно считать как плюсом, так и минусом, но надо отметить, что нарушение этого ожидания в оригинальном варианте вызовет панику из-за некорректного индекса, а в моём варианте можно постфактум проверить длину возвращённого вектора).Все эти итераторы в итоге успешно компилируются в единый цикл, так что эффективность в рантайме не страдает. Можно ли применить те же техники оптимизации для Go? Конечно. Вот код, вторично переведённый на другой язык:
func getIDsForTextSearch(Всё при нём: единственный цикл, один слайс с предварительно выделенной памятью, ранний выход из цикла при достижении нужного количества строк... Что же не так? А вот что: наглядность отсутствует напрочь. Помимо двух возвращаемых значений, есть две технические переменные, которые нужно менять, и три условных оператора, которые нужно расставить в правильном порядке — или получить странные баги. Также тут есть пара мест для того, чтобы сделать ошибку на единицу. Но технически это всё ещё достаточно простой код. Вот только с запашком. Я бы на код-ревью подумал бы, стоит ли такой код пускать в прод.
sortedIDs []string,
spottedIDsWithDocCount map[string]int,
paging paging,
) ([]string, int) {
idsForSearch := make([]string, 0, paging.Limit)
skip := paging.Offset
iterated := 0
total := 0
for _, id := range sortedCallIDs {
if iterated >= paging.Limit {
break
}
count, spotted := spottedIDsWithDocCount[id]
if !spotted {
continue
}
if skip > 0 {
skip--
continue
}
iterated++
idsForSearch = append(idsForSearch, id)
total += count
}
return idsForSearch, total
}
Подведём итоги. У множества задач есть сложность, которую нельзя просто убрать. При дизайне языка программирования можно на него выделить разный бюджет сложности. Создатели Go намеренно этот бюджет урезали, создатели Rust сильно ограничивать не стали (что логично, учитывая, что Rust долго развивался эволюционно, без изначального видения идеального ЯП — и развивается и по сей день). Последствия этих выборов достаточно ожидаемы: в Go сложность задачи размазывается по достаточно объёмному коду, в то время как Rust позволяет использовать сложность языка для того, чтобы получить в итоге относительно простое решение. Кто-то может сказать, что перемещения сложности туда-сюда погоды не делают, и итоговая сложность остаётся той же. Я не могу с этим согласится. Да, Rust относительно сложен (уж точно сложнее Go — и это не хвастовство, а просто констатация факта), но язык, равно как и стандартная библиотека для него, учится один раз, в то время как последствия излишней простоты дизайна языка приходится разгребать в коде постоянно. Лично для меня подобная однократная инвестиция, которая впоследствии пригодится множество раз, видится более разумным выбором — но вы, разумеется, вольны со мной не согласиться.
👍2
Блог*
#prog #моё Люди, которые пишут на Go, в качестве преимущества этого ЯП выставляют его простоту. Согласно аргументации сторонников Go, отсутствие навёрнутых возможностей приводит к тому, что все программисты пишут понятный код. Ну, с тем, что Go простой, действительно…
Последовал советам профессионалов, так сказать
#prog #article
Дежурное напоминание, что C — не "близкий к железу" язык.
queue.acm.org/detail.cfm?id=3212479
Дежурное напоминание, что C — не "близкий к железу" язык.
queue.acm.org/detail.cfm?id=3212479
queue.acm.org
C Is Not a Low-level Language - ACM Queue
In the wake of the recent Meltdown and Spectre vulnerabilities, it
Количество подписчиков моего блога медленно, но неуклонно растёт. Я начал подумывать о том, чтобы дать своему каналу какое-то более выделяющееся имя, ибо из опыта общения с людьми, знающих о канале, я понял, что астериск в названии канала ("Блог*") многие не считают частью названия, а скорее индикатором исправления опечатки. В связи с этим я бы хотел у вас узнать...
...Каким вы видите новое название канала?
Final Results
13%
Dereference pointer there
3%
Блог Антона
13%
Ржавый блог
1%
Антон_лог
5%
Типы и страдания
1%
Politically unaligned reference
22%
&*
2%
Восхитительные низкочастотники
3%
Свой вариант (в чат канала или лс, но лучше в чат)
37%
Да норм название, оставь как есть
#prog #amazingopensource
Потрясающее введение в дизайн крупных программных систем. С кучей ссылок для последующего изучения.
github.com/donnemartin/system-design-primer
Потрясающее введение в дизайн крупных программных систем. С кучей ссылок для последующего изучения.
github.com/donnemartin/system-design-primer
GitHub
GitHub - donnemartin/system-design-primer: Learn how to design large-scale systems. Prep for the system design interview. Includes…
Learn how to design large-scale systems. Prep for the system design interview. Includes Anki flashcards. - donnemartin/system-design-primer
#prog #article
Мы всё ещё не умеем программировать.
http://www.eecg.toronto.edu/~yuan/papers/failure_analysis_osdi14.pdf
Мы всё ещё не умеем программировать.
This paper presented an in-depth analysis of 198 user-reported failures in five widely used, data-intensive distributed systems in the form of 12 findings. We found that the error manifestation sequences leading to the failures to be relatively complex. However, we also found that for the most catastrophic failures, almost all of them are caused by incorrect error handling, and 58% of them are trivial mistakes or can be exposed by statement coverage testing.
http://www.eecg.toronto.edu/~yuan/papers/failure_analysis_osdi14.pdf
Блог*
#prog Leetcode проводит челлендж: 30 дней задач по программированию, из числа тех, что задают на интервью. Обещают простые задачи. Для тех, кто хорошо решает, разыгрываются призы. Только, это... Уже неделю как идёт. leetcode.com/explore/featured/card/30…
#prog
Челлендж закончился, и что же Leetcode предлагает теперь? Вы не поверите — ещё один месячный челлендж.
С другой стороны, мне мероприятие понравилось, так что в майском варианте я также буду участвовать. Тем более что теперь я смогу решать все задачи в тот же день, что они появились, и таким образом получить шанс на выигрыш призов.
https://leetcode.com/explore/challenge/card/may-leetcoding-challenge/
Челлендж закончился, и что же Leetcode предлагает теперь? Вы не поверите — ещё один месячный челлендж.
С другой стороны, мне мероприятие понравилось, так что в майском варианте я также буду участвовать. Тем более что теперь я смогу решать все задачи в тот же день, что они появились, и таким образом получить шанс на выигрыш призов.
https://leetcode.com/explore/challenge/card/may-leetcoding-challenge/
Leetcode
Explore - LeetCode
LeetCode Explore is the best place for everyone to start practicing and learning on LeetCode. No matter if you are a beginner or a master, there are always new topics waiting for you to explore.
Блог*
#prog Leetcode проводит челлендж: 30 дней задач по программированию, из числа тех, что задают на интервью. Обещают простые задачи. Для тех, кто хорошо решает, разыгрываются призы. Только, это... Уже неделю как идёт. leetcode.com/explore/featured/card/30…
В итоге наиболее запоминающийся для меня задачей стала реализация LRU-кеша. Фактически я там сделал двухсвязный список поверх вектора, используемой как арены, что позволило мне хранить в хэшмапе индексы на интересующие меня элементы.
Сделал. Был доволен собой (очень уж там много граничных случаев было). А потом увидел, что моё решение хоть и быстрое, но далеко не лучшее по скорости. Открыл топовое решение — там двухсвязный список на сырых указателях :(
С другой стороны, в это решении для материализации двух изначальных узлов использовались Box::leak, и я не нашёл там кода, который бы потом эту память очищал, так что память в этом решении действительно течёт. Надо будет попробовать переделать на аренах свой вариант, может, нормально по скорости будет.
Сделал. Был доволен собой (очень уж там много граничных случаев было). А потом увидел, что моё решение хоть и быстрое, но далеко не лучшее по скорости. Открыл топовое решение — там двухсвязный список на сырых указателях :(
С другой стороны, в это решении для материализации двух изначальных узлов использовались Box::leak, и я не нашёл там кода, который бы потом эту память очищал, так что память в этом решении действительно течёт. Надо будет попробовать переделать на аренах свой вариант, может, нормально по скорости будет.
doc.rust-lang.org
Box in std::boxed - Rust
A pointer type that uniquely owns a heap allocation of type `T`.
Блог*
#prog Leetcode проводит челлендж: 30 дней задач по программированию, из числа тех, что задают на интервью. Обещают простые задачи. Для тех, кто хорошо решает, разыгрываются призы. Только, это... Уже неделю как идёт. leetcode.com/explore/featured/card/30…
#prog #моё
Ещё одна вещь, которая мне запомнилась — это решение задачи про подсчёт количества островов. Всё просто: есть двухмерный массив из
Идея была относительно простой: мы проходимся по каждой строке. Нули ("воду") мы просто пропускаем. Для единиц же мы проверяем соседей слева и сверху у текущего элемента. Если оба соседа — "вода", то создаётся новая территория, и текущей единице приписывается идентификатор этой территории. Если сосед-"земля" только один, то мы приписываем этой клетке ту же территорию, что и у соседа. Если соседа-"земли" — два, причём с разными идентификаторами, то нам нужно объединить эти две территории. В отдельной табличке под территории для каждого id записано, является ли территория самостоятельной или же частью другой территории (в таком случае там лежит id этой другой территории). Очевидно, нужно для одной из территорий записать, что одна является частью другой, но какую из них модифицировать? Я решил, что стоит всегда территорию с бо́льшим id делать частью территории с меньшим id. Таким образом, все связи (ссылки на "родительскую" территорию) могут перемещаться только ближе к нулю, но никогда дальше от него. Я убедил себя, что этого (вкупе с тем фактом, что id строго возрастают при создании) достаточно для корректного решения задачи. Прогнал своё решение на тестовых примерах, получил правильный ответ и отправил своё решение, будучи уверен, что пройду полный набор тестов.
Ага, щас.
Ещё одна вещь, которая мне запомнилась — это решение задачи про подсчёт количества островов. Всё просто: есть двухмерный массив из
1
и 0
("суша" и "вода" соответственно), требуется подсчитать количество связных (в смысле окрестности фон Неймана) областей из единиц. Простейший способ решить задачу — это бахнуть depth first search (и в силу того, что вход дан как Vec<Vec<char>>
, его даже можно спокойно менять), но этот вариант я сразу отмёл — ведь это же медленно, это же надо прыгать нелинейно по памяти и портить кеш (не говоря уже о том, что там нужно возиться с индексами, что я страшно не люблю делать)! Так что я решил пойти умным путём и решить задачу путём однократного сканирования каждой строки.Идея была относительно простой: мы проходимся по каждой строке. Нули ("воду") мы просто пропускаем. Для единиц же мы проверяем соседей слева и сверху у текущего элемента. Если оба соседа — "вода", то создаётся новая территория, и текущей единице приписывается идентификатор этой территории. Если сосед-"земля" только один, то мы приписываем этой клетке ту же территорию, что и у соседа. Если соседа-"земли" — два, причём с разными идентификаторами, то нам нужно объединить эти две территории. В отдельной табличке под территории для каждого id записано, является ли территория самостоятельной или же частью другой территории (в таком случае там лежит id этой другой территории). Очевидно, нужно для одной из территорий записать, что одна является частью другой, но какую из них модифицировать? Я решил, что стоит всегда территорию с бо́льшим id делать частью территории с меньшим id. Таким образом, все связи (ссылки на "родительскую" территорию) могут перемещаться только ближе к нулю, но никогда дальше от него. Я убедил себя, что этого (вкупе с тем фактом, что id строго возрастают при создании) достаточно для корректного решения задачи. Прогнал своё решение на тестовых примерах, получил правильный ответ и отправил своё решение, будучи уверен, что пройду полный набор тестов.
Ага, щас.
Leetcode
Account Login - LeetCode
Level up your coding skills and quickly land a job. This is the best place to expand your knowledge and get prepared for your next interview.