标题: [原创] 批处理递归概念,方便新手 [打印本页]
作者: pusofalse 时间: 2008-7-28 05:00 标题: 批处理递归概念,方便新手
搜遍整个论坛,没有找到关于递归的帖子,在此在众高手们面前班门弄斧一回。
首先,讲解下递归的概念。如下,引自百度百科:
递归做为一种算法在程序设计语言中广泛应用.是指函数/过程/子程序在运行过程序中直接或间接调用自身而产生的重入现像.
(我至今没弄懂函数的概念,但这并不影响学习。- -|||)
一句话概括就是:程序在运行时不断地调用自身的编程技巧就叫递归。
为方便阅读下文,举一简单例子来说明批处理的参数。
%0 代表批处理自身
%1 批处理的第一个参数
%2 。。。。第二个参数
%3 。。。。第三个参数
可一直到%9 超过9个参数,需用shift 移位,shift用法在此略过(笑~主要是因为我不会 ^_^)
如何使用?OK,新建BAT,保存为useradd.bat:
net user %1 %2 /add
然后打开CMD,CD进入批处理所在目录。然后输入
useradd 1234 123
回车,然后就添加了一个用户名为1234 密码为123的帐户。
此处,useradd为你新建的批处理,相当于一个外部命令。每个批处理文件都相当于一个外部命令。
1234为第一个参数,123第二个参数。
cmd中执行useradd 1234 123
相当于执行了net user 1234 123 /add命令
OK,关于参数,是否很简单,详细信息参照call/?
进入正题。。。
只说不行,看下面的代码:
输出00-99这100个数字- @echo off
- for /l %%a in (0 1 9) do (
- if "%1" equ "" (
- call %~fs0 %%a
- ) else (
- echo %1%%a
- )
- )
- if "%1" equ "" pause
- goto :eof
复制代码
OK,正题。
for /l %%a in (0 1 9) do (
用for的/l参数控制0-9这10个数。
if "%1" equ "" (
call %~fs0 %%a
... ...
%1 为批处理的第一个参数,这句的意思是 如果批处理的第一个参数是空值时,则传递给自身(%~sf0)一个参数,
参数是什么?当然是用for控制的那个数啦。传递完之后,此时的主过程,我们把它命名为a。a现在处于挂起状态,要等子过程(把它命名为b)运行完毕之后它再运行。
OK,在主过程a挂起的时候,我们来看下子过程b运行的流程。
b依旧从批处理头部开始运行。
for /l %%a in (0 1 9) do (
依旧用for控制0-9
if "%1" equ "" (
call %~fs0 %%a
) else (
echo %1%%a
)
但此时的第一个参数就不是空值了,所以它不会再为自身传递一个参数,而是要运行 else 中的内容。
echo %1%%a
此时要循环9次,输出的内容是00 01 02 。。。09
第一个0是主过程传递的参数,第二个数字是现在的子过程B 用for来控制的数。
if "%1" equ "" pause>nul
如果第一个参数是空值,则挂起,子过程B有参数哦,所以直接下一句(某人:喂喂,第一个参数是空值啊!为什么说不是?答曰:第一个参数是空值的是主过程,它依旧在for里面停顿着呢。)
goto :eof
eof :end of file 文件尾部,此时要说明的是,如果是子过程的goto :eof,意为返回主过程。而在主过程的goto :eof则是退出批处理。
此时运行的是子过程,所以它在此结束,并且控制权返回到主过程手里。
OK,第一次传递参数到运行完毕 B至此结束,并返回主过程A
A见B运行完了,它自己又继续执行。A还没完成任务呢,它还得继续为自身再次传递一个参数。流程如上。直到把10个数全部传递完了。
OK,跳过。。。假设此时已经把10个数传递完毕,主过程向下的流程又是什么呢。
if "%1" equ "" pause
主过程的参数是空值,所以运行到这步他会挂起,等待继续。
goto :eof
此时才是真正的退出批处理,因为是主过程A嘛。^_^
------------------------------------------------------------------------------------------------------------------------------------------------
OK 以上,程序运行完毕,输出的00-99完全是在不断调用自身中完成的,这就是递归。
递归的好处在于只需少量的代码就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。
(我没觉得减少。。。反而杂,难懂!)是的,此题最简洁的答案莫过于如下代码:-
- @echo off
- for /l %%a in (100 1 199) do set var=%%a&call,echo %%var:~1%%
- pause>nul
复制代码
用递归的方法来做此题反而繁杂了很多。但是请相信递归的好处,一题多解,拓展思路嘛!
虽然此时我没有太多代码可以作为依据来证明递归到底有多好。以后会的。^_^
递归的缺点在于运行效率较低,但以其简洁来看,也不算太大缺点了。
如下,输出九九乘法表。- @echo off&setlocal enabledelayedexpansion
- if "%1" neq "" (set "n=%1") else set "n=1"
- for /l %%a in (1 1 %n%) do (
- if "%1" equ "" (
- call %~fs0 %%a
- exit/b
- ) else (
- set/a res=%%a*%1
- set/p=%%a*%1=!res! <nul
- )
- )
- echo.&set/a n+=1
- if %n% lss 10 (call %~fs0 %n%) else pause>nul
复制代码
以上,我所理解的递归概念,在实例中已经说明,有何问题欢迎赐教,不胜感谢!
作者: xiaoxiao693 时间: 2008-7-28 20:30
没看懂,继续学习。
作者: jk328019419 时间: 2008-7-31 22:21
你好羡慕你的表达能力啊
作者: mkl 时间: 2008-8-6 18:32
@echo off&setlocal enabledelayedexpansion 中的 &setlocal enabledelayedexpansion 是什么意思?请指点
作者: tjtchly 时间: 2008-8-7 21:48
原帖由 mkl 于 2008-8-6 18:32 发表
@echo off&setlocal enabledelayedexpansion 中的 &setlocal enabledelayedexpansion 是什么意思?请指点
告诉CMD要启用延迟的环境变量。
关于环境变量延迟扩展,使用set /?可以查看到部分说明,不过考虑到其粗劣的翻译水平,建议在查看之前,首先chcp 437切换为英文查看原英文说明。鉴于文中已说得十分详尽,而且有数个代码示例,应该不难理解。在此仅略作一些补充。
在许多可见的官方文档中,均将使用一对百分号闭合环境变量以完成对其值的替换行为称之为“扩展(expansion)”,这其实是一个第一方的概念,是从命令解释器的角度进行称谓的,而从我们使用者的角度来看,则可以将它看作是引用(Reference)、调用(Call)或者获取(Get)。
而命令解释器是扩展环境变量的行为大致如下:首先读取命令行的一条完整语句,在进行一些先期的预处理之后,命令被解释执行之前,会对其中用百分号闭合的字符串进行匹配,如果在环境空间中找到了与字符串相匹配的环境变量,则用其值替换掉原字符串及百分号本身,如果未得到匹配,则用一个空串替换,这个过程就是环境变量的“扩展”,它仍然属于命令行的预处理范畴。
而一条“完整的语句”,在NT的命令解释器CMD中被解释为“for if else”等含有语句块的语句和用“& | && ||”等连接起来的复合语句。
因此,当CMD读取for语句时,其后用一对圆扩号闭合的所有语句将一同读取,并完成必要的预处理工作,这其中就包括环境变量的扩展,所以在for中的所有语句执行之前,所有的环境变量都已经被替换为for之前所设定的值,从而成为一个字符串常量,而不再是变量。无论在for中将那些环境变量如何修改,真正受到影响的只是环境变量空间,而非for语句内部。
而为了能够在for语句内部感知环境变量的动态变化,CMD设计了延迟的环境变量扩展特性,也就是说,当CMD读取了一条完整的语句之后,它不会立即执行变量的扩展行为,而会在某个单条语句执行之前再进行扩展,也就是说,这个扩展行为被“延迟”了。
延迟环境变量扩展特性在CMD中缺省是关闭的,开启它的方法目前有两个:一是CMD /vn,会打开一个新的命令行外壳,在使用exit退出这个外壳之前,扩展特性始终有效,常用于命令行环境中;二是setlocal EnableDelayedExpansion,它会使环境变量的修改限制到局部空间中,在endlocal之后,扩展特性和之前对环境变量的修改将一同消失,常用于批处理语句中。
还有在需要使用变量里套变量也可以使用变量延迟,如:
set a=1000
set b=dd
set a%b%=9000
set c=!a%b%!
凡是要在for语句后,再次调用你原先定义的变量,你就要用变量延迟。
[ 本帖最后由 tjtchly 于 2008-8-7 22:06 编辑 ]
作者: mkl 时间: 2008-8-8 10:46
写的这么详细,真是麻烦你了,谢谢!
作者: weixiaoyaer 时间: 2010-4-17 04:08
我是这样实现的
@echo off
for /l %%a in (0 1 9) do (
for /l %%b in (0 1 9) do (
echo %%a%%b
)
)
if "%1" equ "" pause
exit
作者: bat204183 时间: 2010-4-18 15:24
理解了 哈哈! 楼主 表达很清晰啊!谢谢!
作者: wankoilz 时间: 2010-5-27 10:11
批处理的递归应用,难得啊,学习了,帮顶!
作者: 523066680 时间: 2010-5-27 10:34
^_^ 递归的教程感觉要从一个比较简单的递归问题开始。
会比较有兴趣看下去。
帮老兄顶一下帖子,多久没现身了。
作者: keiamy 时间: 2010-7-27 21:18
真是上了重要的一課呀﹗從前總是看不明白七樓的代碼,現在終於搞懂了^.^
十分多謝pusofalse的細心講解! 很是開心呢"
(看了很久很久哦)不過還是非常非常菜鳥的......哈哈哈﹗
作者: wdhvv 时间: 2011-12-10 17:23
批处理递归概念,学习了 LZ很热心~ 论坛很强大
作者: GkingForce 时间: 2013-5-2 11:39
原文:
传递完之后,此时的主过程,我们把它命名为a。a现在处于挂起状态,要等子过程(把它命名为b)运行完毕之后它再运行。
OK,在主过程a挂起的时候,我们来看下子过程b运行的流程。
b依旧从批处理头部开始运行。
问题:
这里的主过程和子过程到底指的是什么?为什么子过程又要从头部开始运行??
求大神解答
作者: xpw 时间: 2013-5-2 19:47
比如说上面的代码保存为f,主过程就是f, 子过程就是指f里面的(call %~fs0 %%a),就是调用f自己,并传递一个参数。先运行f,得%%a 为0,子过程就是(call f 0),子过程和f里面的代码一样,不同的是,这里已经传递了一个参数0,if "%1" equ ""就不成立,所以就会执行else后面的内容,第一个子过程执行完,会输出00,01...09,返回主过程继续执行for语句,主过程负责输出00~99的十位数,子过程负责输出个位数
作者: wangx 时间: 2013-9-15 18:25
学到很多东西,每天进步一点,谢谢分享。
作者: wdh123 时间: 2016-8-20 21:26
回复 5# tjtchly
终于懂这句话了。受益匪浅
作者: 1212112 时间: 2017-9-27 00:26
学到很多知识点,目前遇到一些问题:
1.call %~fs0 %%a中%~fs0是什么意思?%1又是什么意思?
2.FOR运行第一次时,%%a为0,请问此时的%1的值是什么?%~fs0值是什么,是否为此BAT文件所在的完整路径加文件名?
3.当第一次FOR运行完后,%%a为1时,此时父程序的%1的值是空还是继承上次for循环中子程序中的%1值(0)?
作者: ai20110304 时间: 2018-8-29 23:20
告诉CMD要启用延迟的环境变量。
关于环境变量延迟扩展,使用set /?可以查看到部分说明,不过考虑到 ...
tjtchly 发表于 2008-8-7 21:48
讲得真好。。无论在for中将那些环境变量如何修改,真正受到影响的只是环境变量空间,而非for语句内部。
欢迎光临 批处理之家 (http://bbs.bathome.net/) |
Powered by Discuz! 7.2 |