Pull to refresh
4
0

Пользователь

Send message

Тот же докер, чтобы поставить на убунту, нужно пройти небольшой квест

Вы же в курсе, что докер есть в официальном репозитории вашего дистрибутива и ставится единственной командой, не требуя ничего из перечисленного по вашей ссылке?

apt install docker.io

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

Я тоже люблю винду похейтить, однако... У меня винда ставится за несколько минут. Да, это не минута, и nvme у меня в ноуте по-быстрее будет, но, простите... А средний юзер действительно заметит оптимизацию скорости установки винды с пяти минут до одной, пока заваривает чай? Я уж молчу, что большинство юзеров будут ставить себе винду примерно ноль раз за всю жизнь

И действительно ли этот результат стоит потенциальных неприятных последствий переписывания логики установщика?

Ну да, ну да, кто-то пишет if(!result) вместо if (result != http::ok), чтоб потом узнать, что http::ok внезапно 200, а не 0. Не надо так

А вы http-коды храните в интах? Тогда у вас более серьёзные проблемы, чем обсуждаемая выше, до которой ещё дорасти нужно

Но вот чем принципиально для вас отличается if (opt.has_value()) от if (vec.isEmpty())?

Тем, что одно из условий положительно, а другое негативно. Главное, что оба выражены прямо так, как подразумевал программист. А вот стоит только взять if (!vec.isEmpty()) — здесь уже проблемы, описанные мной выше. Имеем ввиду наличие элементов, спрашиваем про отсутствие

Любой из двух следующих вариантов я считаю предпочтительнее:

auto has_element = !vec.empty();
if (has_element) /* ... */;
if (vec.count()) /* ... */;

Ещё лучше было бы, предоставляй стандартные контейнеры сразу оба — и empty, и has_value

Можно ещё сравнить std::optional и std::any

На самом деле нельзя. У них семантика разная, std::optional семантически предполагает отсутствие значения, это фича для конечных пользователей. А для std::any (как и std::variant) это вынужденная фича, необходимая для поддержания мув-семантики, а не для пользователей. Т.е. не предполагается, что пользователь будет пользоваться has_value/valueless_by_exception как нормальным интерфейсом, ему это в норме и не нужно. А соответственно и неявный каст не нужен — это не часть нормального повседневного интерфейса, так что ответ на вопрос...

Вас не смущает использовать разные способы проверки одного и того же?

Однозначно нет. Пользователь для std::any в норме не пользуется этим интерфейсом. Более того, в архитектуре даже худших среди кодовых баз, с которыми я работал, ещё не было столь серьёзных проблем, чтобы мне нужно было использовать "такой" std::any

А там, где он использовался не как архитектурный костыль — там у меня была гарантия не только на конкретный тип в нём, и на то, что он точно всегда имеет значение

Одинаково плохо читается и это:

if (страна.город.мэрия.зачем.так.много.вложенности.удалена == false)

и это:

if (not страна.город.мэрия.зачем.так.много.вложенности.удалена)


Если в первом случае отрицание далеко от непосредственного контекста, то во втором оно не на том месте и отделено от него синтаксическим шумом. А теперь просто сравните с

if (страна.город.мэрия.зачем.так.много.вложенности.неУдалена())

Может, настало время добавить немного выразительности в интерфейсы своих типов?

Как альтернатива, когда типы трогать не хочется, воспользуйтесь древней технологией — переменной:

auto&& foo = страна.город.мэрия.зачем.так.много.вложенности; 
if (not foo.удалена())

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

Казалось бы, к чему я вообще о какой-то иерархии в этом вопросе и где здесь иерархия? Всё просто — рано или поздно мозг программиста начнёт любой if (something) обобщает до до некоторой общей абстракции. Не думаю, что у неё есть название, но я бы её выразил как if ok/if not ok. Когда мозг их обобщил, становится очень просто читать такой код. И дело не в его длине, а в том, что все подобные конструкции мозг начинает маппить на то самое внутреннее обобщение

Но при этом, когда вы местами используете if (opt.has_value()), местами if (ptr != nullptr), где-то if (result != http::ok), где-то ещё что-то... Мозг просто хуже это делает — вместо одного обобщения он работает лишь с частными случаями всюду. А исключения и частные случаи мозг не очень любит

Но почему мозг точно так же не может обобщить разные примеры выше?

Потому что ещё был упомянут альтернативный вариант проверки, if (opt != std::nullopt). И это просто ужасный, кошмарнейший пример, связанный уже не с длиной кода. Эта строчка подразумевает, что внутри ветки мы работаем с happy path. И автор имеет ввиду положительный путь исполнения, и код пишет под положительную ветку исполнения. Но в условии при этом он спрашивает буквально "не является ли путь негативным". Изначальное семантически чисто положительное условие превратилось аж в две инверсии. Имеем ввиду положительное — спрашиваем дважды негативное. И, хотя формально две инверсии не меняют семантику на негативную, мозг, видя инверсии и негативизацию в условии... считает их чётность... и считает крайне плохо, и сбоит. Видит отрицания — думает про unhappy path. В первую очередь, конечно. Во вторую таки считает чётность, откатывает в своей ментальной модели то, что навыдумывать успел... Много избыточной ментальной работы, много кеш-миссов в голове

И это ещё не всё. Теперь комбинируем все перечисленные проблемы вместе и получаем ещё одну: if (opt != std::nullopt) и if (opt.has_value()) выражают одну мысль, но совершенно разным языком, который не обобщить до одной понятной концепции в своей ментальной модели. И вместо того, чтобы читать произвольную ветку как if (ok), ваш мозг будет вынужден каждый раз дополнительно разбираться, что значит второй аргумент в сравнении, к какому "виду" сравнений оно относится, считать чётность инверсий и перепроверять себя, забивая ментальный контекст этой мусорной работой и утомляя вас

