Однорядковий калькулятор, мистецтво або вада?

Вступна
Як це часто буває, коли Ви шукаєте роботу, Ви проходите одну співбесіду за іншим. Десь Вас вибирають, де Ви. І напевно, в житті кожного з нас бували цікаві співбесіди, про яких можна з задоволенням повідати публіці. Я хочу розповісти про одну таку історію, де є місце емоціям, паніці, потоку мислення та натхнення. Мова в статті піде про внутрішні переживання здобувача, про його протистоянні з інтерв'юером, цікавий і мозговзрывательный код на java, а також відповідь на поставлене питання: 'Незвичайний код — мистецтво або вада?'. Ви зможете зануритися у своє минуле і розім'яти мізки. Якщо заінтригував, тоді поїхали.

Історія однієї людини, X
В далекому 2008 році, хлопець по імені X шукав роботу програмістом. Досвід розробки у нього був, але не такий, коли відривають з руками і ногами. Тому він відволікався на всі вакансії і відповідав на дзвінки. І ось сталося, 'а запросили в серйозну компанію, де йому належало пройти всього 2 співбесіди. Одне, як це водиться, з дівчатами з відділу кадрів, яке втім заваді не стало, а друге — технічне, хвилююче, серце тривожне, невідоме. Настала година X — співбесіда. Після прийнятого рукостискання, молодий чоловік, на ім'я Y, інтерв'юер, справжній програміст — зачіска в бік, джинса подстерлась, сказав, що дуже зайнятий зараз. Ну раз ти прийшов, так і бути, дам тобі хороший ноутбук і задачку 'Напиши мені калькулятор. Простий калькулятор, коли на вхід програми подається вираз, що складається з чисел 2, розділені знаком '+', '-', '*', '/', яке потрібно порахувати. У тебе півтори години.'. І в той момент, сталося щось важливе 'Здивуй мене!', гордовито додав він і пішов. В цю секунду людини X накрив шквал негативних емоцій — 'Ага, ща. Достам АКС 74, 5.45 і змушу тебе танцювати лезгинку і наспівувати Надію Бабкіну — 'Винна я'. У диву то буде, танцюй скільки хочеш… '.
Але емоції і емоції, щоб поступатися місцем здоровому глузду. Грубість — не аргумент. Процес пішов, завирували думки: а може викликати calc.exe, а може ООП навернути, а може офігенний парсер вираження зробити. Але немає. Всього півтори години. Може просто зробити завдання? Як вчинити? Шлях був обраний 'Зроблю як зможу і крапка з комою. Ох вже і постановочка, ох вже і співбесіду'. Хвилин через 20 на обличчі ' а з'явилася посмішка. Його осяяло! А що якщо написати калькулятор, код якого містив би всього 1 рядок, тобто всього 1у крапку з комою не рахуючи пакети і імпорти? Сказано — зроблено. До кінця другої години рішення було готове. 2 години. Прийшов втомлений і трохи замороченный Y. 'Ну як? — запитав він ' а. 'Готово!' — відповів той.

Цікавий і мозговзрывательный код на java
Отже, дорогий читачу прийшов час і нам з Вами спробувати вирішити поставлене, людині X, задачку. Ось більш точне формулювання завдання: Необхідно написати калькулятор для простого виразу, який би містив рівно 1 рядок коду і вмів складати, віднімати, множити і ділити 2 числа. 1 рядок — означає рівне 1 крапку з комою, виключаючи декларацію пакету і импортов. Для вирішення можна використовувати тільки класи з jdk. Приклади вираження«7 + 4», «-12.0 * 14.12» без дужок і будь-яких хитрощів. Рішення потрібно оформити в 1 статичному методі, що виводить консоль результат. Функцію main не чіпати — в ній будуть викликатися функції для перевірки результатів. З обмеженнями, мабуть все. Вітаються будь-які трюки. Оригінальність теж.

Варіанти
В java 7 це робиться досить просто і тут не потрібно бути генієм. Пожертвуємо трохи точністю і безпекою. Клас буду наводити повністю. Якщо хочете подумати тиснути на спойлер не обов'язково.
Варіант номер разів
package com.calculator;

import javax.script.ScriptEngineManager;

import java.io.PrintStream;

public class Calculator1 {

/**
* Найпростіший, але менш точний результат. Що з безпекою?
* @param expression вираз для розрахунку
*/
private static void calc(String expression) {
try {
System.out.println(new ScriptEngineManager().getEngineByName("JavaScript").eval(expression));
} catch (Exception ex) {
try (PrintStream stream = (System.out.append("Nan"))) {}
}
}

public static void main(String[] args) {
calc("+5 + -12");
calc("+5 * -12");
calc("+5 - -12");
calc("+5 / -12");
}
}



У 2008 році такий трюк не пройшов, тому людина X вирішив цю задачу по-своєму. Примітка: код все одно адаптований під java 7, вже вибачте.
Варіант номер два
package com.calculator;

