Search
Write a publication
Pull to refresh
37
0.4
Николай Меркин @nickolaym

User

Send message

тогда очень похоже - 1230, 1800, 1810 этих саженей = 2274, 3341, 3346 метров "приблизительно"

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

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

Думаете, от хорошей жизни там роботы с футболом?

А вот научить первую линию отличать рутинное от критического - это важная задача. И сделать это можно, к сожалению, исключительно через голову службы поддержки.

Поэтому хватит жеманничать. ИМЯ КОМПАНИИ - В СТУДИЮ. Иначе Яндекс будет думать, что вы ломились в Сбер, Сбер - что во Вкусно-И-Точку, а Вкусно-и-Точка - в шавермачечную на вокзале.

Конечно, если желание узнать состояние асинхронной задачи (запускаем - запустили - отменили - закончили с отменой - закончили без отмены) входит в ТЗ, то реализация несколько усложнится.

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

Поэтому - а насколько часто нам это в коде нужно? Может быть, "выстрелил и забыл" тоже подойдёт?

Меня не покидает чувство, что вы только что переизобрели std::promise/future/async. Да ещё и с довольно странным дизайном, в котором у вас есть явные владельцы и всё такое.

Имхо, надо было отталкиваться от описания задачи, а не от допиливания фич к существующему первичному коду. Причём замечу, что если с самого начала взять фьючерсы, то оперировать тредами вообще не придётся. (Они там будут под капотом).

Итак, хочется

  • асинхронную функцию, которая как-то сама может проверять флажок остановки

  • частный случай - функцию, которая делает это в цикле, собранном из условия и тела

Ну окей. Договоримся, что в первом случае это void(atomic_bool& /*stop*/)

Второй делается из первого нехитрым образом:

auto make_async_while_loop(auto precondition, auto body) {
  return [=](atomic_bool& stop) {
    while(!stop && precondition()) { body(); }
  };
}

auto make_async_do_while_loop(auto body, auto postcondition) {
  return [=](atomic_bool& stop) {
    while(!stop && postcondition(body())) {}
  }
}

Если body само по себе длинная операция - ну окей, переделать на body(stop)

Всё. Теперь нам нужна сущность, у которой есть две ручки: cancel и wait. Первая посылает команду остановки (мгновенно), вторая - ждёт завершения, какое бы оно ни было.

class MyAsync {
public:
  explicit MyAsync(auto async_fun):
    future_{
      std::async(
        std::launch::async,
        [this, async_fun=std::move(async_fun)]{ async_fun(stop_); }
      )
    }
  {}
  ~MyAsync() { cancel(); wait(); }
  void cancel() { stop_ = true; }
  void wait() { future_.wait(); }

  MyAsync(MyAsync&) = delete;  // поскольку лямбда связывает указатель

private:
  std::atomic_bool stop_;
  std::future<void> future_;
};

Конечно же, нам хотелось бы семантики перемещения хотя бы. Но для этого есть пимпл.

using MyUniqueAsyncPtr = std::unique_ptr<MyAsync>;
using MySharedAsyncPtr = std::shared_ptr<MyAsync>;
// в принципе, этого уже хватит

class MySharedAsync {
public:
  explicit MySharedAsync(auto async_fun):
    ptr_{std::make_shared<MyAsync>(std::move(async_fun))}
  {}
  void cancel() { ptr_->cancel(); }
  void wait() { ptr_->wait(); }
  // деструктор сам подождёт в деструкторе указуемого
private:
  MySharedAsyncPtr ptr_;
};

Я бы посоветовал сперва скомпилировать код, прежде чем вставлять его в статью... Потому что он с ошибками.

    bool StopFunc() // метод для её остановки
    {
        if (!FuncThread && !FuncStatus)
            return false;
        FuncStatus = false;
        if (FuncThread.joinable()) FuncThread.join();  // не . а ->
        if (FuncThread) delete FuncThread;  // delete не нуждается в проверке на nullptr 
        FuncThread = nullptr;
        return true;
    }
    bool FuncStatus() { return FuncStatus; } // два члена с одним именем

