Через Stack<T>
, насколько понял, Вы это имели ввиду.
Нет. Я имел ввиду, что рекурсию, как правило, описывают неэффективным способом, т.к. он проще. Я бы вообще отказался от неё. Овчинка выделки не стОит.
Значит, я неправильно Вас понял.
<личное мнение>
Я думал Вы про Stack<T>
, так как под «вручную» я именно это и подразумевал.</личное мнение>
Я под ручным подразумевал тот код, который приведён выше. Как делать через Stack<T>
я не знаю.
Под одним и тем же понятием понимали совсем разное… Что-ж, бывает.
Я вообще не понимаю о чем вы говорите. Ручная реализация рекурсивных вызовов подпрограмм через библиотечный Stack<> на .NET? Это вообще как? Можете привести пример?
Такое, наверное, возможно, только если писать свой собственный интерпретатор с другого скриптового языка, да и то это будет очень неудобно и неэффективно. Да и не будут это настоящие “вызовы”, а только поверхностная эмуляция передачи управления.
Поддержка же нативных рекурсивных вызовов в виде команд работы с аппаратным стеком в ОЗУ существует в системе команд любого универсального процессора (PUSH / POP / RET / IRET).
Это смотря какие у вас приоритеты в конкретном случае: простота и изящество кода (а значит, он и пишется быстрее, и понимать/поддерживать его потом проще) или же максимальная производительность в рантайме. Тут нет универсальных критериев для всех и каждого.
Да, у рекурсии (особенно множественной) есть свои существенные недостатки – сложность отладки в нетривиальных случаях, риск исчерпания стека в рантайме и меньшая скорость работы по сравнению с циклическими аналогами. Но даже это не всегда так (см. ниже).
Не во всех. В функциональных, и особенно, логических ЯП (типа Пролога) рекурсия используется очень активно, как внутри (на уровне библиотечных функций и среды исполнения), так и снаружи (на уровне кода пользователя). Но там активно применяются различные оптимизации на низком уровне, типа гарантированной раскрутки концевой рекурсии в цикл.
Кстати, это можно было бы реализовать и в компиляторе Паскаля. @Admin, не думали над этим?
да, прямо страдаем от этой невозможности.
Чего только тут не начитаешься… От рассказов, что не надо использовать рекурсию, до советов употреблять волшебный Stack < T> и рассуждений, что стек - это аппаратно реализованная быстрая память. Как все запущено, однако…
Ответ на первоначальный вопрос получен: это глюк .NET. PascalABC.NET его лишь воспроизводит. Но было бы правильно убрать из документации (несуществующую) возможность обработки StackOverflowException.
Скорее всего, путают внутренние регистры/стеки возврата и данных или кэш процессора с разной архитектурой, и хотя можно извратиться с внешней структурой, ограничить параметры или отказаться от рекурсии, но как правильно решить проблему возврата управления? Насколько я понял, даже при известном размере итерации проверка доступной памяти перед следующим циклом не всегда сработает
Через Stack<T>
действительно можно имитировать рекурсию вручную самому. Ограничение на переполнение в такой реализации, разумеется, будет снято. Я и не говорил, что это будет настоящие вызовы, поэтому и употреблял слово «имитировать».
Код, думаю, можно было бы улучшить, но выкладываю оригинал:
uses GraphABC;
const
Angle = -Pi / 4;
type
EdgeData = auto class
A, B, C: Point;
R: integer;
end;
var
DataStack: Stack<EdgeData>;
function RotatePoint(pA: Point; r, angle: integer): Point;
begin
var angle2 := DegToRad(angle);
Result := new Point(Round(pA.X + r * Cos(angle2)), Round(pA.Y + r * Sin(angle2)));
end;
procedure PushTreePart(pB: Point; r: integer) := DataStack.Push(new EdgeData(RotatePoint(pB, r, -135), pB, RotatePoint(pB, r, -45), r));
function RIsBig(dt: EdgeData) := dt.R > 5;
begin
LockDrawing();
DataStack := new Stack<EdgeData>();
PushTreePart(new Point(300, 300), 200);
while DataStack.Count > 0 do
begin
var dt := DataStack.Pop();
Line(dt.A.X, dt.A.Y, dt.B.X, dt.B.Y);
Line(dt.B.X, dt.B.Y, dt.C.X, dt.C.Y);
if RIsBig(dt) then
begin
var r2 := dt.R div 2;
PushTreePart(dt.A, r2);
PushTreePart(dt.C, r2);
end;
end;
Redraw();
end.
. Про подобное применение Stack<T>
и шла речь в моих сообщениях.
Очень странно наблюдать, как вы разъясняете азбучную истину о том, что рекурсия эквивалентна итерации, ссылаясь исключительно на библиотечную реализацию стека в конкретной системе.
Меня попросили привести пример кода с Stack<T>
, я его и привёл. Ничего больше. Кроме того, я не говорил, что этот подход наилучший.
То, что рекурсия эквивалентна итерации доказано строго математически. В частности, на этом основано лямбда-исчисление. Математика утверждает, что рекурсию можно преобразовать в итерацию и наоборот, но не рассказывает, как именно это надо делать. Вот практика - она дает множество советов, когда заменять, а когда информирует, что программист и дальше будет “колоться, плакать,но продолжать лезть на кактус”.(с)
Что-то я не припомню чтобы у нас в документации была такая гадость
И к чему это ёрничание, а?! Я написал это применительно к возможности эффективного использования рекурсивных алгоритмов, чья глубина может потребовать бОльшего стека. Понятно, что вам лично и вашим школьникам это до лампочки. Но не о вас лично же шла речь.
@c3c, в справке сказано про StackOverflowException, но нигде не написано, что его возможно обработать.
Я боюсь, что вы обращаетесь не по адресу. @ibond живёт и работает в Германии.
А так, да - 1 Мб в .NET на стек и изменить это нельзя. Правда, можно изменить в создаваемом потоке.
Что же касается сожаления по тому или иному поводу, то позволю высказать своё: жаль, что в Ростове выпадает так мало снега.
Уточню: в Ростове-на-Дону. В Ростове-то его выпадает обычно больше, чем надо…
А мне бы понравилось.
<личное мнение>
Не думаю, что высказывание в лицо своих мыслей, направленных на конкретную личность, не приведёт к неконструктивной беседе. Лучше не стоит. Тем более с поднятой темой это никак не коррелирует.</личное мнение>
@spectatorBH, @Admin, @Gleb, @RAlex, давайте вернёмся к изначальной теме. Ростов-на-Дону и снег можно пообсуждать через личные сообщения.