Странная языковая особенность
Какая, на ваш взгляд, самая удивительная, странная, странная или действительно "WTF" языковая функция, с которой вы столкнулись?
Пожалуйста, только одна функция в ответе.
320 ответов
В C массивы могут быть проиндексированы следующим образом:
a[10]
что очень распространено.
Тем не менее, менее известная форма (которая действительно работает!):
10[a]
что означает то же самое, что и выше.
В JavaScript:
'5' + 3 gives '53'
В то время как
'5' - 3 gives 2
В JavaScript следующая конструкция
return
{
id : 1234,
title : 'Tony the Pony'
};
возвращаетсяundefined
синтаксическая ошибка из-за скрытой неявной вставки точки с запятой на новой строке после return
, Следующее работает, как и следовало ожидать:
return {
id : 1234,
title : 'Tony the Pony'
};
Еще хуже то, что этот работает также (по крайней мере, в Chrome):
return /*
*/{
id : 1234,
title : 'Tony the Pony'
};
Вот вариант той же самой проблемы, которая не приводит к синтаксической ошибке, просто молча терпит неудачу:
return
2 + 2;
Таблица истинности JavaScript:
'' == '0' // false
0 == '' // true
0 == '0' // true
false == 'false' // false
false == '0' // true
false == undefined // false
false == null // false
null == undefined // true
" \t\r\n" == 0 // true
Источник: Даг Крокфорд
Триграфы в C и C++.
int main() {
printf("LOL??!");
}
Это напечатает LOL|
потому что триграф ??!
преобразуется в |
,
Удовольствие от автоматического бокса и целочисленного кэша в Java:
Integer foo = 1000;
Integer bar = 1000;
foo <= bar; // true
foo >= bar; // true
foo == bar; // false
//However, if the values of foo and bar are between 127 and -128 (inclusive)
//the behaviour changes:
Integer foo = 42;
Integer bar = 42;
foo <= bar; // true
foo >= bar; // true
foo == bar; // true
объяснение
Быстрый взгляд на исходный код Java покажет следующее:
/**
* Returns a <tt>Integer</tt> instance representing the specified
* <tt>int</tt> value.
* If a new <tt>Integer</tt> instance is not required, this method
* should generally be used in preference to the constructor
* {@link #Integer(int)}, as this method is likely to yield
* significantly better space and time performance by caching
* frequently requested values.
*
* @param i an <code>int</code> value.
* @return a <tt>Integer</tt> instance representing <tt>i</tt>.
* @since 1.5
*/
public static Integer valueOf(int i) {
if (i >= -128 && i <= IntegerCache.high)
return IntegerCache.cache[i + 128];
else
return new Integer(i);
}
Замечания: IntegerCache.high
по умолчанию 127
если не установлено свойством.
Что происходит с автобоксом, так это то, что foo и bar одинакового целочисленного объекта извлекаются из кеша, если явно не созданы: например, foo = new Integer(42)
Таким образом, при сравнении ссылочного равенства они будут верными, а не ложными. Правильный способ сравнения целочисленного значения использует .equals;
Цитируя Нила Фрейзера (смотрите в конце этой страницы),
try {
return true;
} finally {
return false;
}
(в Java, но поведение, по-видимому, одинаково в JavaScript и Python). Результат оставлен читателю в качестве упражнения.
РЕДАКТИРОВАНИЕ: Пока мы находимся на предмете, рассмотрите также это:
try {
throw new AssertionError();
} finally {
return false;
}
APL (кроме ВСЕГО), возможность написать любую программу в одну строку.
например , игра жизни Конвея в одной строке в APL:
http://catpad.net/michael/APLLife.gif
Если эта строка не WTF, то ничего нет!
А вот и видео
Можно использовать странные шаблоны C++, лучше всего демонстрируемые "Многомерными аналоговыми литералами", которые используют шаблоны для вычисления площади "нарисованных" фигур. Следующий код является допустимым C++ для прямоугольника 3x3
#include"analogliterals.hpp"
using namespace analog_literals::symbols;
unsigned int c = ( o-----o
| !
! !
! !
o-----o ).area;
Или другой пример с трехмерным кубом:
assert( ( o-------------o
|L \
| L \
| L \
| o-------------o
| ! !
! ! !
o | !
L | !
L | !
L| !
o-------------o ).volume == ( o-------------o
| !
! !
! !
o-------------o ).area * int(I-------------I) );
Много встроенных переменных Perl:
$#
- не комментарий!$0
,$$
, а также$?
- так же, как переменные оболочки с тем же именем$ˋ
,$&
, а также$'
- странные совпадающие переменные$"
а также$,
- странные переменные для разделителей списков и выходных полей$!
- лайкerrno
как число, ноstrerror(errno)
как строка$_
- переменная скрытность, всегда используется и никогда не видела$#_
- порядковый номер последнего аргумента подпрограммы... возможно@_
- (не) имена текущей функции... возможно$@
- последнее возбужденное исключение%::
- таблица символов$:
,$^
,$~
,$-
, а также$=
- что-то делать с выходными форматами$.
а также$%
- номер строки ввода, номер страницы вывода$/
а также$\
- разделители входных и выходных записей$|
- контроллер буферизации вывода$[
- измените базу массива с 0 на 1 и на 42: WHEEE!$}
- вообще ничего, как ни странно!$<
,$>
,$(
,$)
- реальные и эффективные UID и GID@ISA
- имена прямых суперклассов текущего пакета$^T
- время запуска скрипта в секундах эпохи$^O
- текущее имя операционной системы$^V
- что это за версия Perl
Там намного больше, откуда они пришли. Читайте полный список здесь.
PHP обрабатывает числовые значения в строках. Смотрите этот предыдущий ответ на другой вопрос для получения полной информации, но, вкратце:
"01a4" != "001a4"
Если у вас есть две строки, содержащие разное количество символов, их нельзя считать равными. Ведущие нули важны, потому что это строки, а не числа.
"01e4" == "001e4"
PHP не любит строки. Он ищет любое оправдание, которое может найти ваши значения как числа. Немного измените шестнадцатеричные символы в этих строках, и вдруг PHP решит, что это уже не строки, а числа в научной нотации (PHP не заботится о том, что вы использовали кавычки), и они эквивалентны, так как начальные нули игнорируются для чисел. Чтобы подтвердить это, вы обнаружите, что PHP также оценивает "01e4" == "10000"
как истина, потому что это числа с эквивалентными значениями. Это документированное поведение, просто не очень разумное.
Функция "восьмеричное" преобразование JavaScript полезно знать:
parseInt('06') // 6
parseInt('07') // 7
parseInt('08') // 0
parseInt('09') // 0
parseInt('10') // 10
Подробнее здесь.
Давайте проголосуем за все языки (такие как PL/I), которые пытались избавиться от зарезервированных слов.
Где еще вы могли бы легально написать такие забавные выражения, как:
IF IF THEN THEN = ELSE ELSE ELSE = THEN
(IF
, THEN
, ELSE
имена переменных)
или же
IF IF THEN THEN ELSE ELSE
(IF
переменная, THEN
а также ELSE
это подпрограммы)
В C можно сделать do/while с помощью оператора switch. Вот пример memcpy, использующего этот метод:
void duff_memcpy( char* to, char* from, size_t count ) {
size_t n = (count+7)/8;
switch( count%8 ) {
case 0: do{ *to++ = *from++;
case 7: *to++ = *from++;
case 6: *to++ = *from++;
case 5: *to++ = *from++;
case 4: *to++ = *from++;
case 3: *to++ = *from++;
case 2: *to++ = *from++;
case 1: *to++ = *from++;
}while(--n>0);
}
}
Алгол передачи по имени (показано с использованием синтаксиса C):
int a[3] = { 1, 2, 3 };
int i = 1;
void f(int j)
{
int k;
k = j; // k = 2
i = 0;
k = j; // k = 1 (!?!)
}
int main()
{
f(a[i]);
}
В Java:
int[] numbers() {
return null;
}
Может быть написано как:
int numbers() [] {
return null;
}
В Python:
>>> x=5
>>> 1<x<10
True
>>> 1<x<3
False
Не WTF, но полезная функция.
INTERCAL, пожалуй, лучший сборник самых странных языковых возможностей. Мой личный фаворит - это утверждение COMEFROM, которое (почти) противоположно GOTO.
COMEFROM примерно противоположен GOTO в том смысле, что он может переносить состояние выполнения из любой произвольной точки кода в оператор COMEFROM. Точка в коде, где происходит передача состояния, обычно указывается в качестве параметра COMEFROM. Происходит ли перевод до или после инструкции в указанном пункте передачи, зависит от используемого языка. В зависимости от используемого языка несколько COMEFROM, ссылающихся на одну и ту же точку отправления, могут быть недопустимыми, быть недетерминированными, выполняться с определенным приоритетом или даже вызывать параллельное или иным образом параллельное выполнение, как это видно в многопоточном интеркале. Простым примером оператора "COMEFROM x" является метка x (которая не обязательно должна быть физически расположена где-то рядом с его соответствующим COMEFROM), которая действует как "дверь-ловушка". Когда выполнение кода достигает метки, управление передается в оператор после COMEFROM. Результатом этого является прежде всего затруднение отладки (и понимания потока управления программой), поскольку рядом с меткой нет указания, что управление таинственным образом перейдет в другую точку программы.
Не совсем языковая особенность, но недостаток реализации: некоторые ранние компиляторы Фортрана реализовывали константы, используя постоянный пул. Все параметры были переданы по ссылке. Если вы вызвали функцию, например
f(1)
Компилятор передал бы адрес константы 1 в пуле констант функции. Если вы присвоили значение параметру в функции, вы бы изменили значение (в данном случае значение 1) глобально в программе. Вызвало некоторые царапины на голове.
Не знаю, можно ли это считать языковой функцией, но в C++ почти любая ошибка компилятора, связанная с шаблонами, ежедневно доставляет немало WTF многим программистам C++ по всему миру:)
Я бы сказал, что вся штука с пробелами в Python - моя лучшая особенность WTF. Правда, через некоторое время вы более или менее привыкаете к этому, и современные редакторы позволяют легко с этим справляться, но даже после большей части разработки Python в течение полного рабочего дня я все еще убежден, что это плохая идея. Я прочитал все причины этого, но, честно говоря, это мешает моей продуктивности. Не очень, но это все еще заусенец под седлом.
редактировать: судя по комментариям, некоторые люди думают, что я не люблю делать отступы в своем коде. Это неверная оценка. Я всегда смещал свой код независимо от того, на каком языке и был ли я вынужден или нет. Что мне не нравится, так это то, что именно отступ определяет блок, в котором находится строка кода. Я предпочитаю для этого явные разделители. Среди прочих причин я обнаружил, что явные разделители облегчают вырезание и вставку кода.
Например, если у меня есть блок с отступом 4 пробела и вставьте его в конец блока с отступом 8 пробелов, мой редактор (все редакторы?) Понятия не имеет, принадлежит ли вставленный код блоку из 8 пробелов или внешнему блок. OTOH, если у меня есть явные разделители, становится очевидным, к какому блоку принадлежит код и как он должен быть (пере) отступ, - это происходит с помощью интеллектуального поиска разделителей блоков.
редактировать 2: некоторые люди, которые предоставляют комментарии, кажется, думают, что это функция, которую я ненавижу или которая делает Python плохим языком. Опять не правда. Хотя мне это не очень нравится, это не относится к делу. Вопрос о странной языковой функции, и я думаю, что это странно, поскольку она используется очень, очень немногими (но>0) языками.
Много пространств имен C:
typedef int i;
void foo()
{
struct i {i i;} i;
i: i.i = 3;
printf( "%i\n", i.i);
}
Или с персонажами:
typedef char c;
void foo()
{
struct c {c c;} c;
c: c.c = 'c';
printf( "%c\n", c.c);
}
Я немного боролся по этому поводу:
1;
В Perl модули должны возвращать что-то истинное.
Я удивлен, что никто не упомянул 7 циклических конструкций Visual Basic.
For i As Integer = 1 to 10 ... Next
While True ... End While
Do While True ... Loop
Do Until True ... Loop
Do ... Loop While True
Do ... Loop Until True
While True ... Wend
Потому что наклеить! перед твоим условием это слишком сложно!
Я всегда задавался вопросом, почему самая простая программа была:
class HelloWorldApp {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
Тогда как это может быть:
print "Hello World!"
Может быть, это пугает студентов по информатике в первую очередь...
Для тех, кто не знает, bc
является "языком калькулятора произвольной точности", и я использую его довольно часто для быстрых вычислений, особенно когда задействованные числа большие ($
это подсказка):
$ bc -lq
12^345
20774466823273785598434446955827049735727869127052322369317059031795\
19704325276892191015329301807037794598378537132233994613616420526484\
93077727371807711237016056649272805971389591721704273857856298577322\
13812114239610682963085721433938547031679267799296826048444696211521\
30457090778409728703018428147734622401526422774317612081074841839507\
864189781700150115308454681772032
bc
долгое время была стандартной командой Unix.
Теперь о "функции WTF". Это из man bc
(выделение мое):
quit: при прочтении оператора quit процессор bc завершается независимо от того, где находится оператор quit. Например, "if (0 == 1) quit" приведет к завершению bc.
halt: оператор halt (расширение) является оператором execute, который заставляет процессор bc завершать работу только при его выполнении. Например, "if (0 == 1) halt" не приведет к завершению bc, поскольку останов не выполняется.
JavaScript является объектно-ориентированным, верно? Таким образом, методы работы с буквенными строками и числами должны работать. подобно "hello".toUpperCase()
а также 3.toString()
, Оказывается, что вторая ошибка синтаксиса, почему? Поскольку синтаксический анализатор ожидает, что число, сопровождаемое точкой, является литералом с плавающей запятой. Это не WTF, WTF в том, что вам нужно только добавить еще одну точку, чтобы она заработала:
3..toString()
Причина в том, что буквальное 3.
интерпретируется как 3.0
, а также 3.0.toString()
работает отлично.
В JavaScript:
2 == [2]
// Even stranger
2 == [[[2]]]
// And down-right nutty
var a = { "abc" : 1 };
a[[[["abc"]]]] === a["abc"]; // this is also true
К счастью, добрые люди из stackru.com объяснили мне все это: почему 2 == [2] в JavaScript?
Моя самая большая ненавистная особенность - любой синтаксис файла конфигурации, который включает условную логику. Подобные вещи распространены в мире Java (Ant, Maven и т. Д. Вы знаете, кто вы есть!).
Вы просто заканчиваете программирование на a c**p языке, с ограниченной отладкой и ограниченной поддержкой редактора.
Если вам нужна логика в вашей конфигурации, "Pythonic" подход кодирования конфигурации на реальном языке будет намного лучше.
powerbasic (www.powerbasic.com) включает директиву компилятора:
# BLOAT {bloatsize}
это увеличивает размер скомпилированного исполняемого файла на <bloatsize>
байт. это было помещено в компилятор на тот случай, если людям, создающим исполняемый файл, не нравится маленький размер сгенерированного исполняемого файла. это заставляет EXE казаться больше, чтобы конкурировать с раздутыми языками программирования:)