Як було сказано в інших відповідях, CLR підтримує оптимізацію хвостових викликів, і, схоже, історично було вдосконалено. Але підтримка його в C # має відкрите Proposal
питання у сховищі git для проектування мови програмування C # Підтримка хвостової рекурсії # 2544 .
Тут ви можете знайти деякі корисні деталі та інформацію. Наприклад, згадується @jaykrell
Дозвольте дати те, що я знаю.
Іноді хвостовий виклик - це безпрограшна робота. Це може зберегти процесор. jmp дешевше, ніж call / ret Це може зберегти стек. Якщо торкнутися менше стека, це покращить місцевість.
Іноді зворотний дзвінок - це втрата продуктивності, виграш стеку. CLR має складний механізм, за допомогою якого для передачі абоненту більше параметрів, ніж отриманий абонент. Я маю на увазі конкретно більше стекового простору для параметрів. Це повільно. Але це зберігає стек. Це робитиме лише хвіст. префікс.
Якщо параметри виклику є стеком більшими, ніж параметри виклику, це, як правило, досить легке безпрограшне перетворення. Можуть бути такі фактори, як зміна позиції параметрів від керованого до цілого / плаваючого, і генерування точних StackMaps тощо.
Тепер є ще один кут - алгоритми, які вимагають усунення хвостового виклику, щоб мати можливість обробляти довільно великі дані з фіксованим / малим стеком. Мова йде не про продуктивність, а про вміння бігати зовсім.
Також дозвольте мені зазначити (як додаткову інформацію). Коли ми створюємо компільовану лямбда, використовуючи класи виразів у System.Linq.Expressions
просторі імен, є аргумент під назвою "tailCall", що, як пояснено у коментарі, це
Бул, який вказує, чи буде застосована оптимізація хвостових викликів при компілюванні створеного виразу.
Мене ще не пробували, і я не впевнений, як це може допомогти, пов'язане з вашим запитанням, але, ймовірно, хтось може спробувати це і може бути корисним у деяких сценаріях:
var myFuncExpression = System.Linq.Expressions.Expression.Lambda<Func< … >>(body: … , tailCall: true, parameters: … );
var myFunc = myFuncExpression.Compile();
preemptive
(наприклад, факторний алгоритм) таNon-preemptive
(наприклад, функція ackermann). Автор наводив лише два приклади, які я згадував, не даючи належних міркувань цього роздвоєння. Чи подібна ця біфуркація як хвостова та нехвістова рекурсивні функції?