Java почему то не видит изменения при вводе римских цифр

Рейтинг: -1Ответов: 3Опубликовано: 21.08.2023

Написала код, с арабскими цифрами работает корректно, но на римские цифры ругается, пишет что не те числа введены

 import java.util.Scanner;
public class Main {
    public static void main(String[] args) throws TrowExeprion {
        Scanner in = new Scanner(System.in);
        System.out.println("Введите пример, где значения вводятся через пробел");
        String calc = in.nextLine();
        String[] c = calc.split(" ");
        String[] c1 = {"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X"};
        String num1 = c[0];
        String num2 = c[2];
        int k1=0;
        int k2=0;
        int first = 0;
        int second = 0;
        int x1 = 0;
        String x2 = "0";
        if (num1.equals("1") |num1.equals("2") | num1.equals("3") | num1.equals("4") | num1.equals("5") |num1.equals("6") | num1.equals("7") | num1.equals("8") | num1.equals("9") | num1.equals("10") ){
            first = Integer.valueOf(num1);
        }
        else {
            for (int i = 0; i<c1.length; i++) {
                if (num1.equals(c1[i])) {
                    first = i + 1;
                    k1 = 1;
                }
            }
        }
        if (num2.equals("1") |num2.equals("2") | num2.equals("3") | num2.equals("4") | num2.equals("5") |num2.equals("6") | num2.equals("7") | num2.equals("8") | num2.equals("9") | num2.equals("10") ){
            second = Integer.valueOf(num2);
        }
        else {
            for (int i = 0; i<c1.length; i++) {
                if (num2.equals(c1[i])) {
                    first = i + 1;
                    k2 = 1;
                }
            }
        }

        int x = 0;
        if ((k1 == 0) && (k2 == 0)){
            switch (c[1]){
                case "+" :
                    x = first +second;
                    break;
                case "-" :
                    x = first - second;
                    break;
                case "*":
                    x = first * second;
                    break;
                case "/":
                    x = first / second;
                    break;
            }
        }
        else if ((k1 == 0) && (k2 == 1)){
            throw new TrowExeprion();
        }
        else if ((k1 == 1) && (k2 == 0)){
            throw new TrowExeprion();
        }
        else if ((k1 == 1) && (k2 == 1) && (first<=second)){
            throw new TrowExeprion();
        }
        else if ((k1 == 1) && (k2 == 1) && (first > second)){
            switch (c[1]){
                case "+" :
                    x = first +second;
                    break;
                case "-" :
                    x = first - second;
                    break;
                case "*":
                    x = first * second;
                    break;
                case "/":
                    x = first / second;
                    break;
            }
            x2 = c1[(x-1)];
        }

        if ((first >= 1) && (first <=10) && (second >= 1) && (second <=10) && (k1 == 1 ) && (k2 == 1)){
            System.out.println("Ваше получившееся число:");
            System.out.println(x2);
        } else if ((first >= 1) && (first <= 10) && (second >=1) && (second <= 10) && (k1 == 0) && (k2 == 0)){
            System.out.println("Ваше получившееся число:");
            System.out.println(x);

        } else {
            System.out.println("Числа не подходят");
        }


    }
}

Ответы

▲ 2

Если речь идет о Java (не знаю как в swift, но должно быть похоже, как и во всех языках основанных на синтаксисе С), мне кажется Вы упускаете очень важную концепцию - область видимости переменных.

Упрощенно Ваш код с точки зрения данной проблемы выглядит так:

if (true) {
  int nestedVariable = 1;
}
use(nestedVariable); ///< ERROR

т.е. переменная прекращает свое существование за пределами области видимости, т.е. за пределами блока { } в котором она объявлена (не путать с инициализацией!).

сравните с предыдущим примером:

int nestedVariable;
if (true) {
  nestedVariable = 1;
}
use(nestedVariable); ///< OK

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

упростив некоторые конструкции Ваш код может выглядеть примерно так (неясно откуда берутся c, k1, k2 поэтому они остаются как были, но в них могут быть ошибки, как в случае с условием (c[0] == c1[i]) во втором цикле, возможно тут должно быть (c[2] == c1[i]) или (c[2] == c2[i])):

    int first = switch (c[0]) {
        case "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" -> Integer.valueOf(c[0]);
        default -> {
            int f = 0;
            for (int i = 0; i<c1.length; i++) {
                if (c[0] == c1[i]) {
                    first = i + 1;
                    k1=1;
                }
            }
            yield f;
        }
    };

    int second = switch (c[2]) {
        case "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" -> Integer.valueOf(c[2]);
        default -> {
            int s = 0;
            for (int i = 0; i<c1.length; i++) {
                if (c[2] == c1[i]) {
                    s = i + 1;
                    k2=1;
                }
            }
            yield s;
        }
    };

    int x = switch (c[1]){
        case "+" -> first +second;
        case "-" -> first - second;
        case "*" -> first * second;
        case "/" -> first / second;
        default -> throw new IllegalArgumentException("Unsupported operation: " + c[1]);
    };

