标题: [数值计算] [已解决]批处理使用goto跳出for循环,等待慢,如何解决? [打印本页]
作者: gapkiller 时间: 2010-12-17 14:08 标题: [已解决]批处理使用goto跳出for循环,等待慢,如何解决?
参考下面的代码:-
- @echo off
- for /L %%i in (1,1,1000000) do (
- echo %%i
- goto end
- )
-
- :end
复制代码
上面的代码在第一次循环的时候就应该跳出来
可是从实际结果来看, 代码(goto end)却执行了挺长时间, 让我感到很困惑.
而且loop越多, 所需时间越长, 也就是与那个1000000有关系.
求解惑~~
===================================================================
看了很多人的回复, 虽然原理还不清楚, 但也差不多明白是怎么个事. 不想把这个问题挂着了.标志为[已解决]
搜索了一下论坛, 关于分解质因数的问题, 得到了改善, 效率应该很快了. 见28L
[ 本帖最后由 gapkiller 于 2010-12-31 11:31 编辑 ]
作者: hanyeguxing 时间: 2010-12-17 14:30
在这里,goto 不能暂停或终止 for /l 的迭代,必须等待其完成
[ 本帖最后由 hanyeguxing 于 2010-12-17 16:53 编辑 ]
作者: gapkiller 时间: 2010-12-17 15:36
原帖由 hanyeguxing 于 2010-12-17 14:30 发表
无论子句中怎样(语法错误除外),for /l 都会先把数列从1迭代到1000000的。
你的意思是先迭代数列, 然后再执行后面的语句吗?
可是echo %%i却可以立刻显示出来, 后面紧接着的一句"goto end"执行了很长时间.
似乎在for循环与goto语句结合有点问题. 就好像执行了1000000次goto一样.
除了goto还有什么关键字可以跳出for循环呢?
作者: hanyeguxing 时间: 2010-12-17 16:33
- @echo off
- for /L %%i in (1,1,1000000) do (
- echo %%i
- call:end
- )
- :start
- echo 其他命令...
- pause&exit
- :end
- goto:start
复制代码
作者: gapkiller 时间: 2010-12-17 18:41
楼上两位使用了exit来退出循环, 并不是我想要的.
问题来源是这样的
我写了一个批处理来分解一个数的质因数, (90=2*3*3*5)- @echo off
- setlocal enabledelayedexpansion
-
- :start
- echo.
- echo.
- set /p n=Please input a number:
- set /p p=%n%=<nul
-
- set /a m=%n%/2
- for /L %%i in (2,1,%m%) do (
- if .%n%==.1 goto start
- call:setn %%i
- )
- if not .%n%==.1 echo %n%
- goto start
-
- :setn
- set /a tp=!n!%%%1
- if .%tp%==.0 (
- set /p "p=%1"<nul
- set /a n=!n!/%1
- if not .!n!==.1 set /p "p=*"<nul
- goto setn
- )
- goto :EOF
-
- :end
- echo.
- pause
复制代码
当输入一个较大的数时, 在计算完成后,总要停留好长一段时间才能退出来.
就是因为if .%n%==.1 goto start好像比较慢, 没有退出来.
[ 本帖最后由 gapkiller 于 2010-12-17 18:56 编辑 ]
作者: powerbat 时间: 2010-12-18 00:03
- @echo off
- for /l %%a in (0) do goto :out
- :out
- pause
复制代码
永远都跳不出来。
大量使用goto语句不是一种好的程序结构。
作者: hanyeguxing 时间: 2010-12-18 01:08
- @echo off&setlocal enabledelayedexpansion
- for /l %%a in (2,1,2147483647) do (
- set Xing=
- set Han=%%a
- call:han
- if "!Xing!"=="*%%a" (echo %%a为质数) else echo %%a=!Xing:~1!
- )
- pause&exit
- :han
- for /l %%a in (2,1,%Han%) do call:ye %%a
- goto:eof
- :ye
- set/a Ye=%1,Gu=Han%%Ye
- if defined #%Ye% goto:xing
- if %Gu% neq 0 (goto:eof) else for /l %%a in (2,1,%Ye%) do call:gu %%a
- :gu
- set/a Gu=Han%%%1
- :xing
- if %Gu% neq 0 (goto:eof) else (
- set/a Han=Han/Ye
- set Xing=!Xing!*%Ye%
- if not defined #%Ye% set #%Ye%==
- goto:han
- )
复制代码
[ 本帖最后由 hanyeguxing 于 2010-12-21 18:57 编辑 ]
作者: gapkiller 时间: 2010-12-18 14:57
原帖由 powerbat 于 2010-12-18 00:03 发表
@echo off
for /l %%a in (0) do goto ut
:out
pause永远都跳不出来。
大量使用goto语句不是一种好的程序结构。
"大量使用goto语句不是一种好的程序结构。"
头一次听人对批处理说这话~~~
作者: gapkiller 时间: 2010-12-18 15:02
看样子for循环无法迅速跳出了~~~~
作者: powerbat 时间: 2010-12-19 00:41
>>"大量使用goto语句不是一种好的程序结构。"
>头一次听人对批处理说这话~~~
看来你对程序设计接触得非常少,这应该是一种共识。
作者: cjiabing 时间: 2010-12-19 10:34
忍了半个月,今天来看看大家。
大量使用 goto不是好的程序?这个结论有点绝对,好不好要看使用环境和条件,不管三七二十一就用当然不好,但在该用时不用就更不好,特别是在一些大量重复代码情况下,不大量使用跳转语句就意味着使用大量重复代码,造成语句臃肿。
就楼主的问题,用‘’&‘’可能解决问题,致于原因就大大们解释比较好。
作者: gapkiller 时间: 2010-12-19 20:16
原帖由 powerbat 于 2010-12-19 00:41 发表
>>"大量使用goto语句不是一种好的程序结构。"
>头一次听人对批处理说这话~~~
看来你对程序设计接触得非常少,这应该是一种共识。
看样子, 阁下在批处理里面一定很少使用goto了.
作者: gapkiller 时间: 2010-12-19 20:20
原帖由 cjiabing 于 2010-12-19 10:34 发表
忍了半个月,今天来看看大家。
大量使用 goto不是好的程序?这个结论有点绝对,好不好要看使用环境和条件,不管三七二十一就用当然不好,但在该用时不用就更不好,特别是在一些大量重复代码情况下,不大量使用跳转语 ...
How?
我只想迅速跳出For循环而已.
作者: zqz0012005 时间: 2010-12-19 22:32
注意批处理的初衷不是一种程序语言,只是为了方便在DOS中操作而把多个命令写在一个bat文件里进行批量处理、执行,并引入了少许基本的流程控制语句(如if判断文件是否存在或以上一条命令执行结果为条件,for遍历文件),到了Windows NT时代强化了一些命令的功能,但它本质没变。连程序设计语言最基本的函数功能都没有(虽然可用Call来模拟,但只是“模拟”而已),比Linux Shell脚本都远远不如。所以想用批处理实现各种算法根本就是吃力不讨好的事情,虽然有些算法也能实现,但效率上来说几乎没有什么实用性。有这个时间,去学一门程序语言都是有可能的,或者简单点vbs也很容易。
除开算法,需要大量用到goto语句的时候还真是很少(注意goto :eof不算,它相当于exit /b),否则真需要好好考虑一下你所用到的方法。
作者: wc726842270 时间: 2010-12-20 09:31
有些不解。LZ可以利用多个GOTO(用IF和FOR配合)达到目的效果,最后再结束P就像8L那样不是也一样达到效果了
作者: gapkiller 时间: 2010-12-20 15:41
原帖由 wc726842270 于 2010-12-20 09:31 发表
有些不解。LZ可以利用多个GOTO(用IF和FOR配合)达到目的效果,最后再结束P就像8L那样不是也一样达到效果了
嗯,是这样的, 8楼的效果是使用了Exit来退出了.
但是他退出了整个程序, 我的本意并不是退出整个程序, 而是接着计算下一个数.
你可以运行一下6楼的代码就知道了.
作者: gapkiller 时间: 2010-12-20 15:47
原帖由 zqz0012005 于 2010-12-19 22:32 发表
注意批处理的初衷不是一种程序语言,只是为了方便在DOS中操作而把多个命令写在一个bat文件里进行批量处理、执行,并引入了少许基本的流程控制语句(如if判断文件是否存在或以上一条命令执行结果为条件,for遍历文件) ...
我的初衷也不是解决这么一个问题, 只是我在尝试写这样一个代码的时候发现这么一个问题.
然后我就来这里问一下, 我只是好奇为什么goto不能迅速跳出For循环.
您完全可以说,"哦, 这个是CMD的一个bug".
我觉得用批处理写出这个东西来,比用C来写更有成就感, 因为这个东西用C来写太简单了.
可能是我太执着了, 不好意思~
作者: caruko 时间: 2010-12-20 16:00
为什么不试试在call的语句段里放弃exit,调用goto呢?- @echo off
- for /l %%i in (1,1,100000000) do (
- echo %%i,%time%
- call :end
- )
-
- :end
- goto ee
- goto :eof
- :ee
- echo %time%
- pause
复制代码
[ 本帖最后由 caruko 于 2010-12-20 16:11 编辑 ]
作者: caruko 时间: 2010-12-20 16:14
for语句块是一次性装载到内存中的,因此很可能翻译后的代码是类似汇编中的固定次数循环;因为for并没有提供break语句。
而调用call时会重新装载语句代码,因此可能可以中断循环,在call中再调用goto就达到break的目的了。
或许启用变量延迟,也可以达到效果,楼主可以试一下。
[ 本帖最后由 caruko 于 2010-12-20 16:15 编辑 ]
作者: gapkiller 时间: 2010-12-20 17:30 标题: 回复 20楼 的帖子
谢谢.
不过显然没有break.
会call 100000000次:end
作者: caruko 时间: 2010-12-20 18:16
=.=
不知道你怎么回事...
我这边是瞬间就到pause状态了...
作者: gapkiller 时间: 2010-12-20 18:48
原帖由 caruko 于 2010-12-20 18:16 发表
=.=
不知道你怎么回事...
我这边是瞬间就到pause状态了...
当然是瞬间pause了....
只是这个pause还在for里面~~敲任意键会继续哦.
作者: powerbat 时间: 2010-12-20 22:00 标题: 回复 18楼 的帖子
这位朋友之前没有去过非常批处理verybat论坛,像这个for的问题等很多原理理论知识都是不少高手从那里探讨总结出来的,zqz0012005就是其中之一,我给的那个跳不出来的例子就是从他的帖子里看来的。所以是不是Bug、是什么原理他当然知道,所以他才给了那样的建议(这里他再次详述了批处理的本质和用途)。
可惜非常批处理竟然关闭了,很多精华我都没有备份下来,实在可惜!
作者: gapkiller 时间: 2010-12-20 22:19 标题: 回复 24楼 的帖子
我只是打个比方,并不是说这是一个bug,虽然这很可能是一个bug.
还有,我的目的不是解决代码问题, 只是本着学习的态度想知道这是什么问题.
对了, 那个跳不出来的例子很好!
作者: gapkiller 时间: 2010-12-22 18:18
可能这就是for的一个特点吧~
针对这个问题, 只好放弃for了..
使用goto写了一个~
- @echo off
- setlocal enabledelayedexpansion
- :input
- set /p n=Please input a number:
- if .%n%==. goto exit
- set /p p=%n%=<nul
- set i=2
- set /a n2=%n%/2
- :start
- if %i% gtr %n2% goto end
- set /a t=%n%%%%i%
- if .%t%==.0 (
- set /p "p=%i%"<nul
- set /a n/=%i%
- if not .!n!==.1 set /p "p=*"<nul
- set /a n2=!n!/2
- ) else (
- set /a i=%i%+1
- )
- goto start
- :end
- set /p "p=%n%"<nul
- echo.
- set n=
- goto input
- :exit
复制代码
作者: tmplinshi 时间: 2010-12-22 19:32
只有 for /L 是这样的,所有数都要循环。而直接的 for 和 for /f 可以跳出。以下可以看出:- for /l %%a in (1 1 5) do if %%a==3 goto 1
- :1
- echo 1
-
- for %%a in (1 2 3 4 5) do if %%a==3 goto 2
- :2
- echo 2
-
- for /f %%a in ('"echo 1&echo 2&echo 3&echo 4&echo 5"') do (
- if %%a==3 goto 3
- )
- :3
- echo 3
- pause
复制代码
g:\我的文档\桌面>for /L %a in (1 1 5) do if %a == 3 goto 1
g:\我的文档\桌面>if 1 == 3 goto 1
g:\我的文档\桌面>if 2 == 3 goto 1
g:\我的文档\桌面>if 3 == 3 goto 1
g:\我的文档\桌面>if 4 == 3 goto 1
g:\我的文档\桌面>if 5 == 3 goto 1
g:\我的文档\桌面>echo 1
1
g:\我的文档\桌面>for %a in (1 2 3 4 5) do if %a == 3 goto 2
g:\我的文档\桌面>if 1 == 3 goto 2
g:\我的文档\桌面>if 2 == 3 goto 2
g:\我的文档\桌面>if 3 == 3 goto 2
g:\我的文档\桌面>echo 2
2
g:\我的文档\桌面>for /F %a in ('"echo 1&echo 2&echo 3&echo 4&echo 5"') do (if %a
== 3 goto 3 )
g:\我的文档\桌面>(if 1 == 3 goto 3 )
g:\我的文档\桌面>(if 2 == 3 goto 3 )
g:\我的文档\桌面>(if 3 == 3 goto 3 )
g:\我的文档\桌面>echo 3
3
g:\我的文档\桌面>pause
请按任意键继续. . . |
[ 本帖最后由 tmplinshi 于 2010-12-22 19:36 编辑 ]
作者: gapkiller 时间: 2010-12-31 11:24
- @echo off
- setlocal enabledelayedexpansion
-
- :input
- set /p input=Please input a number:
- if .%input%==.0 goto exit
- set /a n=input
-
- if %n% lss 1 (
- echo Out of range!!
- goto input
- )
- if %n% gtr 999999999 (
- echo Out of range!!
- goto input
- )
- set /p p=%n%=<nul
- set i=2
-
- call:sqrt %n%
-
- :start
- ::echo .%sqrtn%.&pause>nul
- if %i% gtr %sqrtn% goto end
- set /a t=%n%%%%i%
-
- if .%t%==.0 (
- set /p "p=%i%"<nul
- set /a n/=%i%
- if not .!n!==.1 set /p "p=*"<nul
- call:sqrt !n!
- ) else (
- set /a i=%i%+1
- )
- goto start
-
- :end
- set /p "p=%n%"<nul
- echo.
- set input=
- goto input
-
- ::=============runing sqrt=================================
- :sqrt
- set type=%1
- set/a xn=type
- set/a times=1
- if %type% geq 99 set/a times=2
- if %type% geq 9999 set/a times=3
- if %type% geq 999999 set/a times=4
- if %type% geq 99999999 set/a times=5
-
- for /l %%a in (1,1,4) do (
- set/a yn=!xn!*100
- set/a zn=!yn!/100
- if not !yn! lss 0 (
- if !xn!==!zn! (
- set/a xn=!yn!
- )
- )
- )
-
- set/a sn=xn
- set sqn=1
- for /l %%a in (1,1,20) do (
- set/a sqn=sn/sqn+sqn
- set/a sqn=sqn/2
- )
-
- ::echo.
- ::echo √%type% ≈ !sqn:~0,%times%!.!sqn:~%times%!
- set sqrtn=!sqn:~0,%times%!
- goto :eof
- ::=============runing sqrt=================================
-
- :exit
复制代码
作者: linqing8 时间: 2011-4-28 00:51
我也在做质数因子分解的题目,在for中用了goto语句,数字越大的效率越低,逐条指令分析了良久,终于发现问题出在goto上,试着换成call后便好了。
这真是个奇怪的问题,百度了一下,发现bathome里的这个帖子有这个现象的深刻讨论,goto真是让人感觉莫名其妙。
作者: cjiabing 时间: 2011-4-28 14:02
本帖最后由 cjiabing 于 2011-4-28 14:10 编辑
以前没注意看到这个问题,确实有点……
不过,我想大大们应该可以解释,这似乎是FOR /L的预处理吧。
在第一个命令时没见它进行预处理,只对第一个数字进行了排序,它按照原过程进行。而在第二行它就进入对所有数字的 FOR /L 分析了,这种分析似乎不受其它命令的控制,用&无效,|也解释不了。
call可以解释,但call本身有点特殊,况且,每个call都意味着设置一个返回命令,比如goto :eof,否则,cmd就会一直挂着这个call的名字。
一直想写一篇关于中止批处理过程的文章的,可惜没空。
从楼主的代码看,楼主这样用一个for产生一个100000000位的数字,然后只取第一个数字就自动跳出(goto),显然楼主的思路也是缺乏效率的,简直是浪费。
goto 的本意是跳出、跳至,我们通常认为它是有去无回的。
而call的常用来调用、呼叫某程序,呼叫完了它遇到goto :eof时自动返回,如果没有goto :eof似乎它就一直挂在那里,除非遇到其它退出命令。
在for中,试图跳出for循环,再想跳回来,那只能用call。用goto是回不来的。而试图用goto中止进程,需要if等的判断。
在for /l中,即使没有任何多余命令,直接goto也无法阻止该命令进行预处理。在for中,我们通常这样理解,首先从集合中挑选因素处理,然后执行do后面的命令。这个过程反复进行,直到for中的内容被抽取完毕。但在for /l中,这个解释似乎站不住脚,因为for /l更像一次性从集合中抽取元素,然后才逐个去执行do后面的命令。试验如下:- @echo off
- for /L %%i in (2,2,103333333333333) do goto end
- echo.
- echo game over
- echo.
- pause
- exit
-
- :end
- echo.
- echo SORRY! STOP!
- echo.
- pause
复制代码
然后将goto end换成pause,该命令又变回正常的for过程。看来,这个又是具体命令的区别了。cmd常把命令划成三五六等,然后给它们赋予不同的优先权。
在这里看来,只要不跳出for/l的进程,命令是不会出错的,否则for/l死活都要吃完那点草。这是从上面推理下来,唯一合理的解释。- for /L %%i in (2,2,1033333) do (
- echo %%i
- dir
- )
复制代码
再补充一点:
是不是cmd把 for/l和call捆绑到一起了,也就是专门给call预留了空间?
只要for/l跳出进程,for/l就会进入具体的全面的预处理(前面没有进行的),然后设定一个点,以期call出去后能够找到这个回来的点。所以,我们就看到,for /l在跳出前进行了全部处理。
作者: gapkiller 时间: 2012-5-16 17:33
以前没注意看到这个问题,确实有点……
不过,我想大大们应该可以解释,这似乎是FOR /L的预处理吧。
在第 ...
cjiabing 发表于 2011-4-28 14:02
一年多之后再看到这个帖子, 有种说不出的感觉~
谢谢关注!
作者: poter 时间: 2012-5-16 19:23
兄弟你的代码里这一句是什么意思?
set /p p=%n%=<nul
为何变量n的值最后会是*=* ??
作者: gapkiller 时间: 2012-5-16 20:51
兄弟你的代码里这一句是什么意思?
set /p p=%n%=
poter 发表于 2012-5-16 19:23
不是*n* 你理解错了啊
作者: poter 时间: 2012-5-16 21:16
回复 33# gapkiller
兄弟你没懂我意思
如果这段代码单独输出,输入1进去,为何n的值为“1=1”???
@echo off
set /p n=Please input a number:
set /p p=%n%=<nul
echo %n%
set n=
set p=
作者: gapkiller 时间: 2012-5-17 11:45
回复 gapkiller
兄弟你没懂我意思
如果这段代码单独输出,输入1进去,为何n的值为“1=1”? ...
poter 发表于 2012-5-16 21:16
不是1=1吧
作者: gapkiller 时间: 2012-5-17 11:47
回复 gapkiller
兄弟你没懂我意思
如果这段代码单独输出,输入1进去,为何n的值为“1=1”? ...
poter 发表于 2012-5-16 21:16
n=1 啊
作者: poter 时间: 2012-5-17 12:53
回复 36# gapkiller
兄弟,你自己执行一下吧,如果N=1。我就没那么头痛了
作者: poter 时间: 2012-5-17 12:55
回复 36# gapkiller
作者: gapkiller 时间: 2012-6-20 14:38
回复 gapkiller
poter 发表于 2012-5-17 12:55
1不是=1吗?
欢迎光临 批处理之家 (http://www.bathome.net/) |
Powered by Discuz! 7.2 |