答应过要发几篇心得的,第二篇:在 case 语句中使用字符串 (50分)

B

beta

Unregistered / Unconfirmed
GUEST, unregistred user!
to gear1023: 就是这个意思:)
 

唐佐平

Unregistered / Unconfirmed
GUEST, unregistred user!
早见过了
 
Z

zhukewen

Unregistered / Unconfirmed
GUEST, unregistred user!
楼主是从C转过来的吗?
技巧确实很高。
 
B

beta

Unregistered / Unconfirmed
GUEST, unregistred user!
// 楼主是从C转过来的吗?
相反,我是学 Pascal 起家的:)
我奇怪的是这怎么让你联想到 C :)
 
P

Pearl.

Unregistered / Unconfirmed
GUEST, unregistred user!
HHSH的方法不错吗?
假设要找的是'str10', 用HHSH的方法程序会做些什么?
调用10次ansicomparestr函数加10次判断( if strArray=str10 then
)另加一次判断和直接跳转(case语句)。用SS2000的方法呢?
也是调用10次ANSICOMPARESTR函数加10次判断, 可以看到两者执行效率上几乎没有区别(case语句的消耗户忽略不计)
内存消耗也几乎一样(都是10个字符串常量), 从内存寻址上来看,HHSH的方法必须绕一个圈子才能真正访问到所需要的常量内存,而SS2000的方法呢? 常量入口地址由编译器直接写进代码中了。
所以从最终代码质量上来看绝对是SS2000的方法高。
从代码可读性来看也是SS2000的好。 SS2000唯一缺点是必须多写几行代码。
我从HHSH的方法中只看到为了case而case.
另外给所有想用字符串case的人:随便怎样算, 都是直接if...then
...else
if ...的效率高。
想想你得到case所需枚举数字的过程的消耗(你有本是不经过一连串字符串比较吗?)再来反对我吧。
总之,字符串case纯粹属花架子, 中看不中用, 兼有误人子弟之嫌。 不要花时间在这上面啊。
 
S

SS2000

Unregistered / Unconfirmed
GUEST, unregistred user!
有人替我出头说话,真是感激,Pearl我的知音,他们说得我都不敢开口了,哈哈
不知道他们会不会攻击你,我在你后面,给你支持,上(你上),呵呵
 
C

creation-zy

Unregistered / Unconfirmed
GUEST, unregistred user!
>>他们说得我都不敢开口了
知者无畏!——如果你确信掌握了真理的话。何以有“不敢开口”之说?
我在前面已经说过了,在进行少量的判断时, if... then
是最清晰的,并且效率也是极高
的。但是,如果有上百个待匹配的字符串,我认为对Hash之后的结果进行Case的效率更加高
一些(平均50多次字符串比较和一次Hash运算,哪个快?),并且代码的可读性也不会下降(
代价是写少量的注释)。
>>字符串case纯粹属花架子,中看不中用
有同感,我看了一下自己以前写的代码,除了那个有上百个匹配对象的游戏程序用的是Hash
以及一个有30多个匹配对象的脚本引擎用的是TStringList.IndexOf以外,其余的用的都是 if
... then
:)
 

山泉

Unregistered / Unconfirmed
GUEST, unregistred user!
好文章!
 
S

SS2000