import java.io.PrintStream;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Calculator2 {

/**
* Варіант, який буде запропонований в тому чи іншому вигляді більшістю
* @param expression вираз для розрахунку
* @param args хитрість, до якої варто здогадатися
*/
private static void calc(String expression, Object ... args) {
try {
// 1. Відображаємо результат
System.out.println(
// 2. Шукаємо метод за кодом операції
BigDecimal.class.getMethod(
Arrays.asList("multiply", "add", "subtract", "divide").get(
((args = new Matcher[] {
Pattern.compile(
// 3. Регулярка для аналізу виразу. Зазначу, що регулярка то і <b>не дуже важлива</b>, її можна допилити так як хочете.
"[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:[eE][+-]?\\d+)?\\s*([+-\\\\*/])\\s*[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:[eE][+-]?\\d+)?$")
.matcher(expression)})) != null &&
((Matcher) args[0]).find() ?
// 4. Коди символів основних операцій 42: '*', 43: '+', 45: '-', 47: '/' - проста формула дає індекси 0, 1, 2, 3
((int) ((Matcher) args[0]).group(1).charAt(0) - 41) / 2 : -1),
// 5. Обчислюємо результат
BigDecimal.class, MathContext.class).invoke(
// 6. Перший аргумент пішов
new BigDecimal(((args = new Matcher[] {
Pattern.compile("([+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:[eE][+-]?\\d+)?)").matcher(expression)})) != null &&
((Matcher) args[0]).find() ? ((Matcher) args[0]).group(0) : ""),
// 7. Другий аргумент пішов
new BigDecimal(((args = new Matcher[] {
Pattern.compile("[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:[eE][+-]?\\d+)?\\s*[+-\\\\*/]\\s*([+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:[eE][+-]?\\d+)?)$")
.matcher(expression)})) != null && ((Matcher) args[0]) .find() ? ((Matcher) args[0]).group(1) : ""), new MathContext(10, RoundingMode.HALF_EVEN)));
} catch (Exception ex) {
/** Хитрий трюк сказати користувачу що вираз фігове */
try (PrintStream stream = (System.out.append("Nan"))) {}
}
}

public static void main(String[] args) {
calc("+5 + -12");
calc("+5 * -12");
calc("+5 - -12");
calc("+5 / -12");
}
}


Як у відомій пісні: Ну що сказати, ну що сказати, влаштована так java, бажають знати, бажають знати, бажають знати, що буде…

Але, зізнаємося собі, цей код занадто громіздкий і має повторення. А що якщо посилити обмеження і вимагати не використовувати тернарний оператор зовсім? Не відразу, але рішення все ж знайшлося.
Варіант номер три
package com.calculator;

import java.io.PrintStream;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Calculator3 {

/**
* Варіант, без тернарного оператора, тут потрібно трохи подумати.
* @param expression вираз для розрахунку
* @param args хитрість, до якої варто здогадатися
*/
private static void calc(String expression, Object ... args) {
try {
// 1. Відображаємо результат
System.out.println(
// 2. Шукаємо метод за кодом операції
BigDecimal.class.getMethod(
Arrays.asList("multiply", "add", "subtract", "divide").get(
// 3. Запам'ятовуємо всі необхідні значення в args і дістаємо код операції
(Integer) (args = new Object[] {args = new Object[] {
Pattern.compile("([+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:[eE][+-]?\\d+)?)\\s*([+-\\\\*/])\\s*([+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:[eE][+-]?\\d+)?)$").
matcher(expression)}, args[0], ((Matcher) args[0]).find(), ((Matcher) args[0]).group(1), ((int) ((Matcher) args[0]).group(2).charAt(0) -41) / 2,
((Matcher) args[0]).group(3)})[4]),
// 4. Обчислюємо результат
BigDecimal.class, MathContext.class).invoke(
// 5. Перший аргумент пішов
new BigDecimal(args[3].toString()),
// 6. Другий аргумент пішов
new BigDecimal(args[5].toString()), new MathContext(10, RoundingMode.HALF_EVEN)));
} catch (Exception ex) {
/** Хитрий трюк сказати користувачу що вираз фігове */
try (PrintStream stream = (System.out.append("Nan"))) {}
}
}

public static void main(String[] args) {
calc("+5 + -12");
calc("+5 * -12");
calc("+5 - -12");
calc("+5 / -12");
}
}


Так набагато коротше і без повторень, але мозок ось-ось вибухне. Я думаю знайдеться ще кілька рішень.

Цікаво, а думають хлопці, викладають свої думки на scala або kotlin або c# або ..., якщо зазначені обмеження нехай і з допущеннями — підходять?

Висновок
Спасибі дорогий читачу, за твою увагу і терпіння. Як і обіцяв, даю свою відповідь на поставлене запитання: 'Незвичайний код — мистецтво або вада?'. Я б сказав так: 'Очима експериментатора — мистецтво, очима продакшену — порок'. Але як би такий код не називали, пам'ятай, ти можеш спробувати. Окремо хочу вибачитися перед жителями habrahabr за вибраний стиль викладу, якщо що не так. Це експериментальний з мого боку підхід, дякую за розуміння.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.