Версия для печати
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум на Исходниках.RU > Holy Wars > Чистота кода VS оптимизация |
Автор: Jin X 06.04.18, 11:24 |
Много статей развелось про чистоту кода (на основе книги Р.Мартина и не только). В целом концепция, конечно, хорошая. Но бывают какие-то прямо абсурдные, ИМХО, рекомендации. Вот к примеру тут: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> Ну что это за "красота" такая?// ???? const fetchData = (id) => { if (id) { fetch('/data/' + id) } } // ???? const fetchData = (id) => { if (!id) { return } fetch('/data/' + id) } И вот такие слова в конце статьи: Цитата «Погодите! А как насчет изменения производительности?». Мне это безразлично. Правда. Разве что есть реальная проблема, когда приложение становится медленнее, чем ожидалось. С таким мышлением закон Вирта совершенно не кажется "шуткой": Цитата Программы становятся медленнее куда шустрее, чем компьютеры становятся быстрее Далее, второй момент. Более "тонкий", скажем так. Example1 (кусочек кода): <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> . . . while i*i <= n: k = 1 while n % i == 0: k += 2 n //= i result /= k i += 1 . . . Example2 (кусочек кода): <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> Не углубляйтесь в суть алгоритма, это неважно.. . . while i*i <= n: if n % i == 0: k = 3 while 1: n //= i if n % i: break k += 2 result /= k i += 1 . . . Второй вариант чуть более сложный для понимания (спасибо разработчикам Python'а за отсутствие do-while/repeat-until), но и чуть более оптимальный по скорости, ибо тут нет лишнего деления (ну и кое-чего ещё по мелочи). Согласен, не самый показательный пример (особенно учитывая, что это скриптовый язык, но давайте не будем обращать на это внимания, вникните в суть темы... представьте, что это C++ ). Собственно, можно особо не заморачиваться и использовать первый вариант. Но представим, что мы пишем библиотеку и не знаем как она будет использоваться. Насколько критична будет скорость в финальном коде? Ваши мысли обо всём этом безобразии... |
Автор: JoeUser 06.04.18, 12:02 |
Не читай плохих статей! Создавать Elephantware имеет смысл только тогда, когда ты в пожизненной разработке проекта, и тебе оплачивают не качество, а количество доработок. |
Автор: villain 06.04.18, 12:21 |
как это не углубляйтесь? возможно там вообще деление не нужно |
Автор: Jin X 06.04.18, 12:46 |
Суть вообще не в этом. Будем считать, что алгоритм достаточно оптимален (это часть кода разложения на простые множители и манипуляции с ними), но есть 2 вот таких варианта реализации Там вообще умножение должно быть, я заменил его на деление для пущего эффекта |
Автор: Jin X 06.04.18, 13:12 |
Цитата JoeUser @ Не, ну понятно, что создавать класс в 3 поколения с 10 методами в каждом там, когда нужно сделать просто min(x,y) - это бред. Речь идёт именно о чистоте кода, а не о заделе на будущее. Т.е. о понятности кода тебе и людям с возможностью лёгкой модификации.Создавать Elephantware имеет смысл только тогда, когда ты в пожизненной разработке проекта, и тебе оплачивают не качество, а количество доработок. Реально ли (почти) всегда нужно писать одно действие-одна функция и выстраивать 5-этажные вызовы там, где можно было бы обойтись одной функцией? Реально ли (почти) всегда стоит заменять: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> на:// ???? people .filter(person => person.age > 10 && person.firstName.length > 2 && person.lastName.length > 4) <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> ради лучшей читабельности? И т.д. // ???? people .filter(person => person.age > 10) .filter(person => person.firstName.length > 2) .filter(person => person.lastName.length > 4) |
Автор: applegame 06.04.18, 13:19 |
Цитата Jin X @ Реально ли (почти) всегда стоит заменять: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> на:// ???? people .filter(person => person.age > 10 && person.firstName.length > 2 && person.lastName.length > 4) <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> ради лучшей читабельности? И т.д.// ???? people .filter(person => person.age > 10) .filter(person => person.firstName.length > 2) .filter(person => person.lastName.length > 4) Можно еще вот так <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> // ???? people.filter( person => person.age > 10 && person.firstName.length > 2 && person.lastName.length > 4 ) |
Автор: D_KEY 06.04.18, 13:58 |
Думаю, что тут имелся в виду принцип о размещении реакции на нарушения предусловий и обработки "внешних" ошибок в начале функций/методов. Как правило это действительно улучшает читаемость, поскольку дальнейшеая логика становится более простой. Добавлено Цитата Jin X @ И вот такие слова в конце статьи: Цитата «Погодите! А как насчет изменения производительности?». Мне это безразлично. Правда. Разве что есть реальная проблема, когда приложение становится медленнее, чем ожидалось. С таким мышлением закон Вирта совершенно не кажется "шуткой": Цитата Программы становятся медленнее куда шустрее, чем компьютеры становятся быстрее Ну портить код без оснований для улучшения производительности действительно не стоит. И вполне допустимо улучшить его там, где скорость не критична. Добавлено Совсем не знаем? Тогда зачем пишем? Добавлено Цитата Jin X @ Реально ли (почти) всегда стоит заменять: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> на:// ???? people .filter(person => person.age > 10 && person.firstName.length > 2 && person.lastName.length > 4) <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> ради лучшей читабельности? И т.д.// ???? people .filter(person => person.age > 10) .filter(person => person.firstName.length > 2) .filter(person => person.lastName.length > 4) А здесь лучше читабельность? |
Автор: amk 06.04.18, 14:27 |
Цитата Jin X @ Какой же он оптимальный? Тут в каждом цикле два деления, когда можно обойтись одним. Второй вариант чуть более сложный для понимания (спасибо разработчикам Python'а за отсутствие do-while/repeat-until), но и чуть более оптимальный по скорости |
Автор: Jin X 06.04.18, 15:14 |
2 деления во внешне цикле, 2 во внутреннем. Во втором примере при невыполнении условия внешнего цикла деление одно (в самом этом условии). Мне вот даже интересно, как тут можно обойтись одним делением? Добавлено А, ну вот вижу вроде в питоне есть функция divmod, но это для внешнего цикла только... а дальше? Добавлено Хотя нифига, divmod, как оказалось, работает медленнее (даже медленнее 2 вызовов: % и //) Добавлено Причём, я сейчас замерил скорость Example1 и Example2 (причём, с умножением, а не с делением result'а). Разница более 30%, о как! |
Автор: amk 06.04.18, 16:05 |
Цитата Jin X @ Т ак ты же вроде просил не обращать внимания на язык. В Си оптимизатор заменяет пару % / на одно деление. Вынесение одной из этих двух операций может сломать оптимизацию.Хотя нифига, divmod, как оказалось, работает медленнее (даже медленнее 2 вызовов: % и //) В любом случае нет смысла огород городить из-за всего одного сэкономленного деления. Судя по всему, это деление мало на что влияет. |
Автор: Jin X 06.04.18, 16:07 |
Вот написал кто-то функцию power. Сразу понятно, где она может использоваться? Добавлено Ну код же не на Си Но в любом случае % во внутреннем цикле и result /= k ты одним делением не сделаешь. Добавлено Но мы от сути вопроса отошли... |
Автор: D_KEY 06.04.18, 16:55 |
Да, практически везде, где будет применим сам язык. Значит это будет где-то в стандартной библиотеке и должно работать приемлемо во всех нишах, на которые рассчитан язык |
Автор: Pavia 06.04.18, 17:04 |
Тут видимо конфликт рекомендаций. Должно быть <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> const fetchData = (id) => { if (!id) return; fetch('/data/' + id) } Но это противоречит безопасности if без else. Да и return по серёдке кода. <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> . . . while i*i <= n: if n % i == 0: k = 3 while 1: n //= i if n % i: break k += 2 result /= k i += 1 . . . Цитата Jin X @ Второй вариант чуть более сложный для понимания (спасибо разработчикам Python'а за отсутствие do-while/repeat-until), Так там другой подход через множества. Данный код лишён контекста(окружения) поэтому сказать как его от рефакторить трудно. Но как минимум он напрашивается на разбиение на 2 функции. Скорее всего эти 2 функции можно нормально поименовать, внутрь вставить ваши оптимизации. А для того что-бы вызов функций не тормозил сделать их через шаблоны. Цитата Jin X @ Собственно, можно особо не заморачиваться и использовать первый вариант. Но представим, что мы пишем библиотеку и не знаем как она будет использоваться. Насколько критична будет скорость в финальном коде? Это смотря как писать. Вот к примеру есть математическая библиотека eigen легко читаемая оптимизированная и не уступает по скорости конкурентам. Или взять agg тоже качественный код. На самом деле я у себя сформировал комплект исходных кодов, которые счёл наиболее качественными. Считаю что надо стараться писать как можно более понятный код. Почему стараться? Потому что те библиотеки, которые я назвал появились не на пустом месте до них были другие библиотеки, которые имели плохой код. Во-вторых надо писать только то что нужно не делать код на будущее. Код развивается и постепенно наполняет новыми функциями но в тоже время код подлежит рефакторенгу. Тот же OpenCV переживает уже 4 версию. Каждая версия не совместима с другой потому что отрефакторина убран плохой код убраны подпорки. Правда OpenCV не дотягивает до качественного кода, так как содержит нечитаемый код. В этом плане AForge.NET мне больше нравится (Си#), |
Автор: Serafim 07.04.18, 00:40 |
Очевидно, что второй вариант на порядок лучше, хз что тебе не нравится Добавлено Фигурные скобки опускать никогда нельзя |
Автор: applegame 07.04.18, 05:37 |
Никогда не говори "никогда". |
Автор: Jin X 08.04.18, 06:56 |
Он лучше, когда основной код занимает больше 1 строки, а здесь какой смысл в этом нагромождении? Как же делать if, в котором не нужен else? Прописывать else; тоже не фонтан... |
Автор: D_KEY 08.04.18, 08:21 |
Цитата Jin X @ Он лучше, когда основной код занимает больше 1 строки, а здесь какой смысл в этом нагромождении? Сейчас там 1 строка, завтра может быть 10. Без "нагромождения" нужно будет переписывать. pull-request'ы будут больше и дольше будешь апрувов ждать |
Автор: Serafim 08.04.18, 11:29 |
Цитата Jin X @ Он лучше, когда основной код занимает больше 1 строки, а здесь какой смысл в этом нагромождении? Ранний выход из функции проще читается. Я хз как сформулировать своими словами почему именно проще, ты меня в тупик поставил |
Автор: Jin X 08.04.18, 14:33 |
D_KEY, намекаешь на то, что все будут пытаться исправить? Serafim, я понимаю, когда там хотя бы 2-3 строки основного кода. Или если есть большая вероятность, что код будет дописан. Но если предпосылок к расширению кода пока особых нет, чем это лучше? <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> противconst fetchData = (id) => { if (!id) return; fetch('/data/' + id) } <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> const fetchData = (id) => { if (id) fetch('/data/' + id) } Хоть со {скобками}, хоть без... |
Автор: villain 09.04.18, 05:29 |
Цитата Jin X @ Суть вообще не в этом. Будем считать, что алгоритм достаточно оптимален (это часть кода разложения на простые множители и манипуляции с ними), но есть 2 вот таких варианта реализации Там вообще умножение должно быть, я заменил его на деление для пущего эффекта это не 2 варианта реализации, это 2 разных алгоритма первый лучше - нет внезапного выхода из цикла, 2 условия vs 4 а за <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> while 1: ... if n % i: break отдельный котёл положен |
Автор: Jin X 09.04.18, 11:12 |
По большому счёту, это один алгоритм всё же. Вот, к примеру здесь тоже 3 разных алгоритма? Это отсутствие do-while в Python. Можно поменять 2 последние строки местами, как вариант, а перед циклом написать k=1 |
Автор: villain 09.04.18, 12:44 |
Цитата Jin X @ По большому счёту, это один алгоритм всё же. Вот, к примеру здесь тоже 3 разных алгоритма? порядок действий разный? значит разные. Цитата Jin X @ Это отсутствие do-while в Python. Можно поменять 2 последние строки местами, как вариант, а перед циклом написать k=1 зачем, если есть вариант без этих костылей? |
Автор: Serafim 09.04.18, 13:13 |
Цитата Jin X @ Serafim, я понимаю, когда там хотя бы 2-3 строки основного кода. Или если есть большая вероятность, что код будет дописан. Но если предпосылок к расширению кода пока особых нет, чем это лучше? Как минимум тем, что в первом варианте явно видна сигнатура Promise|null, а во втором - нет. Да и, повторюсь, за опускание фигурных скобок хочется убивать В культурном мире - это как код без отступов - признак плохих манер или неуча за клавиатурой. По крайней мере в JS и некоторых других языках, где уже давно есть codestyle стандарты. |
Автор: Астарот 09.04.18, 14:33 |
Цитата Serafim @ Да и, повторюсь, за опускание фигурных скобок хочется убивать В культурном мире - это как код без отступов - признак плохих манер или неуча за клавиатурой. Ох уж мне эти "культурные люди", все-то они возводят в абсолют <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> void doSmthng(){ if(smthngBad1)throw new Exception("Something bad 1"); if(smthngBad2)throw new Exception("Something bad 2"); if(smthngBad3)throw new Exception("Something bad 3"); do(); } Появление фигурных скобок читаемость ухудшит в разы. |
Автор: Serafim 09.04.18, 14:44 |
Ага, конечно <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> function doSmthng() { switch (true) { case smthngBad1: throw new Exception("Something bad 1"); case smthngBad2: throw new Exception("Something bad 1"); case smthngBad3: throw new Exception("Something bad 1"); } do(); } Добавлено И то, это потому, что в JS нет нормального паттерн матчинга |
Автор: Астарот 09.04.18, 15:25 |
И зачем ты три легко читаемые строчки превратил в... это? Да и речь шла про Ну, вот случай, когда опускать их прямо показано |
Автор: applegame 09.04.18, 15:38 |
Цитата Serafim @ Ага, конечно <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> function doSmthng() { switch (true) { case smthngBad1: throw new Exception("Something bad 1"); case smthngBad2: throw new Exception("Something bad 1"); case smthngBad3: throw new Exception("Something bad 1"); } do(); } Вот за такое и правда, хочется взять и у@#$ть. Мне понадобилось несколько секунд, чтобы понять что за муйня такая этот ваш switch(true). |
Автор: Астарот 09.04.18, 15:39 |
Редкий случай, но я вот тут соглашусь |
Автор: applegame 09.04.18, 15:42 |
И чем тебе в этом случае поможет "нормальный" паттерн-матчинг? |
Автор: Астарот 09.04.18, 15:46 |
Думаю речь тут идет в том числе о том, что в js в case можно методы вызывать, то есть за исключением многословности тот же if получается. |
Автор: Славян 09.04.18, 16:21 |
Цитата Serafim @ И как вы, Serafim, переделаете Астаротский пример, если будет обработка с возвратом?.. (или в Яве такого нет?) Ага, конечно <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> function doSmthng() { switch (true) { case smthngBad1: throw new Exception("Something bad 1"); case smthngBad2: throw new Exception("Something bad 1"); case smthngBad3: throw new Exception("Something bad 1"); } do(); } |
Автор: Serafim 09.04.18, 21:30 |
хороший вопрос) в данном случае, кажется, ничем) Цитата Славян @ И как вы, Serafim, переделаете Астаротский пример, если будет обработка с возвратом?.. (или в Яве такого нет?) Имеется ввиду посткондишн? Для этого приватные методы и декораторы, кажется, придумали) |
Автор: Астарот 09.04.18, 21:49 |
При чем тут приватные методы и декораторы ? О_о |
Автор: Serafim 10.04.18, 01:53 |
1) Крупные методы принято делить на более мелкие, в частности твой вариант, в языках с возвращаемым значением в операндах (JS, Python, Ruby), вместо кастуемых (Java, PHP) моет выглядеть следующим образом: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> // На псевдокоде, а-ля JS или жаба class Some { public function doSmthng() { return checkAbility(a) && checkAbility(b) && checkAbility(c) && do(); // В первой группе языков вернётся значение последнего метода } private function checkAbility(value) { return (bool)value || throw new Exception('...'); } } а ещё можно воспользоваться редукцией в группе "вторых языков" <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> private function checkAbilities(...values): bool { return values.reduce((i, result) -> result ? !i : result, true); // Возвращает, либо тру, либо фолс, если один из аргументов values его содержит } 2) В случае если нужна "обработка с возвратом", что бы это не значило, то можно воспользоваться декоратором, и завраппить ответ. Например, из твоего опыта Астарот, могу привести в пример NotNull аннотацию, которая "враппит" (или контейнеризирует, или декорирует, или вообще это монада ) оригинальное значение. Не она, конечно, а подписчик, не допуская нулевого значения в содержимом. В тоже время Ensure и Verify контракты DbC вполне могут сойти за одну из частных применений декораторов на методах. Выбирай любой способ. Добавлено P.S. Но это всё имеет смысл, когда подобных операций больше трёх. В примере из трёх булевых достаточно: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> function doSmthng() { if (smthngBad1 || smthngBad2 || smthngBad3) { throw new Exception("Something bad"); } do(); } |
Автор: Астарот 10.04.18, 06:25 |
Какой кошмар Однострочник, в котором еще найди нужную do(), или наоборот нужную проверку, которую ты вынес на сторону, и куда нужно еще посмотреть, что б узнать, что именно оно бросает. Бряки на дебаге тоже ставить одно удовольствие. Б-же, зачем себя так не любить? Убивайтунг! Вот просто и сразу - ну, не читается же глазом эта билеберда, так зачем? Потому что можешь? Наверное, никогда не пойму этого... Добавлено Ага, а потом гадай - какая же из трех "выстрелила" |
Автор: applegame 10.04.18, 06:30 |
Цитата Астарот @ Сильно похоже на ЫГМ - Ынтырпрайз Гойловного Моска. Убивайтунг! Вот просто и сразу - ну, не читается же глазом эта билеберда, так зачем? Потому что можешь? Наверное, никогда не пойму этого... |
Автор: Vesper 10.04.18, 06:36 |
Что интересно, в любом случае получится if-else, только на несколько уровней ниже. |
Автор: Астарот 10.04.18, 07:02 |
Не знаю, что это, но мне реально страшно - это ж что он может навертеть в действительно не очевидных местах! |
Автор: applegame 10.04.18, 07:40 |
Цитата Астарот @ Вот что это такое - FizzBuzz Enterprise Edition is a no-nonsense implementation of FizzBuzz made by serious businessmen for serious business purposes.Не знаю, что это, но мне реально страшно - это ж что он может навертеть в действительно не очевидных местах! Вот где настоящий ужас |
Автор: Астарот 10.04.18, 07:58 |
Не понимаю, что тебе не нравиться, там даже тесты есть |
Автор: applegame 10.04.18, 08:01 |
Что значит "даже"? Это коммерческий код написанный коммерческим программистом в соответствии с высочайшими энтерпрайз стандартами. Там по определению не может не быть тестов. |
Автор: Jin X 10.04.18, 10:29 |
switch(true) - это сильно Больше похоже на обфускацию, а не упрощение кода Это прикольный выкрутас, конечно (и где-то даже "классический"). Вот только если попадёт в руки джуну, не каждый сообразит, как сие работает. А вот это особенно. Добавлено <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> Или...function doSmthng() { var i; if (smthngBad1 && (i=1) || smthngBad2 && (i=2) || smthngBad3 && (i=3)) { throw new Exception("Something bad " + i); } doIt(); } <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> function doSmthng() { var i = 1; if (smthngBad1 || ++i && smthngBad2 || ++i && smthngBad3) { throw new Exception("Something bad " + i); } doIt(); } |
Автор: OpenGL 10.04.18, 10:55 |
А что это switch(true) делает? Я верно понял, что switch разворачивается в пачку if-ов, в каждом из которых проверяется равенство того, что в switch и в case? |
Автор: Jin X 10.04.18, 11:01 |
OpenGL, получается так... <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> if (true == smthngBad1) {...} if (true == smthngBad2) {...} |
Автор: Vesper 10.04.18, 11:11 |
И потом нарываешься на хитро закопанные грабли в виде смены ключей оптимизации кода, и выясняется... (памяти {$B+}) |
Автор: OpenGL 10.04.18, 11:18 |
Цитата Vesper @ И потом нарываешься на хитро закопанные грабли в виде смены ключей оптимизации кода, и выясняется А причём тут оптимизация? && и || обязаны выполняться по-порядку, и оптимизатор не имеет права их переставлять. |
Автор: Jin X 10.04.18, 11:34 |
Vesper, ну это ж не Delphi, там бы {$B-} был. Ну и это отчасти троллинг примера Серафима, а отчасти... ну мне тоже нравятся такие конструкции и нагромождения всякие (усложнения) А && имеет более высокий приоритет, чем ||. Добавлено Есть ещё замечательная операция xor (^). Тоже можно побаловаться а-ля if (x ^ y) doSmthng(); Видимо, ^^ не стали делать как раз от греха подальше Добавлено Serafim, вот скажи. Ну нафига фигурные скобки для if (while, for... нужное подчеркнуть), который прописан в одну строку? Собственно, это смежно с if (id) fetch('/data/' + id); |
Автор: Serafim 10.04.18, 11:40 |
Ну не говорить же, что if тут будет лучшим вариантом, только за опускание фигурных скобок всё равно надо отрубить что-нибудь лишнее)) Так что пришлось выкручиваться. С другой стороны, мне кажется нормальным использование switch/case для однотипных условных операторов. Кажется, что для этого он и был предназначен, не?) |
Автор: Jin X 10.04.18, 11:41 |
Я понимаю, что какой-нибудь ландырь может по запаре забыть поставить скобки, но при наличии выравнивания этот косяк сразу будет виден... Добавлено Эх, не успел склеить с предыдущим сообщением Добавлено ИМХО, он был (хорошее слово) предназначен для проверки 1 переменной на разные значения, а не наоборот |
Автор: Serafim 10.04.18, 11:43 |
Цитата Jin X @ Serafim, вот скажи. Ну нафига фигурные скобки для if (while, for... нужное подчеркнуть), который прописан в одну строку? Для языков, которые я использую - это общепринятый стандарт, всё равно потом CS поправит код в CI, так что пофигу. Да и вообще, ГОСТ для C/C++, кажется, есть на эту тему |
Автор: Jin X 10.04.18, 11:46 |
Когда больше трёх, ИМХО, лучше использовать не отдельные переменные, а множество, к примеру... |
Автор: Serafim 10.04.18, 11:46 |
Цитата Jin X @ ИМХО, он был (хорошее слово) предназначен для проверки 1 переменной на разные значения, а не наоборот Допускаю, что ты прав. Но это не значит, что обратный случай - плохой. |
Автор: Jin X 10.04.18, 11:46 |
СНиП и СанПиН |
Автор: Serafim 10.04.18, 11:47 |
Цитата Jin X @ Когда больше трёх, ИМХО, лучше использовать не отдельные переменные, а множество, к примеру... +1 |
Автор: Jin X 10.04.18, 11:54 |
Serafim, кстати, что ты думаешь об if (smthngBad1 && (i=1) || smthngBad2 && (i=2) || smthngBad3 && (i=3)) ? |
Автор: OpenGL 10.04.18, 11:57 |
Не от греха подальше, а потому что операция бесмысленна. Главное отличие || от | (&& и &, соответственно), помимо того, что одна - логическая, а вторая - побитовая в том, что первая ещё и ленивая. А вот ^^ ленивой не сделать. Хм, прочитал про это. Какому идиоту вздумалось вводить этот ключ? Это же трындец какой-то. |
Автор: Serafim 10.04.18, 11:58 |
Цитата Jin X @ Serafim, кстати, что ты думаешь об if (smthngBad1 && (i=1) || smthngBad2 && (i=2) || smthngBad3 && (i=3)) ? Это печально Сам иногда говнокодю, делая присваивания в условиях, но не до такой степени |
Автор: Астарот 10.04.18, 12:01 |
Эй, а он не безнадежен! |
Автор: Qraizer 10.04.18, 12:06 |
Цитата OpenGL @ При этом они ещё точки следования вставляют, так что ++ в безопасности.Это обычное !=. Зачем? А причём тут оптимизация? && и || обязаны выполняться по-порядку, и оптимизатор не имеет права их переставлять. |
Автор: Jin X 10.04.18, 12:34 |
Цитата OpenGL @ Если под ленивой ты подразумеваешь проверку одного условия из 2-х (если оно true для ||), то отчего ж && не ленивая (при первом false)?Не от греха подальше, а потому что операция бесмысленна. Главное отличие || от | (&& и &, соответственно), помимо того, что одна - логическая, а вторая - побитовая в том, что первая ещё и ленивая. А вот ^^ ленивой не сделать. Ну ладно... if (smthngBad1 || ++i && smthngBad2 || ++i && smthngBad3) Как будто бы и не присвоение почти (на всякий случай скажу, что согласен, что это говнокодинг, но выглядит прикольно, поэтому иногда так и подмывает так писать) Добавлено Хотя нет, не тормознул... 5 != 7 = true но 5 ^^ 7 = false Добавлено Если только так извращаться: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> но это бред. var x=5, y=7; alert((x&&1) != (y&&1)); // false |
Автор: OpenGL 10.04.18, 13:01 |
Цитата Jin X @ Если под ленивой ты подразумеваешь проверку одного условия из 2-х (если оно true для ||), то отчего ж && не ленивая (при первом false)? && тоже ленивая уж. Имеется ввиду, что || в моём предложении ты можешь заменить на &&, а | на &. |
Автор: Jin X 10.04.18, 13:02 |
Вот, блин! В JS: 5&&1 = 1, а 1&&5 = 5 5||0 = 5 и 0||5 = 5 (почему одинаково - понятно, но почему не true/false?) Чё за бред? p.s. В сях всё нормально работает: везде выдаёт 1 или true (в т.ч. для 2&&1 и 1&&~1, что ожидаемо). Добавлено Что??? Добавлено OpenGL, ты имеешь в виду, что && и || ленивые в отличие от & и | ? А разве основная ценность в этой ленивости? И это достаточная причина, чтобы не делать ^^ ? Ну реально бывает (редко, но бывает), что нужно сделать (x ^^ y). Можно, конечно, написать (!x != !y), но это тоже не вот прям офигеть как читаемо |
Автор: Qraizer 10.04.18, 13:18 |
Ну это не серьёзно. Типы данных разные, и && и &, равно как и || и |, определены для разных типов, для которых есть взаимные касты, тогда как != одна на все типы, и кастов не требуется. Ты же не думаешь, что за типами следить не надо? А то я как-то видал в коде if(a != true), было весело писать багрепорты. |
Автор: Jin X 10.04.18, 13:21 |
Надо, но всегда ли есть смысл писать if (x != 0), когда можно написать просто if (x) ? А в случае с xor как-то тоже бредово выглядит if ((x && !y) || (!x && y)) и аналогично if ((x!=0 && y==0) || (x==0 && y!=0)). Как ты предлагаешь реализовать xor, чтобы получилось красиво? Добавлено Или: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> тоже не айс... bool boolX = x, boolY = y; if (boolX != boolY) {...} if ((bool)x != (bool)y) Добавлено В JS соответственно: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> var boolX=!!x, boolY=!!y; if (boolX != boolY) {...} |
Автор: OpenGL 10.04.18, 13:37 |
Да. Добавлено Цитата Jin X @ А разве основная ценность в этой ленивости? И это достаточная причина, чтобы не делать ^^ ? Имхо, да. Из-за ленивости ты сможешь написать что-то наподобие if(index < a.len() && a[index] < 42), и замена && на & тут не прокатит. ^^ же ровно с тем же успехом в большинстве случаев заменяется на ^. Когда x и y не булевские, и просто конвертятся неявным кастом? Я в этом случае просто сам сравниваю с нулём. Т.е. как-то if ((x != 0) ^ (y != 0)) пишу. |
Автор: Jin X 10.04.18, 13:47 |
Периодически встречаются конструкции вида if x % 1 == 0 или if not 1 % 1 — проверка на целое (python) Или n = ~~(n/5) — деление нацело (JS) Прикольно, конечно, но в "чистоте" такого кода у меня сомнения... |
Автор: OpenGL 10.04.18, 13:48 |
Цитата Jin X @ Надо, но всегда ли есть смысл писать if (x != 0), когда можно написать просто if (x) ? Почему нет? В языках без неявного приведения к bool писать != 0 не напрягает совершенно. |
Автор: Serafim 10.04.18, 13:48 |
Цитата Jin X @ Вот, блин! В JS: 5&&1 = 1, а 1&&5 = 5 5||0 = 5 и 0||5 = 5 (почему одинаково - понятно, но почему не true/false?) Чё за бред? Я же написал даже, что есть языки, где операнды возвращают значение, а не булев результат: Чистота кода VS оптимизация (сообщение #3766081) пункт номер 1 |
Автор: Jin X 10.04.18, 13:50 |
Вариант, конечно (тогда можно использовать != – так понятнее). Но это точно лучше, чем if (x ^^ y) (если бы он был)? Добавлено Цитата Serafim @ Сенькс. В Java так же? Я же написал даже, что есть языки, где операнды возвращают значение, а не булев результат |
Автор: OpenGL 10.04.18, 13:54 |
Зависит от того, как ты относишься к неявным кастам чисел к bool Я вот за пределами олимпиад стараюсь их избегать, и поэтому даже if (a && b) у меня выглядит как if (a != 0 && b != 0) |
Автор: Jin X 10.04.18, 13:56 |
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> #define XOR(a, b) (bool(a) != bool(b)) . . . if (XOR(x, y)) {...} |
Автор: Serafim 10.04.18, 14:02 |
Я не помню, кажется там каст к булеву. Тут тебе Астарот поможет. А в Ruby и JS - 146% возврат значения. Плюс, в рубях и котлине сами операторы могут возвращать значение, типа: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> return if (errValue || errValue2) { throw Exception("Whoops!") } else { print("OK") do(); true } |
Автор: Jin X 10.04.18, 14:10 |
C++ <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> int func(int val) { return val ? (printf("%d ", val), 2) : -1; } int main() { int x; scanf("%d", &x); printf("%d\n", func(x)); return 0; } Добавлено Или так <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> int func(int val) { return val ? ({int x = val*5; printf("%d ", x); 2;}) : -1; } int main() { int x; scanf("%d", &x); printf("%d\n", func(x)); return 0; } |
Автор: negram 10.04.18, 14:20 |
ммм. А как тема "Чистота кода VS оптимизация" превратилась в соревнование кто по-уродливее код написать может? |
Автор: Serafim 10.04.18, 14:28 |
Это значит, что любой оператор (хотя в котлине вроде только if): <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> verifyItems = (...args) => (i or throw Error "asd" for i of args).length Применение для каждого элемента args функции, которая в случае элемента, который равен false вернёт ошибку, в остальных случаях значение аккумулируется и от него берётся length, т.е. количество элементов в результате (этот ваш незамысловатый i++ в if условии). А вот даже можно собрать это всё |
Автор: Qraizer 10.04.18, 14:30 |
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> if(!!x != !!y) /* ... */ |
Автор: Jin X 10.04.18, 14:36 |
Qraizer, ага. С одним ! не так красиво? |
Автор: Qraizer 10.04.18, 15:17 |
Конечно, нет. Алгоритм-то другой. |
Автор: Славян 10.04.18, 15:18 |
А я, Serafim, вообще стараюсь избегать фигурных, если позволительно, a'la: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> if( ... ) A = B, func1( ... ), p = func2(...); else C = D, ...; |
Автор: Jin X 10.04.18, 15:27 |
Славян, ну это уже борщ, ИМХО |
Автор: Serafim 10.04.18, 17:28 |
А я джунов своих по рукам за это бью) |
Автор: applegame 10.04.18, 17:39 |
Бедные джуны. |
Автор: D_KEY 10.04.18, 17:44 |
А тот код, что ты выше приводил, заставляешь писать? |
Автор: Serafim 10.04.18, 17:44 |
Зато QA меньше проблем с "внезапно появившимися багами из ниоткуда" Добавлено Допустим повышенный Complexity, как в примере выше, обычный анализатор сам подсветит, сложнее с Protected Variations и Cohesion - тут надо самому думать как не свалиться в рефакторинг) |
Автор: Vesper 10.04.18, 18:14 |
Приоритет приоритетом, но флаг complete boolean eval означает вычислить все части булева выражения, даже если они уже не влияют на итоговый результат. Пример был вида "if (a<100) or add1(a) then..." где add1 - функция с сайд-эффектом, возвращающая boolean, и при B+ переменная а всегда увеличивалась, а при В- только если (а<100) было ложным. Вот мало ли кто захочет зачем-то поменять эту нас ройку, тем боле если в заголовке где-то проскочит, а особенно если это что-то фиг-отладишь типа буста. Вот надо им было, а ты теперь страдай. Цитата OpenGL @ Хм, прочитал про это. Какому идиоту вздумалось вводить этот ключ? Это же трындец какой-то. Сюрприз, да |
Автор: Jin X 10.04.18, 18:50 |
Цитата Vesper @ Ну вообще, по умолчанию она отключена. Но я, например, если пишу код, в котором эта настройка (или какая-либо другая) важна, прописываю её в заголовке и проблем нет.Приоритет приоритетом, но флаг complete boolean eval означает вычислить все части булева выражения, даже если они уже не влияют на итоговый результат. Там помимо этой настройки есть ещё R, Q, Z и пр., которые тоже могут неслабо влиять на результат. Надо просто знать об этом и проставлять нужные опции. В IDE есть комбинация Ctrl+O+O, которая позволяет быстро увидеть текущие настройки и зафиксировать их. Добавлено В целом же, довольно неплохо иметь привычку всовывать в начало исходников строчку вида {$A8,Z1,O+,Q-,R-,B-} А в главный модуль (program) можно ещё и {$SetPEFlags IMAGE_FILE_RELOCS_STRIPPED} (при подключенном модуле Windows, ну или {$SetPEFlags 1}) вставлять для удаления фиксапов из EXE-шника (которые там нафиг не нужны). Только в DLL такого не надо делать |
Автор: D_KEY 10.04.18, 19:48 |
Неужели кто-то ещё пишет на Delphi? |
Автор: Jin X 10.04.18, 20:06 |
D_KEY, нет, конечно, никто не пишет. Embarcadero чисто по загону новые версии выпускает. Для прикола, тупо поржать |
Автор: Qraizer 10.04.18, 21:56 |
...чем напрочь запрещаем ASLR. |
Автор: OpenGL 11.04.18, 05:56 |
Цитата Vesper @ Вот мало ли кто захочет зачем-то поменять эту нас ройку, тем боле если в заголовке где-то проскочит, а особенно если это что-то фиг-отладишь типа буста. В плюсовых компиляторах тоже такая настройка имеется? Что-то сильно сомневаюсь. Максимум, что ты сделаешь в плюсах - перегрузишь логические операторы для своих типов. И вообще, можно пруф того, что это бывает не только в дельфи? |
Автор: Jin X 11.04.18, 11:43 |
Чтобы ASLR работал, нужно либо включить соответствующие опции в PE-заголовке (при компиляции, например). Либо прописать эти опции в реестре для конкретного файла. Но в целом ты прав, конечно. Добавлено Не углублялся в эту тему раньше. Ну тогда вместо отключения фиксапов можно включать ASLR и DEP (ну или просто DEP хотя бы): <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> {$APPTYPE CONSOLE} uses Windows, SysUtils; const IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = $40; // ASLR, {$DYNAMICBASE ON} in Delphi 2007+ IMAGE_DLLCHARACTERISTICS_NX_COMPAT = $100; // DEP {$SetPEOptFlags IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE or IMAGE_DLLCHARACTERISTICS_NX_COMPAT} // {$SetPEOptFlags $140}, works on Delphi 6+ (and maybe earlier) // http://qaru.site/questions/663368/how-can-i-enable-depnx-and-aslr-on-a-delphi-2006-or-earlier-executable // https://habrahabr.ru/company/eset/blog/206244/ const R: Byte = $C3; begin WriteLn(IntToHex(DWord(@IntToHex), 8)); try TProcedure(@R); WriteLn('DEP is OFF :('); except WriteLn('DEP is ON :)'); end; end. |
Автор: Qraizer 11.04.18, 12:08 |
Без reloc-ов по-любому ASLR не заработает. Но тут как бы палка о двух концах. Во время отладки стрипать их даже полезно, ибо бряки, не привязанные к строкам кода, а поставленные, скажем, на конкретные ассемблерные инструкции, что нередко бывает нужно для либ без сыров, при активном ASLR слетают токатак. Но вот в релизе я удалять их всё-таки бы не советовал. |
Автор: Jin X 11.04.18, 16:11 |
Да это понятное дело. Можно так: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> {$IFDEF DEBUG} {$SetPEFlags IMAGE_FILE_RELOCS_STRIPPED} {$ELSE} {$SetPEOptFlags IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE} {$ENDIF} {$SetPEOptFlags IMAGE_DLLCHARACTERISTICS_NX_COMPAT} |
Автор: Jin X 11.04.18, 17:59 |
Кстати, кто подскажет, что это за прога (которая показывает заголовок PE в таком виде)? |
Автор: korvin 12.04.18, 20:17 |
Цитата Serafim @ Крупные методы принято делить на более мелкие, в частности твой вариант, в языках с возвращаемым значением в операндах (JS, Python, Ruby), вместо кастуемых (Java, PHP) моет выглядеть следующим образом: … А в нормальных следующим: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> doSomethingClever :: Int -> String -> String doSomethingClever id name = show id ++ " => " ++ name tryToDoSomethingClever :: Maybe Int -> Result String tryToDoSomethingClever idParam = do id <- getId idParam item <- getItem id name <- getName item return (doSomethingClever id name) Добавлено Цитата Jin X @ Embarcadero чисто по загону новые версии выпускает. Для прикола, тупо порубить хоть чуть-чуть бабла на легаси-проектах Ага. |
Автор: Serafim 12.04.18, 22:53 |
Оуоу, палехче))) P.S. А это разве не первый вариант? Возвращается же в конце концов функция (точнее композиция функций). Только указанная в качестве первого операнда, если, конечно не использовался оператор "$", тогда вернётся последующий аргумент. P.P.S. Хотя... Кажется это ещё один вариант: 1) C, Java, PHP: "res = a || b" Результат: res = bool 2) JS, Ruby, Python: "res = a || b" Результат: res = a (если он тру) или b 3) Haskell: "res = a || b" Результат: функция "||" от a и b (я довольно плохо плохо знаю Haskell, так что поправь, если ошибаюсь) |
Автор: korvin 13.04.18, 08:35 |
Что? <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> {- tryToDoSomethingClever :: Maybe Int -> Result String tryToDoSomethingClever idParam = do id <- getId idParam item <- getItem id name <- getName item return (doSomethingClever id name) tryToDoSomethingClever :: Maybe Int -> Result String tryToDoSomethingClever idParam = getId idParam >>= (\id -> getItem id >>= (\item -> getName item >>= (\name -> return (doSomethingClever id name)))) -} tryToDoSomethingClever :: Maybe Int -> Result String tryToDoSomethingClever idParam = case getId idParam of Err msg -> Err msg Ok id -> case getItem id of Err msg -> Err msg Ok item -> case getName item of Err msg -> Err msg Ok name -> Ok (doSomethingClever id name) https://ideone.com/f4Bzen |
Автор: amk 13.04.18, 15:39 |
Что значит bool? true если a, иначе true если b, иначе false. При проверке условия вместо возврата результата сразу производится переход к нужной ветке. На самом деле вычисление результата тоже производится переходами. То есть фактически в C res = a || b означает res = (a || b)? true: false В Python надо писать res = a or b. В остальном почти верно. При вычислении результата, сразу после определения значения выражения производится переход на точку сохранения результата. |
Автор: Serafim 13.04.18, 21:26 |
Это означает, что там может находиться либо true, либо false и никак иначе. |
Автор: amk 14.04.18, 08:19 |
Но не указано, что если по `a` можно определить ответ, то `b` не анализируется |
Автор: Jin X 14.04.18, 14:40 |
amk, я думаю, это не сложно определить эмпирически |
Автор: Serafim 14.04.18, 15:20 |
Да ладно? Ты уверен? |
Автор: amk 14.04.18, 17:22 |
Уверен. В C это описано стандартом. Так же и в C++, если переменные не являются пользовательскими типами. Но в таком случае не может быть уверенности даже в результирующем значении. Пользовательский operator||() вовсе не обязан возвращать bool |
Автор: OpenGL 14.04.18, 18:28 |
Так практически везде же. |
Автор: Serafim 15.04.18, 09:22 |
Простите, я табличку сарказма потерял |