Unregistered / Unconfirmed
GUEST, unregistred user!
>>知者无畏!——如果你确信掌握了真理的话。何以有“不敢开口”之说?
哈哈,我是无畏的,不过我要开口,又得和你们打一场口水战了,所以就不敢开口了
既然creation-zy鼓励我开口,我就说两句。creation-zy说的没错,从开始就没错
>>我认为经过排序处理的TStringList的索引速度可以达到Log2N的时间复杂度,而且我还专门
>>看了case生成的asm代码工作流程——只要一次性跳转即可到达要执行的代码处,效率显然比
>>if...then
的N/2的平均比较次数低。——当然,在N<4的时候,用if...then
是最清晰且高效的。
>>在我的网络游戏中,有100多个指令,为了提高速度,我采用了Hash函数,效果还不错。
没有错误,我也同意
>>呵呵,你么说得都很对,我同意。
>>不过我只是表达了我的意思,还表达错了:(, creation-zy还说我的那种办法
>>效率不是最高。我也同意,呵呵,我从善如流。
>>不过,编程序往往并不是效率最重要,还有很多它的因素,使我采用效率并不
>>高的做法,而抛弃效率高的做法,我是不是有问题呀,呵呵
比如再极端点,有1万个字符串要比较又如何?
如果说效率,1万次字符串比较比1万次整数比较效率不可同日而语
但这时候我会写1万行if...then
..else
代码来实现吗,除非我是疯子!
显然,即使不是为了效率,也会想别的办法,因为写1万行if...then
..else
的代码出错概太高了,而且根本不允许这样的代码出现!
如果用creation-zy的方法,先HASH出一个整数,再用case 1万个整数,
这种方法也是够呛,也是我不允许的出现的!,必须想其他办法,如果没有其他办法,
就说明程序设计有问题,必须重新设计!!
所以,任何方法(包括我的方法)都有它使用的范围,如何用超出范围的例子来反驳我,
就没有什么意义了,我为自己辩解,更没有意义,所以,我就“不敢开口了”,呵呵
在通常的应用中,也就几个字符串,如果用那些方法,效率提不高(更差),可读性差,容易出错
根本就是画蛇添足,纯粹属花架子,中看不中用!(当然,你那上百个匹配对象的游戏程序
可能除外,呵呵)(没想到是我上了,呵呵)
 
B

beta

Unregistered / Unconfirmed
GUEST, unregistred user!
呵呵,不小心引发舌战,先道个歉,不过说实话,这正是我期望看到的场面,
这才是真正的讨论问题,这才是我熟悉的大富翁论坛。
先声明一下,该帖子是写给初学者看的,并没有考虑效率的问题,只考虑了可
行性和直观性。
鉴于这个原因,你去和一个初学者扯什么哈希表、汇编代码什么的,对他没有
什么帮助。应为普通的应用就是几个备选项而已,效率不必考虑。
当然,需要考虑效率的时候当然要仔细斟酌。但是“字符串case纯粹属花架子”
未免说的太过绝对了。在不需要考虑效率的时候当然应该考虑程序的清晰性和可
读性(对应我的第三种方法);如果按照这个来类比,那给变量取一个容易辨识
的名字,以及使用缩进格式等都是花架子咯。
我非常赞成在需要考虑效率的时候不择手段,但是那毕竟是少数情况,因为这样
而作出“总之,字符串case纯粹属花架子, 中看不中用”的论断不仅是片面的,
而且武断的。并且那句“兼有误人子弟之嫌”未免有点……
to Pearl:没有针对你的意思,就事论事而已[:)]
 
B

beta

Unregistered / Unconfirmed
GUEST, unregistred user!
在此相指出 Pearl 和 SS2000 一个共同的错误(纯属交流目的):
“随便怎样算, 都是直接if...then
...else
if ...的效率高”
“先HASH出一个整数,再用case 1万个整数,这种方法也是够呛”
不是这样的,我看了一下 case 语句的汇编代码,才明白 Delphi 为什么让 case
只能使用顺序类型,[red]那就是为了提高效率![/red] 因为它作出决定只需要
三条汇编指令!
要清楚地说明这个东西,恐怕又要开一个帖子了。鉴于本贴太长了,我另开一个
帖子专门讨论这个问题: http://www.delphibbs.com/delphibbs/dispq.asp?lid=1406114
case 只需要做一次判断就可以了!而不是“再用case 1万个整数”
而先 Hash 再 case 的做法也明显优于 if then
else
if,虽然说创建 Hash 表也
需要时间,但是这是一次性的,若运行时需要不停地判断,速度差异就非常明显地
显现出来了。因为每次判断只需计算一个字符串的 Hash 值(时间可忽略)和一次比
较加一次跳转,时间复杂度依然是 O(1),而每次都 if then
else
if 则每次的时
间复杂度都是 n/2。何况,要是 Hash 函数设计的合适,初始化时间也可以缩短。
因此您的“随便怎样算……”就显得既片面而又没有根据。
再次强调,我就事论事[:)]
 