Чем больше вы утомляетесь, тем больше ошибок совершите, разных и в разных местах

Какой вывод? Выражайте мысли прямо, чтобы ошибаться меньше. И if (something) — самый простой способ выразить мысль прямо. Автор будет меньше раздумывать, каким именно правильным способом это написать (правильный останется единственным, если вписать в стайлгайд и распространить на всю кодовую базу), а значит и с меньшей вероятностью совершит ошибку из-за семантического насыщения. А пользователь тоже будет читать привычный паттерн на автомате, снижая риски запутаться при виде всех этих таких похожих визуально, но таких семантически разных != nullpt, != http::ok, != nullopt. Ой, а в средний закралась ошибка. А вы её заметили?

У него достаточно взаимопротиворечивых проверок, так что апелляция к нему сомнительна

В таком варианте при прочтении кода лучше видны не стыковки.

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

if (SomeGlobalPointer == nullptr)
{
  delete[] SomeGlobalPointer;
}

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

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

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

В нашей ментальной модели теперь сущностей, от которых зависит исполнение тела, стало в два раза больше — две. Также и возможное число исходов (где-то в фоне считаемое мозгом) увеличилось до четырёх — SomeGlobalPointer, не SomeGlobalPointer, nullptr, не nullptr

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

Несколько ноутбуков, среди которых я выбирал несколько лет назад, были дороже в комплектации "без ОС", чем с Windows. Так что чисто технически, Windows зачастую имеет отрицательную стоимость для конечного потребителя

Думаю, вы можете попробовать запустить на нём waydroid

TL;DR: пайп позволяет визуально отделить вход от выхода и совершать меньше действий в процессе правки команды

В своём рассуждении, вы ловко выкинули из примеров команд pattern:

grep pattern file
cat file | grep pattern

А ведь чаще всего вы будете нажимать стрелку вверх и менять именно паттерн (или даже весь grep) при том, что имя файла будет оставаться фиксированным. Но каждый раз, нажимая стрелку вверх, ваш курсор будет оказываться в конце строки, т.е. рядом с file, и вам каждый раз нужно будет перепрыгивать его, чтобы добраться до паттерна, который будет за сессию работы меняться куда чаще. Лично мне лишние прыжки через имя файла удовольствия не доставляют, поэтому я сознательно использую cat. Вы, конечно, можете опротестовать и предложить мне более эффективную (с точки зрения машины) альтернативу:

< file grep pattern

Но. Стрелку легко перепутать, уничтожив файл. Во-первых, стрелки рядом на клавиатуре; во-вторых, даже будь они далеко — перепутать их всё ещё можно. Ещё здесь нет явного и заметного разделителя, который было бы удобно искать глазами — имя файла миксуется с командой; в случае с cat такой разделитель есть — пайп. Да и просто же выглядит довольно уродливо

Так я и проверил — сразу привёл релевантную ссылку с меткой wontfix в своём исходном сообщении, более релевантных страниц в выдаче не вижу

А что значит -march=native в контексте wasm? Кажется, даже разработчики в некотором недоумении

А в чём проблема? Если операционки — это линуксы, а даты их релизов не отличаются на года, то держать под системой контроля версий единый набор конфигов для них — это обычно не проблема. Ну, допустим, будет ли вам невыносимо плохо от осознания того факта, что у вас среди конфигов лежит что-то под неустановленную программу?

Другое дело, что я не выбрал VCS по одной простой причине. Переезжая, я не люблю тащить за собой всё окружение, включая дефолты, которые могут отличаться между версиями софта или разными дистрибутивами. Интереснее переносить лишь тот его сабсет конфигов, который настраивался сознательно и здесь автоматизация выглядит намного удобнее

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

Исходный код steamcmd, кажется, открыт. Вы вполне можете написать свой клиент или воспользоваться чужими наработками

Всё просто, достаточно лишь перед включением вашего кода добавить пару строк, чтобы он сам успешно собирался:

#include <iostream>
using std::cout;


А в конце запустить вывод Гав в теле функции main:

int main() {
    cout << "Гав";
}

Но я бы настаивал на игнорировании файла sobaka.h билдсистемой, это бы упростило решение задачи

Я тоже сначала использовал fdupes, пока не понадобился поиск уникальных файлов... Тогда я узнал о jdupes --isolate --print-unique --recurse. Когда и в нём сломалась эта фича, я перешёл на rmlint и пользовался им до тех пор, пока не обнаружил, что он непредсказуемо сбоит на специфической fuse файловой системе под одним конкретным устройством (что чуть не стоило мне потери данных)

В общем, есть основания относиться с максимальным подозрением к таким инструментам и не полагаться на них, а использовать вместо этого самописную портянку с применением find/sha512sum/sort/join, которые пока ещё, к счастью, не провинились передо мной

Есть софт, который был написан 20 лет назад долго использовался и по закону еще лет 20 должен работать

А ещё можно издать закон, чтобы все жили по 100 лет и счастливо, странно, что никто пока не догадался ¯\_(ツ)_/¯

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

Если бы такие разработки попали в open source хотя бы частично, это было бы огромным поводом к уважению продукта и, вероятно, банков, которые вложились бы в сообщество

Меня очень радует итоговый отказ от нативных приложений в пользу веб-версий (пусть и вынужденный), но, как пользователя, всё ещё удручает закрытость решения

Information

Rating
Does not participate
Registered
Activity