Pull to refresh
4
0
Send message
Я регулярно думаю о частной проблеме — человеческий, но формализованный язык для общения с самим собой (ладно с ним, с остальным человечеством). Переносимость идей, алгоритмов через моды / синтакс разных ЯП / изменения в синтаксе даже одного ЯП (мой ЯП — C++, он стал динамичнее меняться в последние лет 15). Идеи AOP, DSL мне лично нравятся и для меня лично они адекватны проблеме. Далее, я — довольно типичный «опытный программист со стажем», адекватность для меня что-то значит и для остального человечества. Это я просто в антитезу вашему (понятному мне) скепсису.
Подумайте над идеями, как это исправить «правильными» системами поиска по исходному коду. Которые завоюют мир, а вы, их автор, будете сами себе платить зарплату. Шутка. Я вижу (поверхностно, конечно) ваш психологический профиль, это не ваш метод. Ваш метод — сбросить пар на хабре, продолжить комфортно получать зарплату, где получаете. Я то же так делаю, но не сбрасываю пар на хабре.
Гипотеза: дело в том, что первые пользователи C++ были C программисты, экономили байты исходного кода (кроме того, замечательная более поздняя идея делать this. (this-> на C++, мне лично сама -> противна из-за nullptr dereferencing) была придумана более поздно, в языке Java. Так сложилось, короче. C++ более старый, логичнее задуматься о том, почему и чем идея с this. (Java, C#), увеличивающая многословность, количество ударов пальцами по клавиатуре, правильная и полезная. Я не подвергаю эту идею сомнению, хорошая идея, но не универсальная. Есть другие идеи, точки зрения, инструменты проверки соответствия кода оным.
Удивительно, как много созидательной энергии, увы, тратится на форматирование, соглашения о наименованиях, такие мелкие штуки, которые Компьютер должен делать (и делает) проще для мыслителей-программистов. Однако, у мыслителей-программистов есть некоторая проблема с приоретизацией, реальная проблема — человеческий мозг хватается за мелкие штуки первым делом, ему проще выполнить рутинную работу по переформатированию, переименованию, расставлению скобочек и запятых красивенько так — и это на подсознательном уровне. При том, что Компьютер сделает всё это ОК за миллисекунды.

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

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

Пример реализации: автор(ы) языка Nim забил(и) (в основном) на различия CamelCase snake_case nocase bAdCaSe в наименованиях. Хороший пример послать паразитную проблему подальше, не идеальный (в идеале в проекте есть спеллчекер в идентификаторах и правильная конверсия в CamelCase, snake_case, nocase по настройкам индивидуального разработчика — плюс строгое выпалывание bAdCaSe, конечно).

Слабое место — поиск, но это технически решаемая проблема. Интересный комментарий выше — про полезность минимального Hungarian вроде "_" или «m_» префиксов, теперь уже из-за удобств IDE. С чего я начну новую строку моего идеального кода? Первый элемент намеренья обозначен довольно рудиментарным префиксом, а там уже — любимая мозгом рутинка, как в компьютерную игрушку играешь. Все довольны (главное, мозг :) ).
Спасибо, интересно, любопытно — чисто интуитивно, другой джиттер работает, наверно.
Вы не могли бы поделиться ссылкой про это (очень любопытно, но беглым поиском пока не нашёл)?
«Пока я не знаю, что с этим делать» — по-моему, описанные вами инструменты могут быть полезными для создания учебных материалов. Вспомнился сайт explorabl.es («Explorable Explanations») — я довольно бегло его просматривал, но, как мне помнится, некоторые представленные там инструменты для создания интерактивных обучающих материалов используют модель блокнота. У меня с работой пересечений мало (я программист), не освоил, но любопытно из-за связи с любительской педагогикой (математические и программистские кружки для школьников). Спасибо за статью!
Не смотрели ли вы на то, насколько сложно дополнить парсер (в выбранном вами стеке) реализацией базового IDE-плагина (опять же, соответствующего вашему стеку)? Например, если вы используете IDE, поддерживающую Language Server Protocol (LSP), реализация LSP в каком-то объёме может улучшить usability вашего DSL.
Предложение по стилистической коррекции фрагмента «Обслуживание многоразовой ступени заняло всего 51 день. Это больше предыдущего рекорда почти на две недели.» «Больше» здесь неправильно, правильно «Это лучше предыдущего рекорда почти на две недели» (или «короче» — поскольку длительность обслуживания сократилась на две недели, не увеличилась).
К примеру, как раз взгляд на математику как на дисциплину, оперирующую логическими построениями, сформировался в относительно недавнее историческое время (меньше двух веков) в результате… «смены научных парадигм»? Вы можете спорить, что это не было «сменой научных парадигм», предлагать другой термин — тем не менее, процессы, похожие на смены научных парадигм (лучше всего иллюстрируемые примерами из других наук) происходили и происходят и в математике тоже. Приведу небольшой частный пример несостоявшейся смены научных парадигм в математическом анализе (ну или по крайней мере в преподавании математического анализа) — так называемый «нестандартный анализ». Он предлагает использование расширенной числовой прямой, в которой бесконечно малые величины — реальны (а не являются языковой конструкцией, скрывающей довольно громоздкий дельта / эпсилон аппарат «стандартного» анализа). Набор доказываемых фактов такая смена аксиоматики не меняет, но доказательства и преобразования становятся значительно легче и понятнее. Не прижился (критики внедрения этого подхода в преподавании взяли верх, насколько я понял), но влияние оказал (в частности, прояснил вопрос, как математикам прошлых веков — до появления строгого логического базиса стандартного анализа — могли добиваться верных результатов, оперируя мутными понятиями вроде «бесконечно малые величины», и не надоказывать ложных теорем).
Извините, решил написать реплику, поскольку ваш оппонент не ответил. В студенческие годы я интересовался темой альтернативных аксиоматик, например, есть разделы математики, изучающие последствия удаления или замены некоторых «стандартных» аксиом, например, аксиомы выбора в аксиоматике теории множеств, или логического закона исключения третьего. Так вот, в моём понимании собственно изобретение неевклидовых геометрий в XIX веке было существенным, но только фрагментом «смены парадигм» в математике, растянувшейся на многие десятилетия; о «революции» можно наверно говорить только в применении к конкретному разделу математики, геометрии. Лобачевский и другие авторы неевклидовых геометрий дали неожиданные ответ на трудный вопрос в геометрии: действительно ли постулат о единственности линии, параллельной данной и проходящей через данную точку, является аксиомой? Геометры прошлого строили доказательство этого постулата (пятого постулата «Начал» Евклида), базирующееся на остальных аксиомах геометрии (таким образом низводя его статус от аксиомы к теореме), но позднее в этих доказательствах обнаруживали ошибки. Работы Лобачевского (и других авторов) продемонстрировали независимость пятого постулата от других аксиом — посредством построения моделей альтернативных аксиоматик, в которых пятый постулат заменен одним или другим противоречащим ему утверждением (или «несколько», как написано у вас — гиперболические геометрии — или «ни одной» — эллиптические геометрии).

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

Кстати, вспомнил переводную книгу, изданную в СССР где-то в середине 80-х, «Математика: утрата определённости» Мориса Клайна, сейчас поищу — о, про неё даже заметка в Википедии: ru.wikipedia.org/wiki/%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0._%D0%A3%D1%82%D1%80%D0%B0%D1%82%D0%B0_%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D1%91%D0%BD%D0%BD%D0%BE%D1%81%D1%82%D0%B8 (интересная книга, насколько я помню).
Вы правы насчёт 4-х: в этой схеме испытаний частота события и среднее время ожидания события — величины взаимно обратные (1/4 и 4). Как это проверить? Можно повозиться с простой алгеброй (считать суммы арифметических прогрессий), но можно поступить проще. Давайте назовём искомую величину — среднее количество проверок, включая последнюю, успешную проверку — X. При первой проверке с вероятностью 1/4 мы имеем количество проверок 1 (удача при первой попытке!) и с оставшейся вероятностью 3/4 мы имеем количество проверок 1 (неудача при первой попытке) плюс X (ситуация после первой попытки ровно такая же!):

X = 1/4 * 1 + 3/4 * (1 + X)

1/4 * X = 1

X = 4
Спасибо, было интересно почитать. Вам интересно было бы немного поитерировать над получившимся кодом? В первую очередь я бы предложил избавиться от C-style вызовов new и delete, используя, скажем, std::vector вместо C массивов и std::owning_ptr вместо C указателей на Node. Объём вашего кода сократится: например, вам не нужно будет кодировать HashTable::~HashTable, компилятор прекрасно справится сам с этой задачей (HashTable::HashTable тоже сократится благодаря дефолтовому конструктору std::owning_ptr, вызовы которого сгенерирует компилятор). Аналогично, сократятся Resize и Rehash, каждый на несколько строк в конце (автоматический вызов деструктора локального std::vector-а, свопнутого с основным, позаботится об освобождении памяти). Код будет не только короче, но и дешевле в долговременной поддержке. Производительность не должна измениться.

Говоря о производиетльности — имеет смысл поднять служебные поля на уровень основного массива, чтобы для обращения за ними не надо было бы разыменовывать указатель. Правда, при этом увеличится расход памяти. Мне было бы любопытно рассмотреть последствия в конкретных числах, а вам?
Скажите, почему вы объявляете Decorator как struct, в то время как фактически это namespace? Мне такие struct-ы попадаются в старом коде, вероятно, написанном до появления namespace (сейчас попробовал найти, когда это произошло, и не нашёл — как будто фича namespace в C++ была всегда). Или вы планируете добавить не-static содержимое, или защищаете Decorator от сторонних добавлений?
Не уверен, каким образом поправкой перевода спровоцировал ваши претензии к автору оригинала именно в этой ветке обсуждения перевода. По сути — она не ставила целью написать внятную статью об авиастроительном бизнесе на почтенный сайт habr.com; напишите в The New Republic или Maureen Tkacik напрямую.
Спасибо за перевод, прочитал с интересом.

«Если обнаружите в публикации трудности перевода или оформления, дайте знать.»

Вот здесь ошибка в переводе: "… в результате которого погибли его жена, сын, четырехлетняя дочь, девятимесячная дочь и свекровь" — тёща, не свекровь.
In my current projects I am inclined to not follow the last recommendation ("Never dereference or call a method of non-local shared_ptr, because this puts object outside of shared_ptr control (instead, first make a local copy of shared_ptr).").

That recommendation does not blend well with preceding ones that are not that over-protective or super-defensive. Let it crash, so to speak.

Your 2-part article is very good, I'll be glad to see more!
Regarding using English — sure why not (I cannot update the initial comment but I'll retell the core concern in brief below). Thanks for the links you provided in the post, I will trace details when I have a better chance (over the weekend, maybe). Thank you for listing arguments for not passing smart pointer arguments in favor of passing & references (stating explicitly that the contract guarantees that the reference is not null always) or * pointers (stating explicitly that the contract allows nullptr reference on some occasions so the callee has to handle that possibility in a meaningful way). I've read Core Guidelines and I agree, but a consideration linked to the last recommendation (see below) leaves a residue so to speak.

Regarding my core concern on the last section of your post: the last recommendation of that section kind of introduces a complication twist to «prefer using &, * and not smart pointer argument types» rule. Let' get back to my example: I am implementing a class that has a member variable shared_ptr<widget> m_p. It is guaranteed to be not null at a moment and I need to call an external function named «use» that takes widget&. Being a thorough defensive programmer (as the last recommendation teaches me) I have to do the following (now switching to auto):

auto pStabilized = m_p;
use(*pStabilized);

— because from a formal point of view I have no control over what other functions «use» will call. Note that in case of 5 reference arguments of «use» the caller will have 6 lines of code introducing 5 local variables that are used in one line of code — the «use» call. Well, it takes an effort to be a thorough defensive programmer (and never mind the impact on code readability / maintainability). Now imagine that I am not only doing that in my code but also insist on this pattern while doing code reviews…

Do you agree that this is the right conclusion when all recommendations of the last section are taken into account? If you have a better idea on how to reconcile all recommendations from the last section, please share! I'll do some experiments most likely, but not earlier than over the next weekend.
Большое спасибо, Алексей!

Хотел бы обсудить последний пункт последнего раздела «Never dereference or call a method of non-local shared_ptr, because this puts object outside of shared_ptr control (instead, first make a local copy of shared_ptr)» в сочетании с "// global (static or heap) or aliased local" комментарием в иллюстрирующем коде.

Далее в иллюстрирующем коде создаётся стабилизирующая локальная копия shared_ptr. Мотив понятен — действия внутри f могут привести к удалению widget-а. Таким образом, этот код иллюстрирует определённый «параноидально-защитный» паттерн программирования: в месте использования определённого типа ссылки (shared_ptr) знай, что безопаснее защититься от reentrancy pitfall.

Давайте теперь, взяв на вооружение этот совет (т.е., встав на сторону данного «параноидально-защитного паттерна»), рассмотрим первые две рекомендации раздела:

  • Do not pass shared_ptr (or other countref alternatives) by value, if you do not need to count references (reduces performance due to atomic operations with counter) — prefer passing objects by * or & as usual.
  • Do not pass shared_ptr (or other countref alternatives) by reference or const reference, if you do not need to count references — prefer passing objects by * or & as usual.


Давайте попытаемся совместить логику этих рекомендаций с «параноидально-защитным» паттерном. Caller имеет на руках shared_ptr на widget, который нужно передать в callee (под названием «use» — callee не интересуют манипуляции с ownership widget-а, он просто что-то делает с самим widget-ом). Рекомендовано передать ссылку на widget (то есть в сигнатуре use тип параметра — widget&). Получается, чтобы соблюсти «параноидально-защитный» паттерн, caller перед вызовом use должен удостовериться, что передаваемая ссылка подкреплена локальным инстансом shared_ptr. То есть, если shared_ptr на руках, например, member variable под именем m_p, надо делать так:

shared_ptr<widget> pStabilized = m_p;
use(*pStabilized);

Эту манипуляцию можно было бы выразить короче, если бы (в разрез с первой рекомендацией раздела) параметр use был бы shared_ptr (переданный по значению):

use(m_p);

То есть, в случае принятия обоих рекомендаций раздела (первой и последней) caller должен платить за комфорт callee и создавать стабилизирующие локальные копии не-локальных shared_ptr, так?

Information

Rating
5,449-th
Registered
Activity