Да, вы можете формально доказать, что функция является хвостово-рекурсивной. Каждое сокращение выражения имеет хвостовую позицию, и если все рекурсии находятся в хвостовой позиции, то функция является хвостовой рекурсивной. Функция может быть хвостовой рекурсивной в одном месте, но не в другом.
В выражении let pat = exprA in exprB
только exprB
находится в хвостовой позиции. То есть, пока вы можете оценить exprA
, вам все равно придется вернуться, чтобы оценить exprB
с учетом exprA
. Для каждого выражения в языке есть правило сокращения, которое говорит вам, где находится хвостовая позиция. В ExprA; ExprB
это снова ExprB
. В if ExprA then ExprB else ExprC
это и ExprB
, и ExprC
, и т. Д.
Компилятор, конечно, знает это по ходу дела. Однако множество выражений, доступных в F #, и множество внутренних оптимизаций, выполняемых компилятором, например, например. во время компиляции сопоставления с образцом вычислительные выражения, такие как seq{}
или async{}
, могут не знать, какие выражения находятся в хвостовой позиции.
Практически говоря, с некоторой практикой для небольших функций легко определить хвостовой вызов, просто взглянув на ваши вложенные выражения и проверив слоты, которые НЕ находятся в конечных положениях для вызовов функций. (Помните, что хвостовой вызов может относиться к другой функции!)