Исключения для потока управления

Здесь есть интересный пост об этом, в отношении к потоку управления между приложениями.

Ну, недавно я столкнулся с интересной проблемой. Генерация n-го значения в потенциально (практически) бесконечной рекурсивной последовательности. Этот конкретный алгоритм БУДЕТ находиться по крайней мере в 10-15 ссылках на стек в тот момент, когда он будет успешным. Моей первой мыслью было создать исключение SuccessException, которое выглядело бы примерно так (C#):

class SuccessException : Exception
{
    public string Value
    { get; set; }

    public SuccessException(string value)
        : base()
    {
        Value = value;
    }
}

Затем сделайте что-то вроде этого:

try
{
    Walk_r(tree);
}
catch (SuccessException ex)
{
    result = ex.Value;
}

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


person Matthew Scharley    schedule 06.10.2008    source источник


Ответы (7)


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

person Mitchel Sellers    schedule 06.10.2008
comment
По сравнению с накладными расходами, чтобы определить значение успеха на каждом уровне и выйти из 10+ ссылок на стек? Что касается использования, это личный проект, и я не думаю, что это какое-то оправдание. - person Matthew Scharley; 06.10.2008
comment
если у вас есть более 10 ссылок на стек и вы обрабатываете их, фреймворк все равно должен выполнять кучу дополнительной обработки. Если вы настроили рекурсивные вызовы для возврата успешного значения, это не должно быть проблемой. - person Mitchel Sellers; 06.10.2008
comment
Достаточно верно. Другая половина вопроса заключалась в том, как я мог структурировать это по-другому. Возможно, как bool Walk_r (дерево объектов, результат строки)? - person Matthew Scharley; 06.10.2008
comment
Я думаю, чтобы ответить на этот вопрос, нам нужно немного больше узнать о том, что вы делаете, и о том, как устроен текущий метод. Обычно возвращаемым значением рекурсивного вызова является значение, указывающее на успех/неудачу. - person Mitchel Sellers; 06.10.2008
comment
Что бы ни делалось, нет веской причины для создания исключения для неисключительного условия. Способ вернуть значение — использовать оператор return. Если у вас проблемы с производительностью, вы можете использовать явный стек. - person domgblackwell; 08.10.2008

walk_r должен просто возвращать значение при попадании. Это довольно стандартный пример рекурсии. Единственная потенциальная проблема, которую я вижу, заключается в том, что вы сказали, что она потенциально бесконечна, что нужно будет компенсировать в коде walk_r, подсчитывая глубину рекурсии и останавливаясь на каком-то максимальном значении.

Исключение на самом деле делает код очень странным, поскольку вызов метода теперь выдает исключение для возврата значения, а не просто возвращает «нормально».

try
{
    Walk_r(tree);
}
catch (SuccessException ex)
{
    result = ex.Value;
}

становится

result = Walk_r(tree);
person Robin    schedule 06.10.2008
comment
Я сказал эффектно. Глубина рекурсии составляет всего 15, но общее проблемное пространство исчисляется буквально миллиардами, и его невозможно охватить в режиме реального времени, а значит, оно бесконечно. - person Matthew Scharley; 06.10.2008

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

person finnw    schedule 06.10.2008
comment
Поиск занимает несколько секунд. Не тривиально, но не долго, если речь идет о реальном времени. Исключение вдобавок, вероятно, ничего, но мне удалось перестроить вызов, чтобы он не нуждался в нем, и пункты о читабельности для кого-то тоже полезны. - person Matthew Scharley; 07.10.2008

Не очень хорошая идея создавать исключения как часть алгоритма, особенно в .net. В некоторых языках/платформах исключения довольно эффективны, когда выбрасываются, и они обычно таковы, например, когда итерируемый объект исчерпан.

person Vasil    schedule 06.10.2008

Почему бы просто не вернуть полученное значение? Если он вообще что-то возвращает, предположим, что он успешен. Если ему не удается вернуть значение, это означает, что цикл завершился неудачно.

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

person schonarth    schedule 06.10.2008

Проблема с использованием исключений заключается в том, что они (по большому счету) очень неэффективны и медленны. Конечно, было бы так же просто иметь условие if в рекурсивной функции, чтобы просто возвращаться по мере необходимости. Честно говоря, с объемом памяти на современном ПК маловероятно (хотя и не невозможно), что вы получите переполнение стека только с небольшим количеством рекурсивных вызовов (‹100).

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

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

person TK.    schedule 06.10.2008

Использование исключений в обычном потоке программы в моей книге — одна из худших практик. Рассмотрим беднягу, который охотится за проглоченными исключениями и запускает отладчик, настроенный на остановку всякий раз, когда возникает исключение. Этот чувак теперь злится... и у него есть топор. :П

person Quibblesome    schedule 06.10.2008
comment
Отсюда кастомное исключение и специализированный улов. Проглатывать исключения плохо! - person Matthew Scharley; 06.10.2008
comment
Это все равно остановит выполнение, когда я буду искать проглоченных ошибок. Исключения в обычном потоке приложений просто неверны. Вместо этого используйте архитектуру. - person Quibblesome; 07.10.2008