mud_sanc 板


LINE

※ 引述《laechan (小太保)》之铭言: : for ( i = 0; i < max; i++) : if ( list == some_condition ) : do_something_with( list, this_player()->query("name") ); : 假设 "name" 是不变的, 则以下的方法会比较好: : name = this_player()->query("name"); : for ( i = 0; i < max; i++) : if ( list == some_condition ) : do_something_with( list, name ); 这个是基本的写法,若一个东西在程式里头会被读取两次以上时, 通常都会建议宣告一个变数来取代之。 再来的话就是内回的概念,以上面为例,写法可再改为.. do_something_with(list,name,max) ^^^^^ 然後在 do_something_with 函数中做 some_condition 的判断, 它们的差别就在於一个是呼叫了 max 次的 do_something_with 函数,一个只「呼叫一次」。 enable 中後期的最佳化就是以这个为主。 : write( @ENDMESSAGE : This is a forect : and is really boring : You are feeling sleepy. : ENDMESSAGE : ); 各位只要知道 write(@LONG ... LONG); 的用法即可。 通常若是多行式的,可以使用底下的方式。 write(@LONG . . . LONG @LONG . . . LONG ); 原文则是建议采用两段 write 的方式,因为 write 是 efun ,依目前一般主机的水准这也是可以的,但不建议。 : printf(Your name is %s\nand your level is %d\n", : this_player()->query("name"), this_player()->query("level") ); 这与 sprinf 的用法类似,上面的意思就是.. write("你的 id 是 "+ppl->query("name")+",你的"+ "等级是 "+ppl->query("level")+".\n"); 将之修改为.. write(sprintf("你的 id 是 %s,你的等级是 %d.\n", ppl->query("name"), ppl->query("level"))); 或像上面那样直接用 printf 输出。 (但偶尔会遇到需要做字串累加的情况就不能用 printf) 我自己的实验结果,使用 printf 或 sprintf 在回圈式的字串 累加及讯息输出上确实有帮助─程式不容易因为沉重的回圈而 自行中断执行。 : 摘自 TMI-2 的 /adm/daemons/cmd_d.c : : bin_ls = get_dir(path + "/"); : result = ({ }); : for (i = 0; i < sizeof(bin_ls); i++) { : if(sscanf(bin_ls, "_%s.c", tmp) == 1) : result += ({ tmp }); : } : cmd_table[path]=result; : 这里, 并不是所有的项目都有选到, 被选到的也是在被修改过後再放到最後 : 的阵列中. : 以下面的方法取代: : bin_ls = get_dir(path + "/"); : i = sizeof(bin_ls); : result = allocate(i); : j = 0; : while (i--) { // 使用 'while' 的原因请参考下面 : if(sscanf(bin_ls, "_%s.c", tmp) == 1) : result[j++] = tmp; : } : cmd_table[path]=result[0..j-1]; 在圣殿的实务上,我们还是以底下的方法为主... mixed ids=({}),usr=users(); foreach(ppl in usr) if(ppl->query("level")>119) ids+=({ppl->query("name")}); 因为通常我们都无法事先预知「ids 到最後会有多大」, 当然因为它最大顶多等於 sizeof(usr),所以若照底下那 样写也是可以的... mixed ids=({}),usr=users(); int i,j=sizeof(usr),k; ids=allocate(j); // 预先配置 size 给 ids while(i++<j) if(usr[i]->query("level")>119) ids[k++]=usr[i]->query("level"); 实务上因为这样子写的结果必须额外多宣告一些变数,所 以通常都会采取「变数尽量减少」的写法。 我自己本身也相当少使用到 allocate,但有需要的时候我 倒是会用啦..但我自己比较少有印象说哪里有用到。 : 不要用下列方法来格式你的 mapping: : emotemap = ([ ]); : 应该改用: : emotemap = allocate_mapping( 200 ); 这个我跟 nobu 求证过,allocate_mapping 在目前的圣殿是无效 的指令。 所以 mapping 变数的初始化还是以 emotemap = ([ ]); 为主。 : 3. 控制程序 (CONTROL FLOW) 以及 回圈 (LOOPING) : 在一个完整的 LPC 程式中, 控制程式执行的程序 (由测试, 以及部份的回圈所 : 组成) 可以因为正确的使用不同的形式而变得比较有效率. : 3.1. WHILE : 最简单的 (也是在简化型里最快的) 回圈使用方式. : 常见的是: : list = users(); : for ( i = 0 ; i < sizeof(list) ; i++ ) : do_something_with_item( list ); 这个以前有盯过,各位 wiz 们应该都已经避免这样子写了。 : 这是非常的没有效率, 因为 sizeof(list) 的值每次都要被重新计算出来 (参考 : 上面的 GENERAL POINTS 一节). 如果你想将整个 list 反过来排列, 试着使用以 : 下的方法: : max = sizeof(list); // slight performance gain : i = -1; : while ( ++i < max ) //evaluate and increment at same time : do_something_with_item(list); 这种写法可以减少一个变数 j = sizeof(list) : 如果顺序没有什麽太大的关系, 以下是最快的方式: : i = sizeof(list); : while (i--) : do_something_with_item(list); 这种回圈会比 for 执行起来更快速。 : 这就是 '简单的调件 while 回圈' ('simple condition while loop'). 和下列的 : 实在是没什麽太大的不同. : for ( i = sizeof(list) ; i-- ; ) ; : 以及相对等的 while 回圈. 但还是有少许利益可得. : 3.2. FOR : 最常见的回圈结构之一, 这在当你每次都需要在回圈结述前执行某个比较复杂的 : 动作时非常有用. 如果只是使用简单的指数增加或减少, 用 'while' 叙述会比较 : 好. : 如果 '结述运作' (ending operation) 比较复杂, 则以下的通用例子: : for ( initialise ; test ; final ) { : main body : } : 比下列清楚: : initialize : while ( test ) { : main body : final : } 目前已经进化到使用 foreach,foreach 的用法各位 wiz 们应该也已经知道了吧。 理论上 foreach 在通常条件下执行效率不会比 while 差。 : 3.3. 开关 (SWITCH) : switch 叙述应该尽可能的用来取代 'if-then-else' 型结构. 因为新的 driver : 都有对 switch 叙述作相当程度的简化. 另一方面, 用 switch 叙述的程试码看来 : 也比较 '乾净'. : 用 switch 叙数的另一个优点是 `状况范围' (case range) 的支援. (一个 C 没有 : 的特色). 如果你的测试设定如下面的范围: : case 1: case 2: case 3: : 可用以下代替: : case 1..3: : 可能比较有效率 (至少字少打一些) 这是 case by case,有些东西 switch 无法支援。 但不管如何,用到 if...else 的场合,要特别注意.. 一、判断式的先後「判断」─哪些可以尽量挪前判断,哪些   可以尽量挪後判断。 二、如何减化 if..else 的结构。例如底下... if(xxx) { if(ooo1) . . else . . } else { if(ooo1) . . else . . } 它们或许可减化为... if(xxx && ooo1) . . else if(xxx & !ooo1) . . else if(!xxx & ooo1) . . else . . : 4. 结论 : 我确定这份文件中一定有错误, 但是人不是完美的. : 以下的人对本文的 1.1 版提出问题, 意见: : @TMI-2: Blackthorn, Mobydick, Square, Alexus, Amylaar, Darin : @Ivory.tower: Telsin, Vampyr : @Underdark: Cynic, Brian : Luke [Zak]. : 程式的最佳化 : ============== : 在optimizing_code里已经谈到了一些最佳化的简单注意事项。然而最 : 佳化并不是少数adm或大巫师的事,而是每一个巫师写程式都必须注意 : 的事项。因此请大家在完成一个作品时,再花一些时间重看一下程式 : ,看看可不可以 藉由一些简单的更改来避免一些不必要的计算,即使 : 只是节省一两个计算也是好的。当然也不必为了省一点点的计算而花 : 很多时间或是把程式写得很复杂。但是一些简单的原则,或是稍微注 : 意一下,就可能 会有很大的影响。 : 我们来看一个简单的例子,底下是/adm/daemons/aim_d.c其中的一段 : 程式,注意看第七行: : if( random(100) < 30 && !me->query("npc") ) return 0; : 本来放在倒数第五行,也就是变成注解的地方。我看到了,就把它移 : 到现在的位置。这样有什麽差别呢?如果在原来的位置,中间作了一 : 堆关於skill的计算,当此行成立,这些计算都浪费了。而移到第七行 : 的位置,当此行成立,就不会去计算skill,只是更改一下程式的位置 : ,就可以节省许多不必要的计算。这个程式是医生每回合攻击时都会 : 呼叫到,你可以想像原来的写法作了多少不必要的计算。 : int aim_target(object me, object victim) : { : int skill, diffculty; : string loc; : object weapon; : // difficulty for player : if( random(100) < 30 && !me->query("npc") ) return 0; : skill = (int)me->query_skill("anatomlogy"); : loc = (string)me->query("aiming_loc"); : if( !skill || !loc ) return 0; : diffculty = diffs[loc]; : if( undefinedp(diffculty) ) return 0; : diffculty += diffculty*(int)victim->query("aim_difficulty/"+loc)/100; : [中间省略] : skill /= 10; : skill += (int)me->query_stat("int") * 2 + (int)me->query_stat("kar"); : skill -= (int)victim->query_stat("int") * 2 + (int)victim->query_stat("kar"); : // if( random(100) < 30 && !me->query("npc") ) return 0; : if( random(skill) < diffculty ) return 0; : return (int)call_other( this_object(), "hit_" + loc, me, victim ); : } : 在考虑程式最佳化时,先想想这段程式被使用的频率有多高,如果是 : 使用频率很高的程式,那就要多花些心力注意最佳化。一般使用频率 : 较高的程式大略是/adm/, /cmds/, /std下的程式,不过这不是一般 : 巫师可以动的,另外公会的一些程式,武器的特攻、NPC的tactic,以 : 及一些init,relay_message的程式等等。这些都是使用频率很高的程式 一、最佳化通常来自於经验。我 coding 十一年了,哪些东西   可以怎麽写,其实最後都是出於自然。 二、在追求最佳化的过程中,有时会遇到系统能否负荷的问题   ,例如我们将一个 500 次回圈的字串累加,改成让系统去   跑 500 次的 printf,「理论上」这是很不错的写法,但   实际上它可能遇到的问题就是跑三百多次後系统就不跑了   所以追求最佳化的过程不一定都会顺利,这时该怎麽办?   就是不要「最佳」,而是退一步采取「最适写法」。 这同样是 case by case,我在 /cmds/std/_who.c 以及 /cmds/std/_skilldata.c 的写法就不一样,因为我有实   际 try 过,实务上还是要以能跑出「结果」为首要目标   ,然後再 try 几种写法,从中挑出对系统负担没那麽大   、然後又可以得到我们想要的结果,这就是最适写法。 三、通常最佳化会面临的另一个问题,就是某一程式段最佳化   的同时,却在其它的地方用了又耗资源的写法,这使得你   的最佳化的效果无法突显。   所以才说最佳化的工作通常要靠经验,熟悉最佳化,你在   写每一段程式时就自然而然会采取对系统较好的写法,它   到最後甚至会影响你在系统撰写之初,就同时对资料库做   「最佳规划」,我以底下的范例做结尾... mapping seed_data = ([ "rice":(["name":"稻子", 中文名字 "price":5000, 售价 "value":15000, 种子价格 "mature":36000, 10小时 . . ]), ]); 问题:种子资料一定都得像上面那样宣告为双层 mapping 资料吗? 变通方式之一.. mappnig seed_data=([ "rice":({"稻子",5000,15000,36000,....}),]); }); 一、因为栏位"顺序"是固定的 二、这样的资料非常容易宣告,也非常容易做资料增减 有权限的 wiz 可以比较 /d/skill/skill_stat.c 与 /d/skill/skill_guide.c 的资料宣告方式。            Laechan --



※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 61.225.161.60 ※ 编辑: laechan 来自: 61.225.161.60 (01/05 18:16)
1F:推 happyhero :受教了:) 01/05 22:17







