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

Если сегодня попросить любого уважающего себя разработчика ПО реализовать простой кнопочный калькулятор (но с обратной совместимостью), он, наверно, закатит глаза и скажет, что сможет это сделать за один обеденный перерыв. Но он потерпит неудачу. Я точно знаю это, ведь когда-то я разработал калькулятор с моим дизайном, и это приключение оказалось очень непростым.
Давайте начнём с базы: простейшего калькулятора с десятью цифровыми кнопками, точкой десятичного разделителя, четырьмя арифметическими операциями (+, -, ×, ÷), кнопкой результата (=) и кнопкой сброса результата ©. Калькулятор последовательно выполняет арифметические операции без учёта приоритета. Например:
Мы с лёгкостью можем представить реализацию такого поведения с тремя переменными: регистра ввода, аккумулятора и селектора оператора. Ввод каждой новой цифры сдвигает содержимое регистра ввода на один десятичный разряд вправо. Выбор оператора загружает это значение в аккумулятор и освобождает место для второго операнда. В конце пользователь нажимает "="; после этого вычисляется результат и значение записывается обратно в регистр ввода, делая его доступным для любых последующих операций:
Но… что произойдёт, если пользователь нажмёт 9 сразу после знака равенства? Вероятно, результатом должно стать «159». Нам нужен особый случай: если цифру или точку десятичного разделителя нажимают сразу после какого-то нечислового ввода, то регистр ввода должен быть сброшен. Мы можем исправить это дополнительным флагом «сброс ввода»:
Дополнительное преимущество этой новой схемы заключается в том, что на этапе 4 мы можем дольше сохранять на экране ранее введённый операнд, чтобы было проще находить ошибки ввода данных.
Вроде всё готово?
Но… что, если пользователь введёт 2 + 3 × 5 =? В описанной выше схеме мы отбросим первое сложение и выполним только 3 × 5 =. Чтобы учесть эту проблему, нам нужен ещё один особый случай: если операция уже выбрана, мы должны неявно нажимать "=" перед планированием следующей.
А что, если пользователь вводит 2 × ÷ 3? Наверно, он не имел в виду 2 × 2 ÷ 3! Самое логичное объяснение: он случайно нажал не тот оператор и пытается исправить эту ошибку. То есть нам нужен ещё один особый случай: этот неявный "=" должен срабатывать, только если в промежутке происходил какой-то числовой ввод, то есть когда не задан флаг InR.
Но справедливо ли то же объяснение для 2 × — 3? Это попытка вычитания или пользователь хочет умножить на отрицательное число? Можно только догадываться, но в любом случае неплохо было бы, если бы могли вводить отрицательные числа! Некоторые калькуляторы решают проблему такой неоднозначности отдельной кнопкой знака; многие этого не делают, вместо этого поддерживая запись унарного суффикса непосредственно перед "=":
Унарный оператор — это хитрый хак, но в дальнейшем он ещё больше запутывает UX. Во-первых, он меняет знак всего выражения, а не второго операнда; в случае сложения и вычитания результаты будут противоположны ожидаемым:
Некоторые пользователи могут ожидать, что это будет работать иначе: 10 + -2 = 8.
И проблемы на этом не кончаются; по пока имеющимся у нас правилам, 2 — × 2 = обрабатывается как исправление оператора, а не как смена знака, так что мы получим 4 вместо -4. Действительно ли этого хотел пользователь? Вероятность пятьдесят на пятьдесят.
Посмотрим ещё и на то, что произойдёт, если пользователь введёт 2 +, заметит, что последний операнд («2») всё ещё отображается на экране, и нажмёт "=":
Логично будет предположить, что мы складываем 2 и 2. Но здесь происходит другое. Мы улучшили или ухудшили ситуацию?…
Если вы когда-нибудь баловались со старым калькулятором, то могли заметить «фичу», позволяющую бесконечно зациклить последнюю операцию многократным нажатием на "=":
Этот механизм увлекателен, но довольно бесполезен, если только вы не возводите в степень вручную. Но на этом история ещё не заканчивается: на самом деле это побочный эффект малоизвестной фичи «K-константа», сохраняющей один из ранее введённых операндов и оператор, позволяя изменять другой операнд. Эта функция была придумана для вычисления процентов и доплат, например:
В первой строке мы задаём постоянную доплату («10») и выбираем операцию ("+"). В дальнейшем мы можем просто продолжать вводить второй операнд и нажимать «поехали». Удобно, правда? Но это значит, что наш исходный алгоритм снова оказался ошибочным: аккумулятор и селектор оператора невозможно сбросить после "=".
В этой схеме второй операнд используется как элемент-константа для выполнения деления, сложения и вычитания; но по историческим причинам умножение работает по другим правилам:
И это… приводит к довольно забавным последствиям, если мы просто жмём кнопку "=" и ожидаем согласованного UX:
А, да, кстати: помните запись «унарного суффикса» для смены знака? Во многих вычислениях существует ещё один особый случай для оператора деления (но не для умножения). В результате получается вот такой перл юзабилити:
Унарный оператор деления даёт нам обратное значение. Также он позволяет обратить обычный порядок деления:
Стоит помнить, что этот пользовательский интерфейс со всеми его тонкостями и неочевидностями стал результатом десятков лет эволюции продуктов и исследований рынка. У некоторых из первых калькуляторов 1960-х и 1970-х схемы ввода были ещё более запутанными. Посмотрим, сможете ли вы разобраться в одной из жемчужин моей коллекции — Sharp EL-8:

Да, у него есть кнопки. Нет, он не поддерживает двойное касание или долгое нажатие.
Автор: nmgtech