private:
    thread* FuncThread; // (лишняя косвенность)

предлагаете бизнесу размещать свои офисы у вас в датацентрах?

Я думаю, что надо открытым текстом назвать компанию, чья служба поддержки так беспомощно и НАОТШИБИСЬ прореагировала на сообщение о критической проблеме. Потому что это входит в систему. И пока менеджерам самого верхнего уровня не настучат по шапке за антипиар, которого они добились и заслужили, - на первой и второй линиях поддержки (а то и на нулевой, с этими чёртовыми роботами) ничего не изменится.

Подкину смежный пример.

Описуемые числа - это числа, которые можно (не обязательно уникально) задать некоторой конечной нотацией, - например, математической формулой или программой на любом языке программирования.

Например, конечные десятичные числа включают в себя множество целых и подмножество рациональных (среди рациональных есть бесконечные десятичные).

Десятичные числа со специальной нотацией ЦЕЛАЯ_ЧАСТЬ , КОНЕЧНАЯ_ДРОБЬ ( ПЕРИОД ) неуникально охватывают все рациональные числа. Неуникально - потому что периоды из девяток равны единице в предыдущий разряд и вырожденному периоду из нулей. То есть, 1,23(9) = 1,23(99) = 1,24.

Алгебраические числа - корни многочленов. Это не только рациональные, но и иррациональные числа. И как мы знаем, для многочленов 5 и более высоких степеней мы даже не всегда можем записать это в виде арифметических формул, дополненных знаком корня целой степени... Но тем не менее, эти числа (неуникально) определяются в виде "вот коэффициенты многочлена, а вот порядковый номер его корня". Если коэффициенты рациональные, номер натуральный, то кортежи таких чисел образуют счётное множество. Поэтому, хоть множество алгебраических чисел и шире, чем рациональных (включает их), но оно по-прежнему счётно.

Трансцедентные числа - то есть, не алгебраические. Вот тут дело обстоит интереснее. Да, очевидно, что вещественные числа - это континуум, а трансцедентные = вещественные \ алгебраические, континуум минус счётность = континуум. Пусть и всюду дырявый.

Однако, если мы попробуем описать любое трансцедентное число... ну, какие у нас есть средства? Именованные константы наподобие пи, е, гаммы там всякой. Именованные функции наподобие синуса, куда подставляем константы и числовые литералы. Безымянные ряды с формулой для члена. Интегралы-неберучки с формулой для подынтегрального выражения. И тому подобное.

А все ли трансцедентные числа можно так описать? А давайте посчитаем!

Пусть у нас есть некоторый язык формул. (И неважно, насколько он формальный - то есть, насколько мы где-то отдельно записали его синтаксис и семантику). Формулы конечной длины из символов конечного алфавита. Образуют множество строк... мощностью... ПРАВИЛЬНО! Счётное!!!

И вот что получилось. Описуемые числа - это счётное множество, которое включает в себя

  • целые (конечные цепочки цифр и знака)

  • рациональные (конечные цепочки цифр, знака, разделителей целой части и периода)

  • алгебраические (инструкции "реши-ка мне вот такое уровнение с многочленом")

  • некоторые трансцедентные (формулы произвольной громоздкости)

Но мы-то знаем, что всего вещественных чисел - континуум...

СЛЕДОВАТЕЛЬНО: существует континуум (везде дырявый) неописуемых чисел, - подмножество трансцедентных.

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

Тональ и Нагваль. Живите с этим, как хотите.

Единственное, на что похоже, чтобы было "примерно" - это ровно 1.8 морской мили. В футах или ярдах получается такая же 4-5-значная фигня.

Я хз, кто меряет глубину в морских милях, но от журналистов всего можно ожидать.

Откровенные идиоты - это какая возрастная категория?

