Board logo

标题: [小把戏]交换两个变量的值而不使用临时变量 [打印本页]

作者: namejm     时间: 2007-1-25 08:16    标题: [小把戏]交换两个变量的值而不使用临时变量

  通用型的:
@echo off
set var1=abc
set var2=123
echo 交换前: var1=%var1% var2=%var2%
set var1=%var2%& set var2=%var1%
echo 交换后: var1=%var1% var2=%var2%
pause
  如果要交换的两个变量的值是数字的话,还可以使用如下两种方案:

  方案一:
@echo off
set /a num1=123,num2=456
echo 交换前: num1=%num1% num2=%num2%
set /a num1=%num2%,num2=%num1%
:: set num1=%num2%& set num2=%num1% 也是可以的
echo 交换后: num1=%num1% num2=%num2%
pause
  方案二:
@echo off
:: 会有数值范围的限制,即 num_a 与 num_b 的和必须在2^-31~2^31-1这个范围内
set /a num_a=123,num_b=456
echo 交换前: num_a=%num_a% num_b=%num_b%
set /a num_a=%num_a%+%num_b%-%num_a%,num_b=%num_a%+%num_b%-%num_b%
echo 交换后: num_a=%num_a% num_b=%num_b%
pause

作者: PPdos     时间: 2007-1-25 08:27    标题: 被我先抢座了^^

纪录在案 以备后用!~
作者: hangyug     时间: 2007-1-25 08:29
呵呵,试了一下,真是,,,还在琢磨呢。。。
作者: 0401     时间: 2007-1-25 09:02
不错,巧妙利用环境变量扩充的特性。其实这三个例子应该都算是一个意思吧。
作者: namejm     时间: 2007-1-25 10:11


  Quote:
Originally posted by 0401 at 2007-1-24 20:02:
不错,巧妙利用环境变量扩充的特性。其实这三个例子应该都算是一个意思吧。

  如果你仔细看了代码的话,你就会觉得它们并不是同一种思路:通用型的代码利用了 & 来交换两个变量的值,方案一利用了 set /a 的一些特性,方案二在利用 set /a 的特性的基础上,还使用了A+B-A=B、A+B-B=A这个算式来交换变量的值。

  其实,这几段演示代码还引出了一个比较有趣的话题:set var1=%var2%已经把var2的值赋给var1了,按理说,此时var1的值已经发生了改变,但是,在使用 & 引出另一个赋值语句 set var2=%var1% 的时候,为什么var2还能得到var1发生改变前的值?难道var1这个变量能同时保存一个以上的值吗?set /a num1=num2%,num2=%num1%也存在类似的现象。
作者: pengfei     时间: 2007-1-25 10:25

交换二个变量值算法非常简单.

假如有两个杯子: A和B, 要将A,B杯子中的水互换, 就要用到第三个杯子C了.
1. 将A杯的水倒入C杯;
2. 将B杯的水倒入A杯;
3. 将C杯的水倒入A杯;

这样就完成了二个杯子中的水的互换.
namejm兄给出的三种方法中, 第一种利用管道符交换变量,后两种是差不多的意思, 用逗号运算符来交换变量.

其实这种方法是利用脚本的特殊性实现的, 如果大家熟悉C语言中宏的话就不难理解了. 脚本靠解释器执行, 批处理脚本解释器在解释时必须先执行宏展开, 并且在一般情况下为一行一行解释.
set x=2
set y=3
set x=%y%&set y=%x%

这里最后一句宏展开为set x=3&set y=2, 自动将x与y值展开至赋值语句中了, 因此不用中间变量而交换变量值. 这里解释器扮演了中间变量.
其实真正不用中间变量交换两个变量值, 在二进制程序中也可以实现的方法也有.

就是用位运算来实现的, 呵呵~  大家想想?
作者: qzwqzw     时间: 2007-1-25 10:41
C语言教材的教学案例
set /a "a=b^a"
set /a "b=b^a"
set /a "a=b^a"
另外,四则运算也可以实现

只是没有异或运算灵活无限制
set /a a=a+b
set /a b=a-b
set /a a=a-b

作者: pengfei     时间: 2007-1-25 10:46
是的, 四则运算无论从效率还是使用范围来说都没有位运算那么好.
@echo off
set a=2
set b=3
echo a=%a%  b=%b%
set /a "a=b^a"
set /a "b=b^a"
set /a "a=b^a"
echo a=%a%  b=%b%
set /a a=a+b
set /a b=a-b
set /a a=a-b
echo a=%a%  b=%b%
pause

作者: 0401     时间: 2007-1-25 10:59
在单个语句之中,变量的赋值并不能马上被扩展(引用),只能在下一个语句中才能被扩展。单个语句是指 if for 语句以及使用括号 && & ||等连接的语句。除非启用了环境变量扩展延迟,并且使用“!”来代替“%”才能扩展变量值。所以我才说namejm兄你的三个例子意思差不多。。。
作者: simazhuo     时间: 2007-1-25 11:07
不错,收藏了
作者: namejm     时间: 2007-1-25 11:17


  Quote:
Originally posted by pengfei at 2007-1-24 21:25:脚本靠解释器执行, 批处理脚本解释器在解释时必须先执行宏展开, 并且在一般情况下为一行一行解释.

  呵呵,想起了这就是批处理的预处理机制^_^,准确的说法似乎应是 逐句执行。
作者: pengfei     时间: 2007-1-25 11:20
是的, 一般情况下为一行一行解释, 应该改为逐句解释执行, 用括号括起来的复合语句, 解释器也是一次执行的.
作者: lxmxn     时间: 2007-1-25 13:49

  感觉还是9楼的 0401 说得比较在理,如果启用变量延迟,以上的某些示例可能不成立。
@echo off
set var1=abc
set var2=123
echo 交换前: var1=%var1% var2=%var2%
set var1=%var2%&call set var2=%%var1%%
echo 交换后: var1=%var1% var2=%var2%
pause

作者: pengfei     时间: 2007-1-26 12:03
lxmxn兄用call %%var1%%实际没有展开, 和!var!一样, 是变量延迟的另一种用法. 因此变量交换也无法实现.
作者: htysm     时间: 2007-1-27 05:33
精彩。精彩。
作者: TLang     时间: 2007-1-27 23:40    标题: 关于+-的算法

关于+-的算法:

以前做过这样的,但是,我们继续用杯子思考,就算两种液体不相容,但是要把两杯里面的液体倒入一个杯子,那么只能至多半杯水(基于安全考虑的上限,不要说一杯只剩底,一杯将满的情况),否则会由于溢出而产生错误!!
作者: lxmxn     时间: 2007-1-28 11:47


  Quote:
Originally posted by pengfei at 2007-1-25 23:03:
lxmxn兄用call %%var1%%实际没有展开, 和!var!一样, 是变量延迟的另一种用法. 因此变量交换也无法实现.


  我所要表达的意思正是这种不成立的例子。

作者: electronixtar     时间: 2007-1-29 00:42
额,我说点题外话,这个东西强烈建议加入wiki里!
作者: pengfei     时间: 2007-1-29 02:19
est兄说的对, 放到Wiki不错的想法.

lxmxn兄指出的变量延迟, 脚本中进行变量替换中要防止使用延迟(如call迟延和!var!迟延).
作者: zljzsm     时间: 2008-5-11 09:21
呵呵  又多学了一点
作者: maky1117     时间: 2008-12-2 12:12
呵呵很巧妙地利用批处理中预处理的原理,这也是解释器和编译器的区别之一,C和C++就不得不用临时变量来完成这个交换