Одним из первых предметов в любом курсе программирования являются структуры данных и типы данных, которые они могут хранить. Это немного абстракция для непосвященных. В школе мы узнали о числах как о точках спектра в диапазоне от бесконечности до минус бесконечности с такой точностью, какая вам нужна. Логические понятия, такие как истина или ложь, равенство и неравенство, — это только те понятия, которые нам предстоит понять, но не более того.

Компьютеры обрабатывают все это по-разному. Числа, например, могут быть целыми числами (целые числа) или значениями с плавающей запятой (десятичные числа). И поскольку число должно быть помещено в дискретное место в памяти, диапазон значений, которые может иметь число, ограничен объемом памяти, которое оно занимает. Целые числа также могут быть со знаком и без знака. Кроме того, true и false становятся больше, чем просто понятием, они становятся двумя значениями логического типа данных.

Зачем мне все это? Ну, если я собираюсь заставить компьютер делать то, что я от него ожидаю, я должен понять, как компьютер обрабатывает данные, которые я ввожу в свои программы. Картина еще больше усложняется тем, что разные языки программирования по-разному обрабатывают одни и те же значения!

Странности JavaScript

Основываясь на уроках математики в средней школе и даже в колледже, мы не ожидаем выражения вроде:

true + true

иметь какой-либо смысл. Тем не менее, в JavaScript он оценивается как 2! Как это может произойти? Это происходит потому, что значения True и False хранятся в памяти как 1 и 0, поэтому:

true + true = 1 + 1 = 2

В бинарном мире имеет смысл хранить true и false как 1 и 0. Но большинство языков скрывают это от программиста и сообщают о какой-то ошибке, если вы пытаетесь использовать true или false в математическом выражении.

Java тоже может делать странные вещи

Другой пример того, как значение сохраняется, вызывая неожиданные побочные эффекты, — передача этого в Java:

jshell> 2147483647 + 12
$1 ==> -2147483637

Что! Как получить отрицательное число, сложив два положительных числа? Ну, 2147483647 — это максимальное значение, которое может содержать целое число Java. Но так как Java Integer представляет собой значение со знаком, хранящееся в 32 битах. Первый бит определяет знак числа, а остальные 31 бит определяют его значение. Таким образом, сложение двух чисел вместе, чтобы в сумме получить более 2147483647, переключает первый бит ячейки памяти с 0 (положительный) на 1 (отрицательный), в результате чего сложение двух положительных чисел может привести к отрицательному результату.

Clojure кажется довольно предсказуемым

В школе мы все узнали о порядке операций, и Clojure заставляет нас думать об этом своей структурой операторов.

(+ 1 1) 

Эквивалент Clojure ‘

1 + 1

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

user=> (/ (* (- 42 (+ 8 24)) (/ (+ 7 (+ 4 3)) 2)) 8)
35/4
user=> (/ (* (- 42 (+ 8 24)) (/ (+ 7 (+ 4 3)) 2)) 8.0)
8.75
user=> (/ (* (- 42 (+ 8 24)) (/ (+ 7 (+ 4.5 2.5)) 2)) 8)
8.75

Если бы вы оценивали каждый из них вручную, вы бы ожидали ответа 8,75, который дали последние две попытки. Почему первый отличается? Обратите внимание, что во втором операторе последним значением является 8.0 вместо 8. Включение «.0» делает 8.0 значением с плавающей запятой и заставляет Clojure оценивать все выражение как значение с плавающей запятой. На первый взгляд кажется, что последнее выражение было изменено достаточно, чтобы дать другой результат. Но надо остановиться и подумать, какое значение 4,5+2,5. Возвращаясь теперь к первому выражению, обратите внимание, что оно не имеет значений с плавающей запятой. Он полностью состоит из целочисленных значений, поэтому Clojure оценивает его как целочисленное выражение.

Вывод

Таким образом, каждый компьютерный язык имеет некоторые странности или неожиданные результаты. но когда вы разбираете выражение, сгенерировавшее неожиданный результат, и внимательно смотрите на используемый тип данных, вы почти всегда можете объяснить, почему выражение было вычислено именно так, а не так, как вы ожидали.