1.83K subscribers
3.3K photos
132 videos
15 files
3.58K links
Блог со звёздочкой.

Много репостов, немножко программирования.

Небольшое прикольное комьюнити: @decltype_chat_ptr_t
Автор: @insert_reference_here
Download Telegram
#prog #rust #parsing #моё

При написании парсеров часто требуется отдельно отслеживать, когда мы находимся внутри строки, выделенной кавычками, а когда — вне её. На практике это выливается в то, что появляется цикл и отдельный флаг, который меняется в зависимости от рассматриваемого символа. Эта логика затёсывается где-то между остальными строками кода, внося лишнее состояние, мешая восприятию кода и повышая вероятность совершения ошибок при внесении изменений в код. Переделка цикла на итераторы/стримы — это известный способ повысить понятность кода, но в данном случае наличие внешнего состояния не даёт возможность прямолинейно провести эту трансформацию. К счастью, в Rust есть способ инкапсулировать это состояние так, чтобы оно не маячило перед глазами — и для этого нам даже не понадобится itertools. У трейта Iterator есть метод, который можно назвать "map с состоянием". Встречайте — Iterator::scan!

Вот как выглядит начало цепочки итераторов, которая отслеживает, находимся ли мы сейчас внутри строки или нет:
s.chars()
.scan(false, |in_string, ch| {
*in_string ^= ch == '"';
Some((ch, *in_string))
})
.some_other_adapter(...)

Для демонстрации полученного кода рассмотрим функцию, которая ищет символ вне закавыченной строки в строке:

fn find_outside_string(s: &str, needle: char) -> Option<usize> {
// .chars() поменялось на .char_indices()
// для корректного отслеживания позиции.
s.char_indices()
.scan(false, |in_string, (i, ch)| {
*in_string ^= ch == '"';
Some((i, ch, *in_string))
})
.find_map(|(i, ch, in_string)| {
if ch == needle && !in_string {
Some(i)
} else {
None
}
})
}

И немного тестов:

assert_eq!(find_outside_string("Hey!", '!'), Some(3));
assert_eq!(find_outside_string(r#"Outside "Inside!" Outside!"#, '!'), Some(25));
assert_eq!(find_outside_string("Nope", '?'), None);

Как видите, весь код, относящийся непосредственно к отслеживанию строки, инкапсулирован в одном месте и не отвлекает на себя внимания.
Закончили ли мы? Не совсем: в практичных языках нам также требуется возможность экранировать кавычки, чтобы включать кавычки в строки. Обычно это приводит к ещё большему зашумлению кода парсера. Что ж, реализуем поддержку этого требования. Для того, чтобы не слишком усложнять код, будем считать, что кавычки экранируются обратным слешем (\), а сам слеш — двойным повтором символа (\\). Код поменяется не очень сильно: нужно добавить ещё один scan для отслеживания слешей:

s.chars()
.scan(false, |prev_was_slash, ch| {
let escaped = *prev_was_slash;
*prev_was_slash = ch == '\\';
Some((ch, escaped))
})
// если слеш экранирован, то второй символ
// не надо пропускать дальше
.filter_map(|(ch, escaped)| if ch == '\\' && escaped {
None
} else {
Some((ch, escaped))
})
.scan(false, |in_string, (ch, escaped)| {
// экранированые кавычки не переключают строку
if !escaped {
*in_string ^= ch == '"';
}
Some((ch, in_string))
.some_other_adapter(...)

Для демонстрации перепишем find_outside_string с поддержкой экранирования:

fn find_outside_string(s: &str, needle: char) -> Option<usize> {
s.char_indices()
.scan(false, |prev_was_slash, (i, ch)| {
let escaped = *prev_was_slash;
*prev_was_slash = ch == '\\';
Some((i, ch, escaped))
})
.filter_map(|(i, ch, escaped)| {
if ch == '\\' && escaped {
None
} else {
Some((i, ch, escaped))
}
})
.scan(false, |in_string, (i, ch, escaped)| {
if !escaped {
*in_string ^= ch == '"';
}
Some((i, ch, *in_string))
})
.find_map(|(i, ch, in_string)| {
if ch == needle && !in_string {
Some(i)
} else {
None
}
})
}

Проверим, что оно действительно работает:

let s = r#"An "Escaped \" quote" quote"#;
assert_eq!(find_outside_string(s, 'q'), Some(22));
// результат отличается от результата str::find
assert_ne!(find_outside_string(s, 'q'), s.find('q'));

Замечательной особенностью этого паттерна является то, что он без особых усилий расширяется на экранирование других символов.
#prog #python #amazingopensource

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

https://github.com/gecko984/supervenn
#prog

Шпаргалка по регулярным выражениям. Рисует синтаксические диаграммы, включает в себя интерактивную проверялку. Есть набор часто используемых шаблонов, в число которых, к сожалению, входит e-mail.

https://ihateregex.io
#prog #rust #rustlib

Библиотека для облегчения генерации кода на Rust. Ну, мало ли кого ностальгия по Go замучила.

https://github.com/carllerche/codegen
#gamedev

Пост о том, как упростили кэширование рендеринга ландшафта в FactorIO. Авторы действительно заботятся об игроках со слабыми компьютерами.

https://www.factorio.com/blog/post/fff-333
#gamedev

"This is the book I wish I had when I started making games, and now I want you to have it. "

https://gameprogrammingpatterns.com/

Есть перевод на русский: https://live13.livejournal.com/462582.html
#gamedev

Прототип игры в 3D-ASCII графике. Вещь несколько бессмысленная, но впечатляющая.

https://www.squidi.net/threep/p083/
#prog #rust

Чтобы ускорить сборку своего проекта, достаточно всего лишь... Читать продолжение
#prog #rust #gamedev

Потрясающий туториал по созданию своего рогалика на расте. Уже больше 70 глав!

https://bfnightly.bracketproductions.com/rustbook/chapter_0.html
#quotes (автор, к сожалению, неизвестен)
Forwarded from The Wacky Yellow Dog
@aikidos @Psilon

THE RESTRICT CONTRACT
I, [insert your name], a PROFESSIONAL or AMATEUR [circle one] programmer recognize that there are limits to what a compiler can do. I certify that, to the best of my knowledge, there are no magic elves or monkeys in the compiler which through the forces of fairy dust can always make code faster. I understand that there are some problems for which there is not enough information to solve. I hereby declare that given the opportunity to provide the compiler with sufficient information, perhaps through some key word, I will gladly use said keyword and not bitch and moan about how «the compiler should be doing this for me.»

In this case, I promise that the pointer declared along with the restrict qualifier is not aliased. I certify that writes through this pointer will not effect the values read through any other pointer available in the same context which is also declared as restricted.
#meme

Когда в программисткий чатик заходит девушка
#meme #bio

Простите