ЗЫ: сравнивать строки с помощью == или != можно, если осторожно. т.е. надо помнить, что эти операторы сравнивают только указатель на экземпляр строки, и если вы всегда пользуетесь строкам из некого пула и никогда не создаете новых экземпляоров явно или неявно, то эти операторы можно использовать, НО в Вашем коде этого делать нельзя, потому что условие будет возвращать не то что Вы ожидаете если Вы будете сравнивать строки идентичные по содержанию но размещенные в разных экземплярах.

String s = new String("X");
boolean isNotX = (s != "X"); ///< true, потому что сравниваются разные объекты
String s1 = s;
boolean isNotSame = (s != s1); ///< false, потому что s и s1 указывают на один и тот же объект.
▲ 1

Всё просто. Если в какую-то ветку if код не попадёт, то значит переменная не будет объявлена. Например вы не учитываете тот факт, что в цикле в if может код не попасть никогда.

Объявите переменные вообще где-нибудь вне условий и задайте им дефолтные значения. Например 0.


И да, строки нельзя сравнивать оператарами == и !=, смотрите почему:

Как сравнивать строки в Java?

▲ 0

При условии, что существует простое перечисление с римскими цифрами, данный калькулятор проще реализовать с использованием следующих "фишек":

  • Прочитать данные в именованные переменные при помощиScanner::next вместо чтения строки Scanner.nextLine и разбиения на массив String.split
  • Завести пару булевых переменных для определения состояния ввода: являются ли обе цифры обычными или римскими
  • Для проверки "римских" цифр использовать Enum::valueOf, который выбросит "понятное" исключение для отсутствующего названия элемента. Также для римских цифр должны быть ограничения на значение (от 1 до 10) и что первый операнд всегда больше второго, чтобы результат действия всегда был натуральным числом
  • Вычислить результат, используя один switch expression по виду операции
  • Вывести результат в нужном формате; для конвертации в римсккую цифру используются функции Enum.values() + Enum::ordinal

Вариант реализации (непедагогичная подсказка):

Scanner in = new Scanner(System.in);
System.out.println("Введите пример, где значения вводятся через пробел");
String arg1 = in.next().toUpperCase(), op = in.next(), arg2 = in.next().toUpperCase();

boolean isNum = arg1.matches("[2-9]|10?") && arg2.matches("[2-9]|10?");
boolean isRom = !isNum 
    && Roman.valueOf(arg1).value() < 11
    && Roman.valueOf(arg2).value() < 11;
    
if (!isNum && !isRom) {
    throw new IllegalArgumentException("Неверные операнды: arg1=" + arg1 + "; arg2=" + arg2);
}

int a = isNum ? Integer.valueOf(arg1) : Roman.valueOf(arg1).value();
int b = isNum ? Integer.valueOf(arg2) : Roman.valueOf(arg2).value();
if (isRom && a <= b) {
    throw new IllegalArgumentException("Неверные операнды для римских чисел: arg1=" + arg1 + "; arg2=" + arg2);
}
int res = switch(op) {
    case "+" -> a + b;
    case "-" -> a - b;
    case "/" -> a / b;
    case "*" -> a * b;
    default -> throw new IllegalArgumentException("Неверная операция " + op); 
};

System.out.println("Результат: " + (isNum ? res : Roman.values()[res - 1]));

Соответственно, код перечисления для римских чисел от 1 до 100 с дополнительным методом int value:

enum Roman {
    I,     II,     III,     IV,     V,     VI,     VII,     VIII,     IX,    X,
   XI,    XII,    XIII,    XIV,    XV,    XVI,    XVII,    XVIII,    XIX,   XX,
  XXI,   XXII,   XXIII,   XXIV,   XXV,   XXVI,   XXVII,   XXVIII,   XXIX,  XXX,
 XXXI,  XXXII,  XXXIII,  XXXIV,  XXXV,  XXXVI,  XXXVII,  XXXVIII,  XXXIX,   XL,
  XLI,   XLII,   XLIII,   XLIV,   XLV,   XLVI,   XLVII,   XLVIII,   XLIX,    L,
   LI,    LII,    LIII,    LIV,    LV,    LVI,    LVII,    LVIII,    LIX,   LX,
  LXI,   LXII,   LXIII,   LXIV,   LXV,   LXVI,   LXVII,   LXVIII,   LXIX,  LXX,
 LXXI,  LXXII,  LXXIII,  LXXIV,  LXXV,  LXXVI,  LXXVII,  LXXVIII,  LXXIX, LXXX,
LXXXI, LXXXII, LXXXIII, LXXXIV, LXXXV, LXXXVI, LXXXVII, LXXXVIII, LXXXIX,   XC,
  XCI,   XCII,   XCIII,   XCIV,   XCV,   XCVI,   XCVII,   XCVIII,   XCIX,    C;
        
    public int value() { return ordinal() + 1;}
}