S

SS2000

Unregistered / Unconfirmed
GUEST, unregistred user!
哈哈,beta显然误解了我的意思
如果说case用了一个分支表来进行跳转,可能会比if..then
..else
效率高
特别是n>4,就象creation-zy说的一样,我没有反驳这个问题
>>“先HASH出一个整数,再用case 1万个整数,这种方法也是够呛”
我说case 1万个整数,并没有说要case语句一定会比较1万次
为什么会够呛,其实你再看看就明白了,和效率没有关系
(>>编程序往往并不是效率最重要,还有很多它的因素,使我采用效率并不
>>高的做法,而抛弃效率高的做法,我是不是有问题呀)
你会写1万句case语句来实现吗?我不会,如果你会这么用,而我是你的老板,
我立马把你开除,呵呵
(>>任何方法(包括我的方法)都有它使用的范围,如何用超出范围的例子来反驳我,
>>就没有什么意义了,我为自己辩解,更没有意义)
你的方法可以让人开阔眼见,作为纯技术交流是很好的。
我就事论事,你的方法在实用方面我也是真的认为“是画蛇添足,纯粹属花架子,
~~~~~~~~~~
中看不中用”。
再次强调,我也是就事论事,我的看法是认为你的方法在实用价值上,在技术上
~~~~~~~~
另当别论
其实我开始也只是把我在遇到这种情况的时候的处理方法告诉大家而已,并不想发表
什么高论,没想到还是说了这么多,唉
 
B

beta

Unregistered / Unconfirmed
GUEST, unregistred user!

首先,我想要澄清一点,前面我反复讲效率的前提是前面看到你们在 if then
else
if
和 case 之间 的效率的问题上有意见分歧特别是 Pearl。我只是将 case 与 if then

else
if 进行对比,并没有把 case 推向其他领域,我也并不想表现出 case 在任何时候
都是高效率的化身的意思。更没有说任何时候用 case 都是应该的甚至必要的。这样看来
似乎是您误会了我的意思:)
// 你会写1万句case语句来实现吗?
我说 case 相对 if then
else
if 效率高并不是说要用它来写1万个分支,仅仅是用1万个
分支来说明效率差异之大。我想,我再笨也不至于笨到那种写1万个分支的地步;但是您却
认为我是那样。这无所谓,反正您不是我老板,也不必担心您开除我[:D]
// 在实用方面我也是真的认为“是画蛇添足,纯粹属花架子,中看不中用”
好,说到重点了,现在我们就讨论实用性。您说它在实用性方面是花架子,不中用,那就
是说它不实用咯。好我们看下面两个代码段:
// SegA
case strSel of
Beijing: ShowMessage('First');
Tianjing: ShowMessage('Second');
Shanghai: ShowMessage('Third');
Chongqing: ShowMessage('Forth');
else
ShowMessage('Other');
end;

// SegB
if str = 'Beijing' then
ShowMessage('First')
else
if str = 'Tianjing' then
ShowMessage('Second')
else
if str = 'Shanghai' then
ShowMessage('Third')
else
if str = 'Chongqing' then
ShowMessage('Forth')
else
ShowMessage('Other');
您说这两段代码哪一段实用?反正我不管怎么看得出的结论都是——都实用。
要看您怎么定义这实用二字了。我的理解是不需要太大的代价就可以实现,就叫做实用,
看看 SegA,它不能实现吗?可以。它需要很大代价吗?不用。所以我认为它的确是实用
的。
不知道您怎么认为它就不实用了呢?还是因为听了支持您的 Pearl 的由于错误理解效率
问题而得出的错误论断,而受了一定的影响呢?(纯属猜测[:D])
既然 SegA 是实用的,我们为什么不能用它呢?在实用性满足的前提下,当然应该有别的
东西要考虑——代码的清晰程度。我想,SegA 和 SegB 两段代码,哪段更清晰,大家一
眼就可以看得出来。何况它在实用性和清晰性同时满足的情况下,又能兼得效率优势,可
谓一石三鸟,何乐而不可为呢?还是要强调一下,我说 SegA 实用是在分支比较少的情况
下而言的。正如您所说,分支多了说明设计有问题,根本不存在 SegA 和 SegB 二者选谁
的问题,因为两个都不选。但是在分支少的时候这的确是……(刚刚才说了)
你知道我是就事论事就行了[:)]
 
