Калькуляторы с четырьмя функциями, или Ад UI

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

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

Обычный калькулятор с ЖК-экраном
Обычный калькулятор с ЖК-экраном

Если сегодня попросить любого уважающего себя разработчика ПО реализовать простой кнопочный калькулятор (но с обратной совместимостью), он, наверно, закатит глаза и скажет, что сможет это сделать за один обеденный перерыв. Но он потерпит неудачу. Я точно знаю это, ведь когда-то я разработал калькулятор с моим дизайном, и это приключение оказалось очень непростым.

Давайте начнём с базы: простейшего калькулятора с десятью цифровыми кнопками, точкой десятичного разделителя, четырьмя арифметическими операциями (+, -, ×, ÷), кнопкой результата (=) и кнопкой сброса результата ©. Калькулятор последовательно выполняет арифметические операции без учёта приоритета. Например:

$\begin{array}{l l l l l l l l} 1 & + & 2 & × & 3 & = & \color{steelblue}{\fbox{_____9}} & ((1+2)×3) \\ \end{array}$

Мы с лёгкостью можем представить реализацию такого поведения с тремя переменными: регистра ввода, аккумулятора и селектора оператора. Ввод каждой новой цифры сдвигает содержимое регистра ввода на один десятичный разряд вправо. Выбор оператора загружает это значение в аккумулятор и освобождает место для второго операнда. В конце пользователь нажимает "="; после этого вычисляется результат и значение записывается обратно в регистр ввода, делая его доступным для любых последующих операций:

$\begin{array}{|l|l|l|} \hline \textbf{Нажатая кнопка} & \textbf{Регистр ввода} & \textbf{Аккумулятор} & \textbf{Выбор опер.} \\ \hline \textrm{C} & \color{steelblue}{clear} & \color{steelblue}{clear} & \color{steelblue}{clear} \\ 1 & \color{crimson}{1} & 0 & \\ 2 & \color{crimson}{12} & 0 & \\ + & \color{steelblue}{clear} & \color{crimson}{12} \ (load) & \color{crimson}{+} \\ 3 & \color{crimson}{3} & 12 & + \\ = & \color{crimson}{15} & \color{steelblue}{clear} & \color{steelblue}{clear} \\ \hline \end{array}$

Но… что произойдёт, если пользователь нажмёт 9 сразу после знака равенства? Вероятно, результатом должно стать «159». Нам нужен особый случай: если цифру или точку десятичного разделителя нажимают сразу после какого-то нечислового ввода, то регистр ввода должен быть сброшен. Мы можем исправить это дополнительным флагом «сброс ввода»:

$\begin{array}{|l|l|l|} \hline \textbf{Нажатая кнопка} & \textbf{Регистр ввода} & \textbf{Аккумулятор} & \textbf{Флаги} \\ \hline \textrm{C} & \color{steelblue}{clear} & \color{steelblue}{clear} & \color{steelblue}{clear} \\ 1 & \color{crimson}{1} & 0 & \\ 2 & \color{crimson}{12} & 0 & \\ + & 12 & \color{crimson}{12} \ (load) & \color{crimson}{+, InR} \\ 3 & \color{crimson}{3} & 12 & \color{crimson}{+} \\ = & \color{crimson}{15} & \color{steelblue}{clear} & \color{crimson}{InR} \\ 9 & \color{crimson}{9} & 0 & \color{steelblue}{clear} \\ \hline \end{array}$

Дополнительное преимущество этой новой схемы заключается в том, что на этапе 4 мы можем дольше сохранять на экране ранее введённый операнд, чтобы было проще находить ошибки ввода данных.

Вроде всё готово?

Но… что, если пользователь введёт 2 + 3 × 5 =? В описанной выше схеме мы отбросим первое сложение и выполним только 3 × 5 =. Чтобы учесть эту проблему, нам нужен ещё один особый случай: если операция уже выбрана, мы должны неявно нажимать "=" перед планированием следующей.