like.gif 您可能会有兴趣的文章
icon.png[问题/行为] 猫晚上进房间会不会有憋尿问题
icon.pngRe: [闲聊] 选了错误的女孩成为魔法少女 XDDDDDDDDDD
icon.png[正妹] 瑞典 一张
icon.png[心得] EMS高领长版毛衣.墨小楼MC1002
icon.png[分享] 丹龙隔热纸GE55+33+22
icon.png[问题] 清洗洗衣机
icon.png[寻物] 窗台下的空间
icon.png[闲聊] 双极の女神1 木魔爵
icon.png[售车] 新竹 1997 march 1297cc 白色 四门
icon.png[讨论] 能从照片感受到摄影者心情吗
icon.png[狂贺] 贺贺贺贺 贺!岛村卯月!总选举NO.1
icon.png[难过] 羡慕白皮肤的女生
icon.png阅读文章
icon.png[黑特]
icon.png[问题] SBK S1安装於安全帽位置
icon.png[分享] 旧woo100绝版开箱!!
icon.pngRe: [无言] 关於小包卫生纸
icon.png[开箱] E5-2683V3 RX480Strix 快睿C1 简单测试
icon.png[心得] 苍の海贼龙 地狱 执行者16PT
icon.png[售车] 1999年Virage iO 1.8EXi
icon.png[心得] 挑战33 LV10 狮子座pt solo
icon.png[闲聊] 手把手教你不被桶之新手主购教学
icon.png[分享] Civic Type R 量产版官方照无预警流出
icon.png[售车] Golf 4 2.0 银色 自排
icon.png[出售] Graco提篮汽座(有底座)2000元诚可议
icon.png[问题] 请问补牙材质掉了还能再补吗?(台中半年内
icon.png[问题] 44th 单曲 生写竟然都给重复的啊啊!
icon.png[心得] 华南红卡/icash 核卡
icon.png[问题] 拔牙矫正这样正常吗
icon.png[赠送] 老莫高业 初业 102年版
icon.png[情报] 三大行动支付 本季掀战火
icon.png[宝宝] 博客来Amos水蜡笔5/1特价五折
icon.pngRe: [心得] 新鲜人一些面试分享
icon.png[心得] 苍の海贼龙 地狱 麒麟25PT
icon.pngRe: [闲聊] (君の名は。雷慎入) 君名二创漫画翻译
icon.pngRe: [闲聊] OGN中场影片:失踪人口局 (英文字幕)
icon.png[问题] 台湾大哥大4G讯号差
icon.png[出售] [全国]全新千寻侘草LED灯, 水草

请输入看板名称,例如:BuyTogether站内搜寻

TOP