Pull to refresh
4
0
Send message
Извините, решил написать реплику, поскольку ваш оппонент не ответил. В студенческие годы я интересовался темой альтернативных аксиоматик, например, есть разделы математики, изучающие последствия удаления или замены некоторых «стандартных» аксиом, например, аксиомы выбора в аксиоматике теории множеств, или логического закона исключения третьего. Так вот, в моём понимании собственно изобретение неевклидовых геометрий в 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, так?
Согласен, отличный фильм, пресматривал раза два. Отличная эпизодическая роль у Henry Rollins-а. Ну, что там говорить.
Что вы посоветуете первым посмотреть, если выбор между nom и rust-peg?
Комментирую свой «другой, напишу в комментариях))» выбор в голосовании. «Захватить власть над человечеством» — ну это как люди захватили власть над муравьями, или таки-нет? Людям муравьи по барабану (хотя нечаянно могут всех спалить, ну так получилось, не специально). Та же история с пост-хьюман соображениями, «что там будет?» — посмотрите, люди в шахматы друг с другом продолжают играть не смотря на победу Deep Blue над Каспаровым… сколько лет там назад это было? Ой, 22 с хвостом года назад. Что-то сильно в мире любителей шахмат изменилось за эти 22 года? Нет. Один мой знакомый (сильный шахматист) искал в партиях AlphaZero плодотворную дебютную идею, не нашёл и был сильно тем разочарован (ну, как если бы муравьи не нашли интересных идей для совершенствования муравейников у людей… которые свои муравейники строят непонятным муравьям способом :) )
Выступление Херба про конкретный проект https://github.com/hsutter/gcpp называлось Lifetime Safety By Default — Making Code Leak-Free by Construction, он его делал примерно 3 года назад на CppCon, слайды здесь, видео здесь — это не про статический анализ, поэтому решил привести ссылки. Пользуясь случаем, про raw pointers (*) и references (&). Некоторое время назад я программировал, избегая их использования, возвращая результатами функций смартпойнтеры и передавая их аргументами, например, как в getChild в вашем коде. Однако, сейчас я пересмотрел этот подход в сторону рекомендаций Core Guidelines, конкретно F.7: For general use, take T* or T& arguments rather than smart pointers, F.60: Prefer T* over T& when “no argument” is a valid option и R.30: Take smart pointers as parameters only to explicitly express lifetime semantics. Херб подробно разбирает этот вопрос в GotW #91 Solution: Smart Pointer Parameters. Конечно, открытие «сырых» ссылок / указателей упрощает злоупотребление, идея в том, что злоупотребления пресекаются статическим анализатором (которому в таком контексте нужно не точно анализировать все возможные варианты lifetime management, а предупреждать о нарушениях конкретной модели lifetime management, предлагаемой в Core Guidelines). Насколько хороши в этом плане существующие анализаторы вроде clang-tidy, сейчас сказать не могу (надо когда-нибудь разобраться, конечно).
Если вы собираетесь писать небольшой генератор, почему бы не нацелить генератор на анализ графа сильных ссылок (для предотвращения циклов) ещё в фазе кодогенерации? Ещё один момент: мне попадались статьи Херба Саттера на данную тему, вам интересно было бы прочитать? Он нацеливался на добавление нового смартпойнтера в стандартную библиотеку (если я не путаю), но не довёл до этого.
Не хотел спорить — однако, заглянул в это обсуждение месяц спустя по другому поводу, заодно попробовал прояснить для себя этот терминологический вопрос (и пока решил остаться при своём мнении). Попадётся на глаза Dragon Book, загляну. Однако, на данный момент я в своей неправоте не убеждён — например, англоязычная Википедия перенаправляет «Syntax Analysis» на «Parsing» — страницу, начинающуюся с фразы «Parsing, syntax analysis, or syntactic analysis is the process of analysing a string of symbols, …». Далее, русская программистская терминология довольно последовательно следует за английской по понятным причинам.

Википедия, конечно, не истина в последней инстанции, но в моих глазах она довольно авторитетна, по крайней мере по околопрограммистским и околоматематическим темам.

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

В условиях неоднозначности я, если мне это сильно занятно, пытаюсь выяснить наиболее частое словоупотребление, и использовать именно его. Пример — ошибочно использовал везде слово «компонента» в женского роде (как в «компонента связности» и в прочих математических и физических «компонентах»), удивляясь, всё чаще встречая его в мужском роде (учился я на математическом факультете, а работать стал программистом). Постепенно осознал, что при употреблении в инженерном контексте большинство собеседников употребляют слово «компонент» в мужском роде, так что ввиду этого мне следует себя переучить — и вообще, это два разных слова, которые я принимал за одно (да, так и есть, ещё раз заглянул в Википедию, русскоязычную уже на этот раз). Возможное различие в словоупотреблении «парсинг» и «синтаксический анализ» беру на заметку, но пока — не убеждён.
Вы пишите о англоязычной (перешедшей также в русский язык) терминологии, а я упомянул французскую, вспомнив рассказ учителя программирования 30-летней давности примерно. Насколько справедливо это утверждение (о том, что французы используют французское слово «октет» в значении английского / русского «байт»?) было тогда, и насколько справедливо оно сейчас? Точно не знаю, но беглая проверка поисковиками меня убедила, что скорее, оно было и остаётся справедливым. Спрошу при случае у коллеги-француза.
Интересно, спасибо. В разделе про DSL вы пишете про «Реализации на базе flex и bison», это очень сурово и олдскульно — у меня опыт с ANTLR довольно позитивный (не уверен, что буду в следующий раз использовать ANTLR, скорее, попробую ещё что-нибудь… Но уж точно не flex и bison! 21 век всё-таки настал :) ). Что сегодня важно для хорошего DSL — реализация LSP (Language Server Protocol) с самого начала. Дойдёт до «следующего раза», буду ориентироваться на инструменты, облегчающие реализацию LSP в первую очередь.
Отличная подборка, спасибо! Добавления:

«Когда-то давно» www.youtube.com/watch?v=o5jgrUVNhWY (люблю пересматривать, интересная графика и саундтрек интересный)

«Урок» www.youtube.com/watch?v=_Z9G7AHb05g (кажется, «Урок» был выше в комментариях, потом проверю)
Достойное примечание, напомнило про «октет» в французском вместо чуждого «байт». Исторически, боюсь, «лексический анализ» уступит «парсингу», увы. Кстати, с языковой точки зрения «лексический анализ» — устаревшее заимствование, архаичное, в сравнении с более современным заимствованием «парсинг».

Information

Rating
Does not participate
Registered
Activity