Список постов в категории "Программирование"

Знакомимся с полезными инструментами STL — std::remove() и std::remove_if()

Добрый день, всем привет!

Снова в эфире рубрика «Вы не спрашивали, но мы отвечаем», раздел «Программирование», глава «Что нового я узнал в сейчас лет».

Например, не так давно я узнал, что std::unordered_map не сортирует данные в отличие от std::map. А много-много лет назад (в 2007-м году) нам пришлось реализовывать бинарный поиск, чтобы ускорить вставку элементов в std::map (там жил кэш текстур) вместо того, чтобы взять std::unordered_map. Наш просчёт немного оправдывает то, что мы были молоды, неопытны и std::unordered_map появился в стандарте C++11 спустя несколько лет после описываемых событий.

Вообще, я не отношусь к тем людям, которые ежедневно читают свежайший стандарт C++, подчеркивая карандашиком важные места. Скорее, я начинаю читать стандарт, когда нужно найти решение текущей задачи. Так, например, я совершенно случайно наткнулся на std::enable_shared_from_this, когда просматривал код нашего проекта, поминая тимлида нехорошими словами . Там был метод типа make() у класса, который должен вернуть std::shared_ptr от экземпляра этого класса. Если вы наследуете класс от std::enable_shared_from_this, то у вас появляется метод shared_from_this(), и дело в шляпе. Нужно только помнить, что метод shared_from_this() можно вызвать только у объекта, который уже управляем std::shared_ptr иначе будет исключение.

Каких-то одиннадцать лет назад я написал статью про эффективное удаление элементов массива. Тогда единственный способ, который я знал, выглядел следующим образом:

auto iter=data.begin();
while(iter != data.end()) {
    if (condition(*iter)) {
        iter = puf(data, iter);
    else {
        ++iter;
    }
}

Вся упомянутая в статье магия с перемещением в конец массива и удалением происходит в puf().

Оказывается, есть метод лучше, и имя ему — std::remove_if. Этот метод перемещает в начало элементы, для которых условие ложно, возвращая итератор на конец массива с элементами, для которых условие истинно. Эксперимент показал, что после итератора лежит мусор.

С новыми знаниями, удаление элементов из контейнера будет выглядеть так:

auto iter = std::remove_if(data.begin(), data.end(), condition);
data.erase(iter, iter.end());

Что является более продвинутым вариантом и пишется короче, с чем я вас и поздравляю!

Обратите внимание, методы std::remove и std::remove_if не удаляют элементы, удаление происходит после вызова std::erase.

Пару слов про компьютерный звук

Компьютерный звук в компьютере представлен в виде (сюрприз-сюрприз!) массива чисел. Минимальная единица звука в компьютере — это семпл — одно число. Частота дискретизации — это количество семплов в одной секунде. Всем известная цифра 44100 — это всего лишь сорок четыре тысячи сто семплов в секунду. Семпл может быть в разных форматах и их достаточно много, чтобы описывать в этой статье. Лично я встречался с форматом float32 и с одним байтом (std::uint8_t) на семпл.

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

  1. Создать два буфера (A и B) в нужном формате
  2. Заполнить буфер A семплами
  3. Отдать буфер A на проигрывание звуковой карте
  4. Заполнить буфер B семплами
  5. Отдать буфер B на проигрывание звуковой карте
  6. Перейти к шагу 2.

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

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

«А как же второй канал, стерео и звук вокруг?», — спросит внимательный читатель. Лично я сталкивался только со стерео звуком, поэтому про пяти и более канальный звук не расскажу. Семплы для стерео звука располагаются в буфере interleaved, по очереди: LRLRLRLR. А значит для стерео звука количество семплов на секунду в буфере удваивается. И на одну секунду для частоты 44100 надо уже 88200 семплов.

Настройка title для каждой страницы сайта на WordPress без плагинов

Просто добавьте этот код в functions.php вашей темы:

function your_document_title($title) {
  $sep = ' | ';
  $array = [];
  $term = get_queried_object();

  global $page;

  if (is_singular()) {
    if ($page) {
      array_push($array, 'Часть '.intval($page));
    }
    array_push($array, get_the_title($term));
  }
  else if (is_paged()) {
    array_push($array, 'Страница ' . intval(get_query_var('paged')));
  }

 if (is_search()) {
    array_push($array, 'Результат поиска по запросу "'.esc_html($_GET['s'].'"'));
  }
  else if (is_category($term)) {
    array_push($array, 'Категория "' . $term->name . '"');
  }
  else if (is_tag($term)) {
    array_push($array, 'Метка "' . $term->name . '"');
  }

  array_push($array, get_bloginfo('name'));

  if (is_front_page() && !is_paged()) {
    array_push($array, get_bloginfo('description'));
  }

  return implode($sep, $array);
}
add_filter('document_title', 'your_document_title');

Эти строчки добавляют фильтр к методу document_title.
Для каждого заголовка страницы WordPress вызывает наш код следующим образом:

$title = your_document_title(default_title());

Правила такие:

  • для одиночных постов — «Название поста»
  • для страницы категории выводится — Категория «Название»
  • для страницы тега выводится — Метка «Название»
  • для главной страницы «Название сайта | Описание сайта»
  • для постов разбитых на части добавляется «Часть N» в начале
  • для страница поиска типа /s=QUERY «Результат поиска QUERY»
  • для страниц пагинации типа /page/N добавляется «Страница N» в начале
  • для всех страниц добавляется информация из поля Настройки->Общие->Название сайта
  • для всех страниц, кроме главной и страниц пагинаций, в конце добавляется информация из поля «Настройки->Общие->Краткое описание»

Добавляем мета тег canonical к каждой странице сайта на WordPress без плагинов

Всем привет! Чтобы добавить запись типа meta rel="canonical" href="url" к каждой странице вашего сайта на WordPress, добавьте этот код в functions.php вашей темы:

function remove_args($url) {
  $parsed = parse_url($url);

  $fragment = '';

  if (array_key_exists('fragment', $parsed)) {
    $fragment = '#' . $parsed['fragment'];
  }

  return sprintf("%s://%s%s%s",
    $parsed['scheme'],
    $parsed['host'],
    $parsed['path'] ?? '',
    $fragment);
}


function print_canonical_link() {
  $url = "";

  // Одиночная страница
  if (is_single()) {
    global $page;
    // Пост из нескольких частей
    if ($page) {
      $url = get_pagenum_link($page);
    }
    else {
      $url = get_permalink();
    }
  }
  // Страница пагинации типа что-то-там/page/N
  else if (is_paged()) {
    $url = get_pagenum_link(get_query_var('paged'));
  }
  // Главная страница сайта
  else if(is_front_page()) {
    $url = get_home_url();
  }
  // Страницы категории или тега
  else if(is_category() || is_tag()) {
    $url = get_term_link(get_queried_object());
  }

  if ($url) {
    echo '<link rel="canonical" href="'.remove_args($url).'" />';
  }
}
add_action('wp_head', 'print_canonical_link');

Подробности для непосвящённых: функция print_canonical_link() выводит запись вида
<meta rel="canonical" href="url" /> во время вызова штатной функции wp_head(), что очень уважают поисковые системы, но это неточно. А функция remove_args() удаляет GET аргументы вида «?key=value» из URL. Критика, замечания, пожелания приветствуются.

Загрузка разных счётчиков с помощью jQuery.getScript() без плагинов WordPress

Всем привет! Иногда бывает, что счётчики Google Analytics, Яндекс.Метрика и счётчик mail.ru тормозят загрузку основной страницы. Написал js скрипт для того, чтобы это избежать.

jQuery(document).ready(function($) {
    $.ajaxSetup({cache : true});

    // Данные для Google Analytics
    window.dataLayer = [];
    window.gtag = function() {
        window.dataLayer.push(arguments);
    }

    $.getScript(
        // !! ОБЯЗАТЕЛЬНО ЗАМЕНИТЕ ИДЕНТИФИКАТОР GOOGLE ANALYTICS "G-41BWRDZLG5" НА СВОЙ !!
        'https://www.googletagmanager.com/gtag/js?id=G-41BWRDZLG5',
        function() {
            gtag('js', new Date());
            gtag('config', 'G-41BWRDZLG5');
        }
    );

    // Данные для Яндекс.Метрики
    window.ym = function() { (window.ym.a||[]).push(arguments); }
    window.ym.l = 1*new Date();

    $.getScript(
        'https://mc.yandex.ru/metrika/tag.js',
        function() {
            // !! ОБЯЗАТЕЛЬНО ЗАМЕНИТЕ ИДЕНТИФИКАТОР ЯНДЕКС МЕТРИКИ "91531653" НА СВОЙ !!
            ym(91531653, "init", {
                    webvisor:true,
                    trackLinks:true,
                    clickmap:true,
                    accurateTrackBounce:false
                }
            );
        }
    );

    // Данные для top.mail.ru
    var _tmr = window._tmr || (window._tmr = []);
    _tmr.push({
        // !! ОБЯЗАТЕЛЬНО  ЗАМЕНИТЕ ИДЕНТИФИКАТОР MAIL.RU "2601331" НА СВОЙ !!
        id: "2601331",
        type: "pageView",
        start: (new Date()).getTime()
    });

    $.getScript('//top-fwz1.mail.ru/js/code.js');
});

Известная бага счётчика Яндекс.Метрика — замедление сайта, поэтому скачал его локально в папку [wordpress_folder]/metrika и добавил в задачу в crontab для обновления этого файла каждый час, Затем заменил https://mc.yandex.ru/metrika/tag.js в скрипте выше на /metrika_tag.js.

@hourly curl -s https://mc.yandex.ru/metrika/tag.js -o [wordpress_folder]/metrika_tag.js

Источник вдохновения статья Yandex Metrika: Сторонний код заблокировал основной поток. Не забудьте поменять идентификаторы счётчиков и проверить, что tag.js доступен по адресу [ваш сайт]/metrika/tag.js, если вы решили воспользоваться ускорением загрузки счётчика.

Чтобы добавить загрузку счётчиков в WordPress, необходимо положить его содержимое в файлик wordpress/wp-content/themes/[ваша_тема]/js/counters.js и добавить следующий код в wordpress/wp-content/themes/[ваша_тема]/functions.php:

functions.php (для всех пользователей)

function enqueue_counter() {
  // Регистрируем скрипт
  wp_register_script('counters', get_template_directory_uri() . '/js/counters.js', array('jquery'), false, true);
    
  // Загружаем скрипт
  wp_enqueue_script('counters');
}
add_action('wp_enqueue_scripts', 'enqueue_counters');

Я использую проверку, что пользователь не авторизован, зачем мне отслеживать самого себя:

functions.php (только для неавторизованных пользователей)

function enqueue_counter() {
  // Регистрируем скрипт
  wp_register_script('counters', get_template_directory_uri() . '/js/counters.js', array('jquery'), false, true);
    
  // Загружаем скрипт, если пользователь неавторизован
  if (!is_user_logged_in()) {
    wp_enqueue_script('counters');
  }
}
add_action('wp_enqueue_scripts', 'enqueue_counters');

Личный блог Евгения Жирнова