Pull to refresh

Comments 27

Это что за производство, в котором оверхед в 3 раза от нормального алгоритма приемлем? производство ханойских башен? :)
Ну вот как-то на той планете, где я живу это далеко не самый страшный оверхед. Вы ж понимаете, что просто щелкая какой-нибудь DoubleBuffering в WinForms Вы мгновенно получаете двойной оверхед. Вот когда кто-нибудь невзначай забабахивает квадратичный (а то и кубичный) алгоритм вместо линейного — это да, жесть. И то живет порой в релизе до поры до времени.

Вообще-то надо аппроксимировать финт "Два кольца на соседних штырях – а они там поместятся?" до финта:


А что будет, если радиус кольца больше суммы диаметров 2 штырей и расстояний между ними, а 3 штырь очень сильно отстает от первых двух и нам надо переместить кольцо на 3 штырь?

Тогда ни решение А ни В ни С не дадут верного решения. И только ваш этот "оверхед в 3 раза", над которым вы похихикали, сделает все так, как задумано.


Кстати, решение А является подмножеством решения на прозводстве, ибо смысл решения А в восстановлении картины без колец вообще.

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

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

Честно скажу, сначала я начал говорить вариант B, но вовремя изменил его на A.
Было бы интересно узнать почему (интересует также мнение людей с вариантом С).
Как раз по тем причинам, что описаны в статье.
Добавляем в последний вариант виртуальную башню и получаем реакт.
Как видим, и здесь можно описать предполагаемое промежуточное состояние. И это описание должно мгновенно зажечь тревожный сигнал для разработчика – по условию задачи у нас есть только одно кольцо, не два.

Но и не ни одного! Так что на мой взгляд решения А и Б правильны и логичны. Просто в Б нам важна сохранность данных. Поэтому мы сначала копируем кольцо, а лишь потом удаляем исходное.
А Вы возьмите три башенки и руками колечко переставьте. В какой-то момент на башенках не будет ни одного, правда?
увы, но программа это далеко не точное отображение реальности, очень далеко.
Руками мне к примеру вообще не придется столбик домалевывать.

Я лишь хочу сказать, что правильный ответ зависит от конкретной ситуации и наших потребностей, поэтому нельзя однозначно утверждать А>Б

План B предполагает некую “виртуальную реальность”, непротиворечивость которой требует аккуратного анализа. Два кольца на соседних штырях – а они там поместятся? После такого вопроса провальный тест для программы B становится очевидным:

А что если для меня так же очевиден провал А. Мы передвигаем столбики примерно так

__|__ | |

Теперь после первых двух шагов А у нас нет кольца, а третий мы не можем выполнить, потому что столбик 3 мешает. В итоге остаемся ни с чем (ну или с бекапами). В варианте Б кольцо останется на столбике, мы ничего не теряем. Но опять же, это додумывание условий. И додумать можно и до А, и до Б.
В данном случае речь все-таки идет о рисовании, а не о обработке данных. Нарисовать заданную картинку заново можно всегда, бэкапы тут не нужны.
увы, но программа это далеко не точное отображение реальности

Конечно, нет. И вообще программа и реальность — вещи, слава богу, разные пока. Речь была о том, что придумывание отображения программы в реальность помогает оценить непротиворечивость объектной модели, поскольку непротиворечивоть реальности тогда за нас. Я б сказал, что это такой brain design pattern — concept reuse: мы придумываем привязку к тому, про что уже известно, что оно работает, и тем самым экономим ресурсы мозга (не тратя их на всесторонний анализ чего-то совершенно нового).

При этом да, вариантов проекции в реальность может быть более одного. По поводу Вашего варианта (в пределах этой игрушечной задачи) я имею такие сомнения:
1. Ваш test case демонстрирует задачу, которая в принципе не имеет решения (как выглядит финальная картинка?). Кейс, о котором шла речь в статье, имеет решение — финальная картинка понятна, просто надо не испортить ее по дороге.
2. Ваша проекция — это не проекция в реальность, это проекция в другой программистский опыт (что за бэкап на реальном объекте, типа деревянного кружочка?).
3. Если уж говорить про бэкап, использование одного и того же места и для бэкапа и для работы — вряд ли хорошая практика (см. первую букву в слове SOLID).
полностью согласен с первым абзацем. Пример как по мне чуть не подошел, потому что в статье про рисовать, а в первом ответе Вы уже просите передвигать. И пойди пойми, что здесь важно а что художественное описание.

насчет сомнений
1. расстояние между столбиками должен гарантировать к примеру другой юнит тест. Цель была показать, что после «выброса исключения» отсутствие кольца не всегда более желаемое состояние, чем дубликат.
2. так и задумывалось. Первая отсылка к реальным объектам (производство) идет уже после самой задачи, потому я не брал ее за дано.
3. про место я не сказал ни слова.
Программисты, решившие с пару задачек на атомарные транзакции, например что-нибудь вроде exception-safe операции добавления элемента в конец непрерывного массива (с++ std::vector::push_back) могут почувствовать себя Буридановым ослом, выбирая между А и Б.

Мотивация: нефиг трогать 1 столбик до того, как 2 столбик получит кольцо. Связано с тем, что это упрощает откат к последнему корректному состоянию. В нашем случае, если перемещение было прервано исключением линейкой-по-рукам, то можно ойкнуть и сразу убежать — башенки останутся в корректном состоянии.

