Size: a a a

2021 February 07
Блог*
#bio
источник
Блог*
Разгадана одна из главных тайн мироздания: Почему вомбат срет кубиками?

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

Так вот это не так. У вомбатов любопытный кишечник. Одни участки мышц в нем твердые, другие эластичные. И именно поэтому какашки получаются почти квадратными. Но возникает вопрос - а зачем им быть такими? Какой смысл заложила в это Природа? Дело в том, что вомбаты таким образом помечают территорию и общаются с сородичами. Какашка, выпав из жопы, должна остаться на месте, чтобы донести свой "месседж". А будь она круглой, то просто укатится хрен знает куда. Поэтому куб - идеальная форма.

Знание - сила!
источник
2021 February 08
Блог*
#prog #quotes
источник
Блог*
Если ты в душе плюсовик — ты и на JS уб понапишешь
источник
Блог*
#prog #article

Статья (перевод) о том, как сбить с толку шахматный движок и заставить его крашнуться или предложить заведомо неверные ходы.
источник
Блог*
Скажите что-то хорошее о канале
источник
Блог*
Скажите что-то плохое о канале
источник
2021 February 09
Блог*
#prog #rust #meme

Какой же ор. Жаль, что те, кто не пишут на Rust, не поймут.
источник
Блог*
источник
Блог*
источник
Блог*
вафель держит Илью 2021 год, фото в цвете
источник
Блог*
источник
2021 February 10
Блог*
#prog #python
источник
Блог*
**Pattern matching accepted for Python**

Давайте поорём
источник
Блог*
#prog #cpp

— Mom, can we have pattern matching?
— We already have pattern matching at home.

Meanwhile pattern matching at home:
https://github.com/solodon4/Mach7
источник
Блог*
#prog #go
источник
Блог*
источник
2021 February 11
Блог*
dereference_pointer_there
#моё

"Разбудите меня через сто лет, спросите, что делают гоферы, и я отвечу: копируют и вставляют." © Почти Салтыков-Щедрин
Эх...
источник
Блог*
Никто:
Абсолютно никто:
Мой коллега в час ночи в рабочем чатике:

— Эй, а попробуйте решить на расте вот такую задачу по десериализации YAML!..

#трудовыебудни
источник
Блог*
dereference_pointer_there
#prog #rust #моё

В Rust есть такая удобная вещь, как сопоставление с образцом (pattern matching), и она работает в том числе и для строк. К сожалению, оно позволяет сопоставлять только строки целиком, но не по частям. В частности (no pun intended), match не позволяет разделить строку на некоторый фиксированный префикс и всё остальное.

Или всё же позволяет? В конце-концов, можно написать так:

match str_value.as_bytes() {
   [b'p', b'r', b'e', b'f, b'i', b'x', rest @ ..] => {}
   _ => {}
}

, и тут даже будет помогать компилятор — он подскажет нам, если мы будем дважды проверять один и тот же префикс. Но тут есть и недостатки: остаток строки (rets во второй строчке) — не &str, а &[u8], ну и, конечно, это довольно неудобно писать. Первый недостаток отчасти перекрывается str::get_unchecked/std::str::from_utf8_unchecked — отчасти, поскольку в паттерн байта можно написать и часть многобайтового символа, а вот второй недостаток обойти сложнее. В идеале мы бы хотели написать матч в виде сопоставления части строки, чтобы потом он скомпилировался в примерно такой же код, как наверху — чтобы к нему могли быть применены те же оптимизации, что и к обычному матчу, и чтобы получить выгоду от проверки полноты покрытия — но это довольно существенное вмешательство в синтаксис, требующее написания процедурного макроса, написание которого отводится читателю в качестве самостоятельного упражнения.

Если же ослабить требование максимальной эффективности генерируемого кода (серьёзно, Rust и так достаточно быстрый), то можно обойтись более слабыми macro_rules!. Как можно переписать сопоставление с префиксом на обычные функции? Один из способов — это написать match, в котором значение ни с чем не сопоставляется, а условие "начинается с заданного префикса" задаётся в охранном выражении (guard clause). Сказано — сделано:

macro_rules! prefixes {
   (match $value:ident {
       $($prefix:literal.. => $arm:expr,)*
       _ => $catch_all:expr $(,)?
   }) => {
       match $value {
           $(x if x.starts_with($prefix) => $arm,)*
           _ => $catch_all,
       }
   }
}

Ну и давайте сделаем какую-нибудь функцию, которая использует этот макрос:

fn use_prefixes(s: &str) -> String {
   prefixes!(match s {
       "foo".. => s.to_string(),
       "bar".. => [s, s].concat(),
       _ => String::new(),
   })
}

fn main() {
   let inputs = [
       "foobar",
       "barfoo",
       "overall",
   ];

   for input in &inputs[..] {
       println!("{:?}", use_prefixes(input));
   }
}

Но, погодите-ка, так потеряли одно из преимуществ компилятора: проверку полноты покрытия! Как мы можем её восстановить? Пойдём ленивым путём: сделаем свою функцию, в которой будем матчить по переданным строкам и позволим компилятору сделать работу за нас. Однако возникает вопрос, где эту функцию хранить? Простейший способ добиться этого — обернуть весь итоговый match в один блок и сделать внутри этого блока функцию. Так как функция не будет использована, она будет помечена #[allow(dead_code)], а на внутренний match повесим #[warn(unreachable_patterns)], чтобы предупреждения компилятора были даже в том случае, если они по каким-то причинам выключены на верхнем уровне:

macro_rules! prefixes {
   (match $value:ident {
       $($prefix:literal.. => $arm:expr,)*
       _ => $catch_all:expr $(,)?
   }) => {{
       #[allow(dead_code)]
       fn non_repeating() {
           #[warn(unreachable_patterns)]
           match "" {
               $($prefix => (),)*
               _ => (),
           }
       }
       match $value {
           $(x if x.starts_with($prefix) => $arm,)*
           _ => $catch_all,
       }
   }}
}


Попробуем оставить в use_prefixes одинаковые префиксы:

fn use_prefixes(s: &str) -> String {
   prefixes!(match s {
       "foo".. => s.to_string(),
       "foo".. => [s, s].concat(), // <--
       _ => String::new(),
   })
}


Что же скажет компилятор?
#prog #rust #моё

Кое-что я при разработке этого макроса упустил: проверка на недостижимость кода недостаточно точна. Если строка не начинается с префикса, скажем, "java", то пытаться отщипнуть префикс "javascript" уже не имеет смысла, потому что он заведомо отсутствует. Однако мой макрос проверяет строки на (не)равенство и не учитывает их возможных структурных отношений. Сейчас мы это исправим, и пойдём по уж проторенной дорожке: вынесем нужную проверку в const fn и будем генерировать код, который не тайпчекается в случае, если проверка завершилась неудачей.

Начнём с выяснения того, является ли одна строка префиксом другой. Ничего сложного в этом нет (кроме того, чтобы перепутать одну строку с другой, ага), просто несколько неудобно из-за ограничений const fn:

const fn is_prefix(s: &str, maybe_prefix: &str) -> bool {
   if maybe_prefix.len() > s.len() {
       return false;
   }

   let s = s.as_bytes();
   let mp = maybe_prefix.as_bytes();
   let mut i = 0;

   while i < mp.len() {
       if mp[i] != s[i] {
           return false;
       }
       i += 1;
   }

   true
}

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

const fn has_unreachable_patterns(ss: &[(&str, bool)]) -> bool {
   if ss.is_empty() {
       return false;
   }

   let mut i = 0;
   let mut j;
   while i < ss.len() - 1 {
       // строки, для которых есть охранное выражение, пропускаем
       if ss[i].1 {
           i += 1;
           continue
       }
       j = i + 1;
       while j < ss.len() {
           // А вот вторую строку в паре нужно проверять всегда,
           // вне зависимости от того, есть у него охранное выражение или нет:
           // если охранного выражения нет у первой строки, то исполнение
           // до ветки со второй строкой точно не дойдёт.
           if is_prefix(ss[j].0, ss[i].0) {
               return true;
           }
           j += 1;
       }
       i += 1;
   }

   false
}

Осталось только сгенерировать код для проверки, заменив в макросах функцию non_repeating (это изменение, кстати, одинаковое для всех двух (трёх) макросов) — и это, кажется, наиболее зубодробительная часть:

const _HAS_NO_UNREACHABLE_PATTERNS: [(); 0] = [
   ();
   has_unreachable_patterns(
       &[
           $((
               $prefix,
               false $(|| {stringify!($condition); true})?
           ),)*
       ]
   ) as _
];

Это довольно много, так что разберём по частям:

const _HAS_NO_UNREACHABLE_PATTERNS: [(); 0] = [
   ();
   has_unreachable_patterns(...) as _
];

Это объявление константы. Если has_unreachable_patterns(...) вычисляется в true, то оно при касте в usize становится 1, вызывая ошибку несоответствия типов.

&[
   $((
       $prefix,
       ...
   ),)*
]

Здесь мы формируем ссылку на литерал массива, элементами которого являются пары, первыми элементами которых являются строки-префиксы.

false $(|| {stringify!($condition); true})?

Здесь мы формируем значение true, если охранное выражение имеется. Сделать повтор синтаксического фрагмента без соответствующей синтаксической метапеременной нельзя, но и само выражение нам не требуется, поэтому воспользуемся стандартным трюком: переведём выражение в безвредную строку и отбросим её. На итоговое выражение это не повлияет, а наличие нужной метапеременной обеспечено. И да, это не замыкание, как может показаться, а выражение с оператором "или".
источник