А что, если пользователь вводит 2 × ÷ 3? Наверно, он не имел в виду 2 × 2 ÷ 3! Самое логичное объяснение: он случайно нажал не тот оператор и пытается исправить эту ошибку. То есть нам нужен ещё один особый случай: этот неявный "=" должен срабатывать, только если в промежутке происходил какой-то числовой ввод, то есть когда не задан флаг InR.

Но справедливо ли то же объяснение для 2 × — 3? Это попытка вычитания или пользователь хочет умножить на отрицательное число? Можно только догадываться, но в любом случае неплохо было бы, если бы могли вводить отрицательные числа! Некоторые калькуляторы решают проблему такой неоднозначности отдельной кнопкой знака; многие этого не делают, вместо этого поддерживая запись унарного суффикса непосредственно перед "=":

$\begin{array}{l l l l l l c r} 1 & 0 & × & 2 & + & = & \color{steelblue}{\rightarrow} \ & 20 \\ 1 & 0 & × & 2 & - & = & \color{steelblue}{\rightarrow} \ & -20 \end{array}$

Унарный оператор — это хитрый хак, но в дальнейшем он ещё больше запутывает UX. Во-первых, он меняет знак всего выражения, а не второго операнда; в случае сложения и вычитания результаты будут противоположны ожидаемым:

$\begin{array}{l l l l l l c r l} 1 & 0 & + & 2 & - & = & \color{steelblue}{\rightarrow} \ & -12 & (-(10+2)) \end{array}$

Некоторые пользователи могут ожидать, что это будет работать иначе: 10 + -2 = 8.

И проблемы на этом не кончаются; по пока имеющимся у нас правилам, 2 — × 2 = обрабатывается как исправление оператора, а не как смена знака, так что мы получим 4 вместо -4. Действительно ли этого хотел пользователь? Вероятность пятьдесят на пятьдесят.

Посмотрим ещё и на то, что произойдёт, если пользователь введёт 2 +, заметит, что последний операнд («2») всё ещё отображается на экране, и нажмёт "=":

$\begin{array}{l l l l c r} 2 & + & \color{steelblue}{\fbox{_____2}} & = & \color{steelblue}{\rightarrow} \ & 2 \\ \end{array}$

Логично будет предположить, что мы складываем 2 и 2. Но здесь происходит другое. Мы улучшили или ухудшили ситуацию?…

Если вы когда-нибудь баловались со старым калькулятором, то могли заметить «фичу», позволяющую бесконечно зациклить последнюю операцию многократным нажатием на "=":

$\begin{array}{l l l l c r} 2 & + & 1 & = & \color{steelblue}{\rightarrow} \ & 3 \\ 2 & + & 1 & = \ = & \color{steelblue}{\rightarrow} \ & 4 \\ 2 & + & 1 & = \ = \ = & \color{steelblue}{\rightarrow} \ & 5 \\ ... \end{array}$

Этот механизм увлекателен, но довольно бесполезен, если только вы не возводите в степень вручную. Но на этом история ещё не заканчивается: на самом деле это побочный эффект малоизвестной фичи «K-константа», сохраняющей один из ранее введённых операндов и оператор, позволяя изменять другой операнд. Эта функция была придумана для вычисления процентов и доплат, например:

$\begin{array}{l l l l l c r l} 2 & + & \color{crimson}{1} & \color{crimson}{0} & = & \color{steelblue}{\rightarrow} \ & 12 & \\ 5 & & & & = & \color{steelblue}{\rightarrow} \ & 15 & (5 + \color{crimson}{10}) \\ 0 & & & & = & \color{steelblue}{\rightarrow} \ & 10 & (0 + \color{crimson}{10}) \end{array}$

В первой строке мы задаём постоянную доплату («10») и выбираем операцию ("+"). В дальнейшем мы можем просто продолжать вводить второй операнд и нажимать «поехали». Удобно, правда? Но это значит, что наш исходный алгоритм снова оказался ошибочным: аккумулятор и селектор оператора невозможно сбросить после "=".

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