Просто в Б нам важна сохранность данных
Интересно. Транзакция? Что если произойдет сбой после отрисовки кольца и до того, как исходное удалено? На экране будет 2 кольца. Вы об этом не подумали?

Вообще я заметил, что нам (программистам) свойственно «додумывать» различные условия и крайности. Это хорошо видно на stackoverflow, когда плохо сформулированные вопросы неожиданно получают несколько ответов, решающих разные проблемы.

В вашем случае, выбрав Б, вы «додумываете» некую «сохранностить данных». В моем случае, выбирая А (см. мой комментарий ниже), я «додумываю» некую синхронность отрисовки, типа кольцо сначала должно быть убрано, а уже потом добавлено.
Вот Вы сами додумали какую-то транзакцию. Там чуточку другой калибр.

Самый простой пример: я переношу через сеть объект(кольцо) из одной бд(штыря) в другую.
Отослав пакет я не буду ничего удалять пока не придет подтверждение. В худшем случае у меня будет дубликат в базе. (надеюсь это не окажется база транзакций :D) И тогда лучше пусть так, чем ничего.

Разумеется пример надуманный, ровно как и пример кольцами и все остальные.

Собственно мою мысль Вы сами же сказали в последнем абзаце, кто куда додумает, тот туда и придет.
Насчет «тупого» кода правильно сказано. Очень часто не всегда можно оценить все недостатки алгоритма, поэтому часто изначально пишу самый тупой код, добиваюсь его четкой работы и только после этого перехожу к оптимизации с учетом выявленных проблем.
Ага. По-моему у Дейстры в «Дисциплине программирования» этот принцип сформулирован — сначала сделаем программу работающей, а уж потом работающей быстро.
Либо я неправильный, либо лыжи не едут. Попробовав представить, что выйдет в результате варианта А и получил, что мы:
1) стираем середину первого штыря (первое-то кольцо на штыре 1 никуда не девается);
2) рисуем новый штырь поверх двух разноцветных колец на 1 штыре, и при этом получается, что у нас штырь как бы огибает первое серое кольцо;
3) надеваем на 2 штырь кольцо.
Не середину стираем, а нижнюю часть. В задаче под «кольцом» понимается прямоугольная область изображения, находящаяся в фиксированном месте, не зависящем от текущего состояния картинки.

Хорошее вступление… Последнее предложение — кто это «мы»? Я? Я вообще не понял смысла. Вы ввели определенные ограничения в «вашу игру» — даны 2 команды и поставили задачу с помощью их нарисовать что-то. Круто! И?

«Сильный» программист просто знает правильный порядок, допустим, если бы были задержки при синхронной отрисовке или анимации, то правильный порядок был бы «убрать кольцо с 1» (требует 2 команды, из-за того, что прямоугольники накладываются друг на друга оптимальный порядок единственный) и «добавить кольцо к 2», т.к. тогда кольцо сначало было бы убрано, а потом надето, как в реальности. Несмотря на то, что вы добавили условие «нет анимаций» именно этот путь наиболее «логичный».

Последняя программа тоже логична, она просто сперва очищает экран, а потом рисует то состояние, которое нужно. Это может быть более оптимальный путь, если ввести несколько уровней абстракции, к примеру, добавить очередь/список команд, при изменение которого потребуется перерисовка, перед которой экран всегда будет очищаеться. Повысится читаемость, модифицируемость и поддерживаемость программы за счет производительности. Что лучше — зависит…

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

Мне кажется, в статье хорошо освещена проблема (мне сложно представить мысли человека, выбравшего В или С). А вот анализа и решения (что с этим делать) — увы нет.
Спасибо за детальную рецензию. Возможно, и вправду стоит написать более подробную мораль в конце этой истории, просто опасаюсь переборщить — статья и так-то вышла довольно морализаторская. Если коротко:

«Мы» (по отношению к тупым программам) — это, допустим, тимлиды, которых задолбала демонстрация изобретательности, ведущая к нетривиальным багам. А ежели не тимлиды, то буквально мы все, на которых эти баги потом высыпаются в виде неработающего чего угодно (ибо внутри этого чего угодно в наше время наверняка сидит какая-нибудь такая программа).
«Сильный» программист просто знает правильный порядок…
мне сложно представить мысли человека, выбравшего В или С

Все так и есть. Но тот факт, что мысли для случаев В, С представить сложно не спасает от появления таких программ. То есть то, что этому сильному «просто», другому, оказывается, совсем непросто (даже в таком, казалось бы, игрушечном случае). Вопрос как раз в том, можно ли это вот «просто» как-то вербализовать и поделиться с теми, кому непросто? Ну вот ровно это я и попытался сделать.
«Сильный» программист, наверное, заметит, что команды X(номер, цвет) для разных значений параметра «номер» коммутативны (могут выполняться в произвольном порядке), а для одинаковых — нет. Это наблюдение сразу дает все три варианта A, B и C. Ну и по завету Дейкстры («детерминизм — частный случай недетерминизма»), напишет параллельную программу, для которой ABC являются сериализациями.
Сомневаюсь, чтоб сильный программист такое заметил — просто потому, что это неверно (когда прямоугольники для колец перекрываются, см пример В), о чем частично и был спич.
Sign up to leave a comment.

Articles