批次限制-浏览菜单时的最大递归 [英] Batch Limitation - Maximum Recursion while browsing menus

查看:113
本文介绍了批次限制-浏览菜单时的最大递归的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在测试我的代码时遇到了一个大问题.而这个问题就是令人讨厌的消息……超出了最大递归深度".看看它是如何工作的:

I have come across a big problem while testing my code. And this problem is the annoying message ... "Maximum recursion depth exceeded". Look how it works:

@echo off

REM ---------- MAIN MENU ----------
:Main

echo 1.Supermarket
echo 2.Exit Batch

setlocal
for /f %%A in ('"prompt $H & echo on & for %%B in (1) do rem"') do set "BS=%%A"
set /p Menu=%BS% Type in your option {1,2} followed by ENTER:
if not '%Menu%'=='' set Menu=%Menu:~0,1%
if %Menu% EQU 1 ENDLOCAL & goto Super
if %Menu% EQU 2 goto EOF

REM ---------- SECONDARY MENU ----------
:Super

echo 1.Supermarket
echo   a.Apple
echo   b.Banana

setlocal
for /f %%A in ('"prompt $H & echo on & for %%B in (1) do rem"') do set "BS=%%A"
set /p Menu=%BS% Type in your option {1,a,b} followed by ENTER:
if not '%Menu%'=='' set Menu=%Menu:~0,1%
if %Menu% EQU 1 ENDLOCAL & goto Main
if %Menu% EQU a ENDLOCAL & goto App
if %Menu% EQU b ENDLOCAL & goto Ban

:App

 setlocal enabledelayedexpansion
 set /a cnt=0
 for /f "tokens=* delims= " %%a in (*.apple) do (set /a cnt+=1)
 if %cnt% EQU 0 (
 echo There are %cnt% apples & pause>nul & ENDLOCAL & goto Main
 ) else (
 echo There are %cnt% apples

 [... do many things and after 200 lines]

 echo The total number of apples was %cnt% & pause>nul & ENDLOCAL & goto Main

:Ban
echo This is Banana & pause>nul & goto Main

:EOF

让我们假设用户连续遵循以下顺序:

Let's suppose that the user follows this sequence in a row :

1-> a(第一次)-> 1-> a(第二次)-> 1-> a(第三次)等 1-> a(第八次)-> 1-> a(第九次),此时,您将收到超出最大递归深度".消息.如果您只是忽略此消息,则代码的内部计算将开始表现出奇怪的行为,即使显然"坚称它似乎一直在产生良好的结果

1 --> a (First time) --> 1 --> a (second time) --> 1 --> a (third time) etc etc etc 1 --> a (eighth time) --> 1 --> a (ninith time) and, at this moment, you will get the "Maximum recursion depth exceeded". message. If you just keep ignoring this message, the internal calculations of your code will start to behave strangely, even tough it "apparently" seems to have been yielding good results

实际上,顺序无关紧要.如果您在字母"a"和"b"之间不断变化,或者"a"比"b"更多,那么会发生相同的事情:

In fact, the order does not matter. The same thing happens if you keep varying between letter "a" and "b" or more "a" than "b" as:

1-> b(第一次)-> 1-> a(第二次)-> 1-> a(第三次)... 1-> a(第八次)-> 1-> b(第九次).再次出现超出最大递归深度".消息

1 --> b (First time) --> 1 --> a (second time) --> 1 --> a (third time) ... 1 --> a (eighth time) --> 1 --> b (ninth time). Again the "Maximum recursion depth exceeded". message

此外,如果用户不停地输入选项1(始终只停留在MAIN MENU(主菜单)),则在第十二次尝试后会弹出错误消息.

In addition, if the user types in the option 1 relentlessly (invariably staying at MAIN MENU only) the error message pops out after the twelfth trial.

如何保持与上述相同的代码结构,但避免出现此错误?我在这里简化,但是我在谈论的是一批几百行代码以及几个二级,三级,四级等子菜单.

How to keep the same code structure as above but avoid this error? I am simplifying here but I am talking about a batch of several hundred lines of code and several secondary, tertiary, quaternary etc submenus.

谢谢,
马莱克

Thanks,
Maleck

推荐答案

我看不到您发布的代码中的任何递归,并且毫不奇怪,我无法使代码失败.

I don't see any recursion in the code you posted, and not surprisingly, I can't get the code to fail.

您必须在未显示导致问题的代码中进行递归.

You must have recursion in code that you are not showing that is causing the problem.

我知道批处理文件有两种致命的递归错误:

I am aware of two types of fatal recursion errors with batch files:

1)SETLOCAL被限制为最多32个级别.尝试在没有发出ENDLOCAL的情况下第三十三次使用SETLOCAL将导致以下致命错误消息:

1) SETLOCAL is limited to a maximum of 32 levels. Attempting to use SETLOCAL a 33rd time without issuing an ENDLOCAL will result in the following fatal error message:

Maximum setlocal recursion level reached.

我怀疑这是您要达到的递归限制.

I suspect this is the recursion limit you are reaching.

更新:实际上,每个CALL级别限制为32 SETLOCAL.有关更多信息,请阅读 http://ss64.org/viewtopic.php?id= 1778

UPDATE: Actually, the limit is 32 SETLOCAL per CALL level. For more information, read the entire thread at http://ss64.org/viewtopic.php?id=1778

