python

Вредные заклинания в программировании

  • четверг, 28 декабря 2017 г. в 03:12:33
https://habrahabr.ru/post/345690/
  • Программирование
  • Ненормальное программирование
  • Python
  • Java
  • C


С тех пор, как я посмотрел легендарное видео Wat Гэри Бернхардта, меня завораживает странное поведение некоторых языков программирования. Некоторые из них таят больше сюрпризов, чем другие. Например, для Java написана целая книга с описанием пограничных ситуаций и странной специфики. Для C++ вы просто можете почитать сами спецификации всего за $200.

Далее поделюсь с вами моей коллекцией самых неожиданных, забавных и всё-таки валидных «заклинаний» программирования. По сути, использование этих особенностей поведения ЯП считается пагубным, поскольку ваш код никоим образом не должен быть непредсказуемым. Хорошо, что многие линтеры уже осведомлены и готовы посмеяться над вами, если попробуете какое-то из перечисленных дурачеств. Но как говорится, знание — сила, так что начнём.

Вражеское переназначение True в Python 2


Рифмуется с true, так что вы знаете, что это poo («какашка»).

>>> True = False
>>> True
False

К счастью, такой код выводит SyntaxError в версии Python 3, поскольку True, False и None теперь стали зарезервированными словами. Такая шалость всё-таки далека от подлости в C++, когда вы вставляете #define true false в стандартный заголовочный файл на рабочей машине коллеги.

Призрачное взаимодействие с объектом в Java и Python


Семантика == часто озадачивает начинающих Java-программистов, но ещё более усложняет ситуацию непостоянство оператора даже в тривиальных ситуациях, пусть это и сделано для производительности.

Integer a = 100;
Integer b = 100;
System.out.print(a == b); // prints true

Integer c = 200;
Integer d = 200;
System.out.print(c == d); // prints false

JVM использует однотипный справочник для значений в диапазоне [-128, 127]. Что ещё более странно, так это соответствующее поведение Python.

>>> x = 256
>>> y = 256
>>> x is y
True

>>> x = 257
>>> y = 257
>>> x is y
False

Пока ничего слишком удивительного.

>>> x = -5
>>> y = -5
>>> x is y
True

>>> x = -6
>>> y = -6
>>> x is y
False

Похоже, нижний предел для интерпретатора Python такой же… -5. Целые числа в диапазоне [-5, 256] получают одинаковые ID. Но всё равно это работает как-то странно.

>>> x = -10
>>> y = -10
>>> x is y
False
>>> x, y = [-10, -10]
>>> x is y
True

Видимо, применение деструктурирующего присваивания сразу меняет правила. Я не уверен, почему так происходит, и даже задал вопрос на Stack Overflow в попытке разобраться. Может быть, повторяющиеся значения в списке указывают на тот же объект для экономии памяти.

Обратная запись с индексом в C


Обратная запись с индексом мгновенно доставляет головную боль любому разработчику.

int x[1] = { 0xdeadbeef };

printf("%x\n", 0[x]); // prints deadbeef

Причина работы такого кода в том, что array[index] на самом деле просто синтаксический сахар для *(array + index). Благодаря коммутативному свойству сложения можно поменять их местами и получить тот же результат.

Оператор «перехода» в C


На первый взгляд оператор --> выглядит как синтаксическая ошибка. Но когда вы понимаете, что он нормально компилируется, то начинаете думать, что это недокументированная функция языка. К счастью, это ни то, ни другое.

for (x = 3; x --> 0;) {
    printf("%d ", x); // prints 2 1 0
}

«Оператор» --> — это на самом деле два оператора, которые в этом контексте разбираются как (x--) > 0. Известно, что такая штука вызывает немалую путаницу при использовании в продакшне — чистое зло.

Оператор sizeof в C


Оператор sizeof обрабатывается в процессе компиляции, что даёт ему интересные свойства.

int x = 0;
sizeof(x += 1);
if (x == 0) {
    printf("wtf?"); // this will be printed
}

Поскольку объекты оператора sizeof анализируются в процессе компиляции, то выражение (x += 1) никогда не будет запущено. Также любопытно: исследования показывают, что printf("wtf?") — самая популярная строчка кода, которая никогда не поступает в продакшн.

Начало индексов с единицы в Lua, Smalltalk, MATLAB и др…


На форумах /r/programminghumor полно мемов об «индексах, которые начинаются с единицы». Поразительно, но немало языков программирования в реальности используют 1-индексированные массивы. Более полный список см. здесь.

0 соответствует true в Ruby


…и только в Ruby. *

if 0 then print 'thanks, ruby' end # prints thanks, ruby

* правка: В обсуждении Reddit мне указали, что такое справедливо также для Lua, Lisp и Erlang.

Триграфы, диграфы и токены в C


По историческим причинам в C остались альтернативные варианты написания для нецифробуквенных символов.

Триграф Символ Диграф Символ Токен Символ
??= # <: [ %:%: ##
??/ \ :> ] compl ~
??' ^ <% { not !
??( [ %> } bitand &
??) ] %: # bitor |
??! | and &&
??< { or ||
??> } xor ^
??- ~ and_eq &=
or_eq |=
xor_eq ^=
not_eq !=

if (true and true) { // same as if (true && true)
    printf("thanks, c");
}

Некоторое чужеродное оборудование вроде IBM 3270 не позволяло набрать некоторые часто используемые символы в C/C++, так что ввели использование диагрфы, триграфы и токены, чтобы сохранить совместимость с определёнными кодировками.

Надеюсь, статья была интересной. Можете почитать обсуждение на Reddit.