$\begin{array}{l l l l c r l} \color{crimson}{2} & × & 5 & = & \color{steelblue}{\rightarrow} \ & 10 & \\ & & 1 & = & \color{steelblue}{\rightarrow} \ & 2 & (\color{crimson}{2} × 1)\\ & & 3 & = & \color{steelblue}{\rightarrow} \ & 6 & (\color{crimson}{2} × 3) \end{array}$

И это… приводит к довольно забавным последствиям, если мы просто жмём кнопку "=" и ожидаем согласованного UX:

$\begin{array}{l l l l c r l} 2 & + & 1 & = & \color{steelblue}{\rightarrow} \ & 3 & \\ 2 & + & 1 & = \ = & \color{steelblue}{\rightarrow} \ & 4 & (2 + 1 + 1) \\ 2 & + & 1 & = \ = \ = & \color{steelblue}{\rightarrow} \ & 5 & (2 + 1 + 1 + 1) \\ & & & ...но... & & \\ 3 & × & 2 & = & \color{steelblue}{\rightarrow} \ & 6 \\ 3 & × & 2 & = \ = & \color{steelblue}{\rightarrow} \ & 18 & (3 × 2 × \color{crimson}{3})\\ 3 & × & 2 & = \ = \ = & \color{steelblue}{\rightarrow} \ & 54 & (3 × 2 × \color{crimson}{3 × 3})\\ \end{array}$

А, да, кстати: помните запись «унарного суффикса» для смены знака? Во многих вычислениях существует ещё один особый случай для оператора деления (но не для умножения). В результате получается вот такой перл юзабилити:

$\begin{array}{l l l l c r} 2 & + & \color{steelblue}{\fbox{_____2}} & = & \color{steelblue}{\rightarrow} \ & 2 \\ 2 & × & \color{steelblue}{\fbox{_____2}} & = & \color{steelblue}{\rightarrow} \ & 4 \\ 2 & ÷ & \color{steelblue}{\fbox{_____2}} & = & \color{steelblue}{\rightarrow} \ & \color{crimson}{0.5} \\ \end{array}$

Унарный оператор деления даёт нам обратное значение. Также он позволяет обратить обычный порядок деления:

$\begin{array}{l l l l c r l} 2 & ÷ & 6 & ÷ & = & \color{steelblue}{\rightarrow} \ & 3 & (6/2) \\ \end{array}$

Стоит помнить, что этот пользовательский интерфейс со всеми его тонкостями и неочевидностями стал результатом десятков лет эволюции продуктов и исследований рынка. У некоторых из первых калькуляторов 1960-х и 1970-х схемы ввода были ещё более запутанными. Посмотрим, сможете ли вы разобраться в одной из жемчужин моей коллекции — Sharp EL-8:

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

Источник: https://habr.com/ru/companies/nmg/articles/813879/


Интересные статьи

Интересные статьи

Большинство крупных языковых моделей, используемых сегодня во всем мире, недостаточно ориентированы на азиатские страны, оставляя без внимания огромные группы населения и языки. Такие страны, как Синг...
Сегодня мы опубликовали на GitHub под открытой лицензией Apache 2.0 исходный код Yandex DataLens — сервиса для анализа и визуализации данных. Теперь использовать опенсорс-версию DataLens может любой ж...
Добавляем функционал быстрых ответов на любом тарифе Битрикс24Как добавить одну из самых полезных функций для чатов открытых линий для любого коммерческого тарифа? Все очень просто, нужно лишь..... ...
В 1С-Битрикс: Управление сайтом (как и в Битрикс24) десятки, если не сотни настраиваемых типов данных (или сущностей): инфоблоки, пользователи, заказы, склады, форумы, блоги и т.д. Стр...
Если Вы используете в своих проектах инфоблоки 2.0 и таблицы InnoDB, то есть шанс в один прекрасный момент столкнуться с ошибкой MySQL «SQL Error (1118): Row size too large. The maximum row si...