S

SS2000

Unregistered / Unconfirmed
GUEST, unregistred user!
你还少说了一点
关键是还需要把'Beijing'字符串转化成Beijing这个枚举这个过程
首先需要定义枚举类型,把每个字符串都定义一个相应的枚举,然
后再调用你的那个函数(老实说,我还从来没有这么用过)
strSel := TMyStrSel(GetEnumValue(TypeInfo(TMyStrSel), str));
最后才是你的那两个比较!
而你直接这样比较太有点不地道了吧。你直接比较,谁都知道case好。
 
S

SS2000

Unregistered / Unconfirmed
GUEST, unregistred user!
你还少说了一点
关键是还需要把'Beijing'字符串转化成Beijing这个枚举这个过程
首先需要定义枚举类型,把每个字符串都定义一个相应的枚举,然
后再调用你的那个函数(老实说,我还从来没有这么用过)
strSel := TMyStrSel(GetEnumValue(TypeInfo(TMyStrSel), str));
最后才是你的那两个比较!
而你直接这样比较太有点不地道了吧。你直接比较,谁都知道case好。
 
B

beta

Unregistered / Unconfirmed
GUEST, unregistred user!
// 关键是还需要把'Beijing'字符串转化成Beijing这个枚举这个过程
// 你直接比较,谁都知道case好。
是啊,就是因为在这种情况下 case 好,所以才用前面的几行代码来换回这后面的好处
这个不是问题呀,虽然说看起来有点奇怪,但是除此之外没有什么妨碍了吧?呵呵[:)]
我这样一个转换也是为了后面的直观嘛,而且此一转换本身也并非不直观,因此总的
来说是增加了程序的可读性,我是这样认为的。
// (老实说,我还从来没有这么用过)
这很正常啊,我也不常这样用,我只是提供一种可行的思路而已,我又没说要以此为
标准[:D] 何况要是您以前用过,那就不新颖了嘛:)
// 而你直接这样比较太有点不地道了吧。
这里的地道只是您的一种心理感觉而已,特别是在这个特定环境下。如果换了别人,
应该不会觉得这个有什么地不地道的问题。
何况,地道了,也就沦为常规了,我还能称其为“另类”的解决方案吗?[:D]
当然,我并不是为了表现我另类,才写此文,刚才说了,这仅仅是为了提供一种比较
新颖的并且可行的解决方案。
 
D

dxd0222

Unregistered / Unconfirmed
GUEST, unregistred user!
好像Delphi爷爷的时候,(好像是TP4.0)吧,就可以这么干!!!!!
 
B

beta

Unregistered / Unconfirmed
GUEST, unregistred user!
to dxd0222: 怎么干? case str of ?
呵呵,我没有用过 TP4,我从 TP5 开始用的,不过 TP5 好像不行:(
 
C

chillkwanjane

Unregistered / Unconfirmed
GUEST, unregistred user!
beta,如果说效率,GetEnumValue()就不用作字符串比较了吗?
 
C

catbrother

Unregistered / Unconfirmed
GUEST, unregistred user!
《程序员》有类似的文章
 

Similar threads

S
回复
0
查看
947
SUNSTONE的Delphi笔记
S
S
回复
0
查看
767
SUNSTONE的Delphi笔记
S
顶部