2)如果在任何一个CALL返回之前都发出太多的CALL,则CALL递归可能会失败.每当批处理到达脚本,EXIT/B或GOTO:EOF末尾时,它都会从CALL返回.递归CALL的最大数量取决于计算机. (可能取决于代码吗?)错误消息将类似于以下内容:

2) CALL recursion can fail if you issue too many CALLs before any of them return. Batch returns from a CALL whenever it reaches the end of the script, EXIT /B, or GOTO :EOF. The maximum number of recursive CALLs is machine dependent. (Perhaps code dependent?) The error message will look something like this:

******  B A T C H   R E C U R S I O N  exceeds STACK limits ******
Recursion Count=600, Stack Usage=90 percent
******       B A T C H   PROCESSING IS   A B O R T E D      ******

某些系统配置可能会增加您可以进行的递归CALL的数量,但始终会有一些上限.

There may be some system configuration that could increase the number of recursive CALLs you can make, but there will always be some upper limit.

如果您要达到递归限制,那么除了重组代码之外,您实际上别无选择.但是除非您显示递归代码,否则我们很难(就我而言,这几乎是不可能的)为您提出解决问题的方法.

If you are reaching a recursion limit, then you really don't have much choice other than to restructure your code. But unless you show your recursive code, it is difficult (nigh impossible as far as I'm concerned) for us to suggest a way for you to work around the problem.

编辑以响应更新的问题和评论

这是需要遵循的一般规则:如果有代码循环,则每个SETLOCAL必须与ENDLOCAL配对.该循环可以由GOTO创建,也可以是FOR循环.

Here is a general rule that you need to follow: If you have a code loop then every SETLOCAL must be paired with an ENDLOCAL. The loop could be created by GOTO, or it could be a FOR loop.

您正在大量使用GOTO循环.您成功地确定,在发出SETLOCAL之后,必须在执行执行返回的GOTO之前发出ENDLOCAL.在我看来,您已经解决了这个问题.

You are making heavy use of GOTO loops. You successfully identified that after issuing SETLOCAL you must issue ENDLOCAL before you execute a GOTO that loops back. It looks to me like you solved that problem.

最后一次编辑-使用CALL简化逻辑

跟踪使用GOTO循环时必须发出ENDLOCAL的确切位置和次数非常麻烦.如果您改用CALL,则逻辑会简单得多,并且代码也会更加结构化.

It is a pain to keep track of exactly where and how many times you must issue ENDLOCAL when you loop with a GOTO. The logic is much simpler if you use CALL instead, and the code is more structured as well.

在CALLed子例程执行期间发出的所有SETLOCAL语句均在例程结束后隐式结束.使用CALL时无需显式使用ENDLOCAL.

All SETLOCAL statements that were issued during execution of a CALLed subroutine are implicitly ended once the routine ends. There is no need to explicitly use ENDLOCAL when using CALL.

下面,我展示了如何重构您的代码以使用CALL.研究所有代码-我做了许多微妙的更改,我认为这些更改可以简化和/或改进您逻辑的其他方面.

Below I've shown how your code could be restructured to use CALL. Study all of the code - I've made a number of subtle changes that I think simplify and/or improve other aspects of your logic.

@echo off
setlocal
:: Define the BS variable just once at the beginning
for /f %%A in ('"prompt $H & echo on & for %%B in (1) do rem"') do set "BS=%%A"

:Main  ---------- MAIN MENU ----------
:: ECHO( echoes a blank line
echo(
echo Main Menu
echo   1.Supermarket
echo   2.Exit Batch
:: If user hits ENTER without entering anything then current value remains.
:: So clear the value to make sure we don't accidently take action based
:: on prior value.
set "Menu="
set /p Menu=%BS% Type in your option {1,2} followed by ENTER:
if defined Menu set "Menu=%Menu:~0,1%"
if "%Menu%" EQU "1" call :Super
if "%Menu%" EQU "2" exit /b
goto :Main

:Super ---------- SECONDARY MENU ----------
setlocal
echo(
echo Supermarket
echo   a.Apple
echo   b.Banana
echo   1.Retutn to Main
set "Menu="
set /p Menu=%BS% Type in your option {1,a,b} followed by ENTER:
if defined Menu set "Menu=%Menu:~0,1%"
:: Use IF /I option so case does not matter
if /i "%Menu%" EQU "1" exit /b
:: Note that :App and :Ban are still logially part of this subroutine
:: because I used GOTO instead of CALL. When either :App or :Ban ends, then
:: control will return to the Main menu
if /i "%Menu%" EQU "a" goto :App
if /i "%Menu%" EQU "b" goto :Ban
goto :Super

:App
setlocal enableDelayedExpansion
set /a cnt=0
for /f "tokens=* delims= " %%a in (*.apple) do (set /a cnt+=1)
echo There are %cnt% apples
if %cnt% GTR 0 (
 REM ... do many things and after 200 lines
 echo The total number of apples was %cnt%
)
pause>nul
exit /b
:: The EXIT /B above returns to the Main menu (ends the CALL to :Super)
:: Note that the SETLOCAL at the start of :Super and the one at the
:: start of :App are implicitly ended. No need to explictly issue
:: ENDLOCAL.

:Ban
echo This is Banana
pause>nul
exit /b
:: The EXIT /B above returns to the Main menu (ends the CALL to :Super)
:: Note that the SETLOCAL at the start of :Super is implicitly ended.
:: No need to explictly issue ENDLOCAL.

这篇关于批次限制-浏览菜单时的最大递归的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