Как вести счет «сделано» в рекурсивном алгоритме на Java?

У меня есть рекурсивный алгоритм, который просматривает строку, символ за символом, и анализирует ее, чтобы создать древовидную структуру. Я хочу иметь возможность отслеживать индекс символа, на котором в настоящее время находится синтаксический анализатор (для сообщений об ошибках, как и для всего остального), но я не заинтересован в реализации чего-то вроде кортежа для обработки нескольких возвращаемых типов.

Я пробовал использовать целочисленный тип, объявленный вне метода и переданный в рекурсивный метод, но поскольку он окончательный, приращения рекурсивного вызова «забываются», когда я возвращаюсь. (Поскольку приращение значения Integer делает ссылку объекта, передаваемого по значению, на новый объект)

Есть ли способ заставить что-то подобное работать, которое не загрязняет мой код?

Правда, не ответ, а немного сочувствия: :-) Пару месяцев назад я столкнулся с похожей ситуацией. Поскольку я использовал Common Lisp, я мог просто объявить свой счетчик «специальным» (т.е. с динамической областью видимости вместо лексической), но я помню, как думал: «Практически на любом другом языке для этого потребуется намного больше, чем 1 строка кода. решать".

Ken 01.04.2009 09:08
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
4
1
5 423
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

Это своего рода взлом, но иногда я использую изменяемый AtomicInteger, чтобы делать такие вещи. Я также видел случаи, когда передается int [] размера 1.

Текущее решение, которое я использую:

int[] counter = {0};

а затем передать его рекурсивному алгоритму:

public List<Thing> doIt (String aString, int[] counter) { ... }

и когда я хочу увеличить его:

counter[0]++;

Не супер элегантно, но работает ...

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

Чтобы получить желаемое поведение, вы можете написать свой собственный класс, подобный Integer, только изменяемый. Затем просто передайте его рекурсивной функции, он увеличивается в пределах рекурсии, и когда вы снова обращаетесь к нему после завершения рекурсии, он все равно сохранит свои новые значения.

Обновлено: обратите внимание, что использование массива int [] является разновидностью этого метода ... В Java массивы также передаются по ссылке, а не копируются, как примитивы или неизменяемые классы.

Ответ принят как подходящий

Поскольку вы уже обнаружили псевдоизменяемый целочисленный «взлом», как насчет этой опции:

Есть ли смысл делать отдельный класс Parser? Если вы сделаете это, вы можете сохранить текущее состояние в переменной-члене. Вероятно, вам нужно подумать о том, как вы собираетесь обрабатывать любые проблемы безопасности потоков, и это может быть излишним для этого конкретного приложения, но может сработать для вас.

Честно говоря, я бы перекодировал функцию, чтобы сделать ее линейным алгоритмом, использующим цикл. Таким образом, у вас не будет шансов закончиться нехваткой места в куче, если вы перейдете через очень большую строку. Кроме того, вам не понадобится дополнительный параметр, чтобы отслеживать счетчик.

Это также, вероятно, приведет к ускорению алгоритма, поскольку ему не нужно выполнять вызов функции для каждого символа.

Если, конечно, нет особой причины, по которой он должен быть рекурсивным.

Одна из возможностей, о которой я могу думать, - это сохранить счетчик в переменной-члене класса. Это, конечно, предполагает, что общедоступный метод doIt вызывается только одним потоком.

Другой вариант - рефакторинг общедоступного метода для вызова частного вспомогательного метода. Частный метод принимает список в качестве параметра и возвращает счетчик. Например:

public List<Thing> doIt(String aString) {
    List<Thing> list = new ArrayList<Thing>();
    int count = doItHelper(aString, list, 0);
    // ...
    return list;
}

private int doItHelper(String aString, List<Thing> list, int count) {
    // ...
    // do something that updates count
    count = doItHelper(aString, list, count);
    // ...
    return count;
}

Это предполагает, что вы можете выполнять обработку ошибок в общедоступном методе doIt, поскольку переменная count фактически не передается обратно вызывающей стороне. Если вам нужно это сделать, вы, конечно, можете выбросить исключение:

public List<Thing> doIt(String aString) throws SomeCustomException {
    List<Thing> list = new ArrayList<Thing>();
    int count = doItHelper(aString, list, 0);
    // ...
    if (someErrorOccurred) {
        throw new SomeCustomException("Error occurred at chracter index " + count, count);
    }
    return list;
}

Трудно понять, поможет ли это, не зная больше о том, как на самом деле работает ваш алгоритм.

Вы можете просто использовать статическую переменную класса int, которая увеличивается каждый раз, когда вызывается ваш метод doIt.

Вы также можете:

private int recurse (int i) {

    if (someConditionkeepOnGoing) {
        i = recurse(i+1);
    }

    return i;
}

Если в функции более одного рекурсивного вызова и предыдущий счетчик равен 1, оба новых вызова функции получат аргумент 2, тогда как один из них должен получить 3, поскольку это уже третье действие.

Riptyde4 24.03.2015 05:21

Другие вопросы по теме