Board logo

标题: [转贴] 浅谈批处理脚本的编写 - 1.效率篇 [打印本页]

作者: HAT    时间: 2011-12-17 13:55     标题: 浅谈批处理脚本的编写 - 1.效率篇

1.效率篇
  代码效率的提升往往由算法决定,曾发过专贴(浅谈提高代码效率的编写习惯:http://tieba.baidu.com/p/1187281687),但是以实例为主,并没有太多的文字说明,现在归纳一下:
  影响代码效率的主要有几点:

1.1 外部命令、goto、call 的调用次数
  这是众所周知的,这里只列个提纲,就不详加说明了
1.1.1 外部命令启动慢、运行快,所以不宜频繁启动
1.1.2 goto、call、exit 都是比较耗时的(相对于 for 构建的循环)
1.1.3 管道至少开启两个新进程(内部命令或语块将启用 cmd 解析执行),不解释
1.1.4 for /f 从命令获取输入时必然启用 cmd 进程,不解释


1.2 变量尽量少
  批处理中的变量是以变量表的形式存储,每次读取时从头读到符合条件的变量位置,所以我们所设定的变量越多、在表中位置越靠后,效率越低。
  值得注意的是,变量不仅仅受 set 影响,setlocal 的本质也是开辟一个新的变量表作为本地化操作下的临时变量环境,所以每次使用 setlocal 都会增加将当前的变量表所占用的空间,但因为旧的变量表处于“隐藏”状态,所以对效率没有明显的影响


1.3 算法思想(重点)
  这是我今天在效率方面想要论述的核心内容,撇开五花八门的技巧不谈,优化算法的时候应该遵循两个准则:
1.3.1 将循环次数由指数级变为算术级
  指数级运算是以乘法为基础的,算术级运算则是以加法为基础的(还不懂就看代码吧),随着运算量的增加,指数级算法的实际运算量将远高于算术级算法

【code1】
  1. @echo off&setlocal enabledelayedexpansion
  2. (for /f "delims=" %%a in (a.txt) do (
  3.    set /a a+=1,b=0
  4.    echo %%a
  5.    rem 输出 a.txt 内容
  6.    for /f "delims=" %%b in (b.txt) do (
  7.       set /a b+=1
  8.       if !a!==!b! echo %%b
  9.       rem 逐行对比行号,当相等时输出 b.txt 内容
  10.    )
  11. ))>合并ab文本.txt
  12. ::循环次数为 a.txt*b.txt 的行数
复制代码
【code2】
  1. @echo off&setlocal enabledelayedexpansion
  2. for %%a in (a b) do (
  3.    for /f "delims=" %%b in (%%a) do set /a "%%a+=10001"&echo !%%a:~-4!%%a:%%b
  4. )>>tmp.txt
  5. ::将ab两文件补位,使对行的前缀均为 [行号][文件名],这样排序时行号相同的内容将被输出为上下行
  6. sort tmp.txt /o tmp.txt
  7. ::排序tmp.txt,并输出到原文件
  8. (for /f "tokesn=1* delims=:" %%a in (tmp.txt) do echo %%b)>合并ab文本.txt
  9. ::循环次数为 (a.txt+b.txt)*2 的行数
复制代码
  再比如:
【code1】
  1. ::解约瑟夫环问题,详细介绍请自行百度。
  2. @echo off&setlocal enabledelayedexpansion&set "str= "
  3. echo %time%
  4. for /l %%a in (1,1,29) do set "str=!str!%%a "
  5.    for /l %%a in () do (
  6.    for %%a in (!str!) do (
  7.       set /a "n=(n+1)%%3"
  8.       if !n!==0 set "str=!str: %%a = !"
  9.    )
  10.    for /f "tokens=1,2" %%a in ("!str!") do if "%%b"=="" echo 最后剩下的是%%a号&echo !time!&pause>nul&exit
  11. )
  12. ::修改自 batman 的代码,使用 for 嵌套逐个去除复合条件的项,运算量为 !29(就是 1*2*3*4 一直到 29)
复制代码
【code2】
  1. @echo off
  2. set /a n=29,k=1,m=3,s=0
  3. for /l %%a in (2,1,%n%) do set /a s=(s+m)%%%%a
  4. set /a x=(s+k)%%n
  5. if %x% equ 0 (set x=%n%)
  6. echo 最后剩下是:%x%
  7. pause
  8. ::修改自 inittab 的公式法方案,运算量为 29
复制代码
  初中学过,前者的函数图象是u形曲线,后者则是稳定的直线,也就是算法的复杂度下降了

1.3.2 将加法思路变为减法思路
  加法思路是每次都将结果处理成所需的格式再输出
  减法思路是先得出大概的结果,再慢慢修正
【code1】
  1. (for %%a in (*.txt) do findstr /v ". $" "%%a"&&echo 文件 %%a 为空)>空文件列表.txt
  2. ::逐个判断文件是否为空,为空则输出文件名。
复制代码
【code2】
  1. (for /f "delims=" %%a in ('findstr /mv ". $" *.txt') do echo 文件 %%a 为空)>空文件列表.txt
  2. ::一次性输出空文件名,再将结果修正为所需的格式
复制代码
类似的例子还有:
【code1】
  1. for %%a in (*.bat) do ren "%%a" "%%~na.txt"
  2. ::用 for 逐个重命名 bat 文件为txt,这是典型的加法思路
复制代码
【code2】
  1. ren *.bat *.txt
  2. ::一次性重命名所有bat文件为txt,这是减法思路中一次成型的特例
复制代码
  遇到比较复杂的情况时,先整出个大样子,再一点一点修正,往往比从头到尾精雕细琢一轮来完成要快很多


1.4 影响环境的操作尽量少
  包括变量写入、句柄的重定向、除 nul 之外所有设备的输出(文件是 file 设备)等等都是比较耗时的,这个也在旧帖中亦有较详细的论述,在此略过不谈。

转自:http://tieba.baidu.com/p/1326823919
作者: cjiabing    时间: 2011-12-20 23:43

呵呵,效率我就不谈了,看看各位有什么补充的。
作者: HAT    时间: 2011-12-20 23:57

扩展阅读:

提高批处理代码效率的常用技巧及方案
http://www.bathome.net/thread-4831-1-1.html
http://www.bathome.net/thread-4482-1-1.html




欢迎光临 批处理之家 (http://bathome.net./) Powered by Discuz! 7.2