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

в 10:45, , рубрики: UX, интерфейсы, История ИТ, калькуляторы, математика

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

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

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

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

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

$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:

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

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

Автор: nmgtech

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js