Плохие аналогии подобны котёнку с дверцей. Куда пошлёт вас нанятый вами, ЖЭКом, сантехник, когда вы предложите ему купить себе весь инструмент самостоятельно, хоть ключи хоть плоскогубцы.

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

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

Смысл выбора пробы по наиболее равномерному распределению размеров классов - в том, чтобы как можно сильнее сузить пространство в самом худшем случае. И неважно, этот худший случай "не совпало ничего" или "совпало 3 из 5". А не выбирать такую пробу, у которой именно класс "не совпало ничего" самый большой.

В принципе, можно запустить моделирование. Взяли какую-то стратегию и применили её ко всем 100 адресам. Если вписались в ограничение в 10 попыток, - это дерево решений считаем удачным. Если для каких-то веток дерево слишком высокое, то находим узел, из которого растут высокие ветки, и делаем в нём другую пробу (следующую по предпочтению в нашей стратегии). И убеждаемся, разумеется, что классы эквивалентности там получаются другие, иначе смысла нет менять шило на мыло. Да, в худшем случае это NP-полная задача.

(немножко накосячил: результат пробы - это величина, противоположная расстоянию Хэмминга, то есть, "5" соответствует расстоянию 0, все компоненты равны)

Эммм... А почему бы не пойти таким путём.

Пусть у нас есть метрика d(a,b) - расстояние Хэмминга между двумя адресами-кортежами.

Изначально множество адресов S состоит из всех 100 штук.

Для каждого адреса a из S строим табличку: H[a][m] = {b : d(a,b) = m}

Выбираем такой адрес a1, для которого разбиение на классы эквивалентности по расстоянию до него наиболее равномерно. (Ну понятно, что для любого a, H[a][5] = {a}, поэтому этот класс мы учитывать не будем).

Делаем пробу a1, получаем расстояние m1, получаем подмножество S1 = H[a1][m].

Для каждого адреса из S (не из S1) снова строим табличку H1[a][m] = {b in S1 : d(a,b) = m}.
Проще говоря, оставляем в классах в H только те, что лежат в S1. Ключами по-прежнему являются все адреса.

Опять же, выбираем такой a2, для которого разбиение на классы в H1 наиболее равномерно.

Делаем пробу a2, и так далее.

Неправда. Давайте построим такой же контрпример!

Пусть у нас первые четыре компоненты - по 10 уникальных значений, а пятая - все 100 (сквозная нумерация всех адресов).
Четыре компоненты задают 4-значные десятичные числа, то есть, там возможна 1000 вариантов. Но чисел всего 100 штук.

Любой запрос даст нам либо ответ "5" (полное совпадение), либо - от 0 до 3. Ответ "4" не может быть, так как это означало бы, что либо пятая компонента совпала, а какая-то другая нет, либо четыре первых совпали, а пятая нет, то есть, сломана сквозная нумерация.

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

Предположим, что "угадай 5-значный адрес (строго десятичный) за 10 попыток" - заведомо решаемая задача.
В таком случае берём наши 4-значные адреса и добавляем к ним хаотично пятые цифры. Мы свели задачу к известной. Следовательно, "угадай 4-значный адрес" также заведомо решаемая.

Следовательно, существуют наборы адресов со 100 уникальными квартирами, для которых решение всё ещё есть.

  • Если адреса с точностью до дома уникальны - решение (видимо) есть

  • Если адрес с точностью до дома одинаков для всех - решения точно нет

  • Эти две границы можно, наверное, ещё как-то сузить.

В статье вся вёрстка кода люто яростно поехала! Что это за pythonКопироватьРедактироватьold_events и т.п.?

Если вы кодите так же, как статьи пишете, то... ну ээээ... ожидаемо.

я в детстве крону попробовал - думал, будет как две планеты, а оказалось, как собака за язык укусила!

1
23 ...

Information

Rating
4,275-th
Location
Санкт-Петербург, Санкт-Петербург и область, Россия
Registered
Activity