2009年4月25日星期六

xsl递归

昨天晚上我们项目组的一个同学希望我将我们的配置页面的列表修改一下,他提出了两个要求:

1、在传给我们一些字符串时,要是不传东西我们,我们列表中要显示未绑定,传的字符串过长时,要显示其中的一部分,后面内容省略。

2、有一个列表的显示值的底层本应发字符串给我们的,他们却是发一个ulong的数字给我,其中从最低位到从左往右第四位,分别表示的一种状态,这样一组合就有16种情况,要我们上层去将数字转化为相应的页面显示。

第一个问题比较简单,我们在写自定义列的时候,用xpath的一个函数string-length去获取字符串的长度,然后再用<xsl:choose>判断一下就可以了,代码如下:

<xsl:choose>

<xsl:when test="string-length($str) > 14"><!--$str可以使xpath路径-->

<xsl:value-of select="substring($str,1,14)"/>

<xsl:value-of select="'...'"/>

</xsl:when>
<xsl:when test="string-length($str) = 0">

<xsl:value-of select="xpath"/>

</xsl:when>

<xsl:otherwise>

<xsl:value-of select="$str"/>

</xsl:otherwise>

</xsl:choose>


第二个问题就比较麻烦了,(要是用C,就相当简单了,可是对于我们页面上来说,还是不那么容易的)看到第二个问题,我想到的方法有两种,第一种,用js获取值,然后进行转化,在动态的赋给每一个table中的那一列数据。用js写的话,会比较容易,基本语法和C一样,在页面加载后调用的初始化函数中去初始化那些显示值,这样首先用js去存储这么多数据就是一种浪费空间,然后就是对于行数比较多的,这个也会降低速度。那么就自然的引出了第二个方法,用xsl,结合xpath的方法和它的一些逻辑函数,这个实际上也是包含两种方法的,一是用xml将16种情况的显示值全部列出来,然后到时候通过取集合中的元素的方法就可以了,我个人不太喜欢这样做,因为不通用,要是有一个状态位数更多的情况就又得修改。于是我选择的是让xml存储的是每一位所代表的状态字符,然后通过判断传上来的ulong数据,对这些数据进行组装。

所以,一开始我就决定了用最后一种方法,今天刚调试出来,我是怎么做的呢?


我刚开始试过用<xsl:variable>去存储那些ulong值,然后通过修改变量的值来处理的方法,空口说不好,写出下段代码帮助理解:

<xsl:variabe name="str" select="''"/><!--str用于存储转化后的字符-->

<xsl:variable name="num" select="."/><!--用num变量存储那个ulong型数的值-->

(xsl和xpath中没有移位这种操作)下面的逻辑用伪码表示

if($num-8>0){$num = $num-8; $str = $str+"相应字符串"}

if($num-4>0){$num = $num-4; $str = $str+"相应字符串"}

if($num-2>0){$num = $num-2; $str = $str+"相应字符串"}

if($num-1>0){$num = $num-1; $str = $str+"相应字符串"}

至此,str就保存了完整的转换后的字符串了。

想的很轻松,写成了之后,页面什么显示都没有,在调试了不少时间,发现了原来xsl中的变量是不允许改变的。这个实在是有点“没人性”,好端端的变量名,只允许一次赋值,又不能够改变,这算什么变量嘛,干脆叫宏、常量得了,^_^

这下我就晕掉了,变量竟然不能够改变,我们如何处理呢,岂不就是宣告此路不通,这时,想到了强大的call-template,apply-templates这些类似函数的东东。

竟然通过变量改变搞不定,我也来学学我们这个项目中随处可见的call-template,
apply-templates这样的东西,用递归来帮助解决问题。

先把调用代码的主体copy看看

<xsl:variable name="str">

<xsl:call-template name="findGlobleString">

<xsl:with-param name="num" select="$num"/><!--要转化的ulong型的数字-->

<xsl:with-param name="step" select="'8'"/> <!--解析开始的最高位,二进制"1000”-->

<xsl:with-param name="maxstep" select="'16'"/><!--能够解析数字的上限+1-->

</xsl:call-template>

</xsl:variable>

下面我将template用伪码写出来,便于理解,更加简洁

<xsl:template name="findGlobleString">

<xsl:param name="num"/>

<xsl:param name="step"/>

<xsl:param name="maxstep"/>


/*伪码逻辑 begin*/

if($num >= $step and $num < $maxstep)//如果num的step位上为1

{

if($step == $maxstep/2) 打印相应字符串


else if($step == $maxstep/4) 打印相应字符串

...


else if($step == 0) 输出空


if($step / 2 >0)//如果step不在最低位,继续调用自己

{

/*调用自己

传参为 1.$num-$step作为num参数传入

2.$step/2 作为step参数传入

3.$maxstep 作为maxstep参数传入

*/

}

else//step已在最低位,解析结束

{

打印空

}

}

else if($num =0 or $step =0)//解析结束标志

{

打印空

}

else if($step < $maxstep and $step>1 and $num<$maxstep)//num的$step位上数字为0,且不是最低位,$step右移一位,再次调用自己解析

{


/*调用自己

传参为 1.$num作为num参数传入

2.$step/2 作为step参数传入

3.$maxstep 作为maxstep参数传入

*/

}

else

{

打印错误信息

}


/*伪码逻辑 end*/



</xsl:template>

这样执行完这些代码之后str这个variable种存储的就是我们想要转化的字符串了。同时这个template的可扩展性也是较强的,对于位数更多的情况,直接将传入的step参数和maxstep参数做相应的修改就可以了。

上面我写的这些逻辑符都是xsl中有的,这里就不贴出源码了,要转化很容易。自己对递归也没多少认识,有更好办法的,欢迎指教!

2009-04-25


……

[阅读全文]

2009年4月10日星期五

强大的二进制数

今天实验室轮到我来发“每日一招”,(ps:解释下,这是我们项目组的一个内部活动,用来让大家在工作中将自己各方面好的经验和方法共享,同时也能活跃实验室的气氛),想了一会儿找不到什么好的东西,就将我当初在电视上看到的李开复在面试清华北大的两名计算机专业的学生时问的一个问题,如下:

有1000个苹果,现在给你十个箱子,将所有的苹果都装到箱子中,假设每个箱子都是可以装任意多的苹果的,现在要你提出一种装苹果的方案,使得要任意个苹果,都可以用其中的几个箱子搭配给出!

实验室的童鞋们果然都很聪明,很多人都在十分钟内就给出了正确的答案。同时有另外一个同学也由此引出了第二个问题:

一个奴隶主有1000坛酒,其中有1坛被人投了毒(很慢性的毒)。现在他要用他的奴隶来试毒,请问用怎么样的方法可以用尽量少的奴隶并且最少的时间把有毒的那坛找出来?

第一个问题的答案很简单,估计大部分人也能够很轻松的解决,就是让箱子装苹果的个数依次为1,2,4,8,......256, 489,大概原理就是1000个苹果用二进制表示的话是有十位,511用2进制表示是9个1,这样如果我们用前九个箱子装上2进制前九位每位数字代表的大小,我们就可以用其中的组合配出任意的一个<=511 的数了,再将最后的苹果数装入到最后的一个箱子中,如果需要的苹果数大于511而小于1000,就可以直接先将最后一个箱子的苹果给出,这样剩下来需要的苹果数就必然在511内了,这样就又可以由前面九个箱子组合配出。

第二个问题我刚开始始终不明白,只知道这题肯定与第一题极为相似,但是想了很久都不会(sigh!),最终还是请教他才得以解决。不得不承认二进制数是多么强大。题目是这样解决的:
首先呢,对1000坛酒编号,二进制号,十位即可,如“1010111010”,全部表示成十位,不足的补零。然后拿出十个奴隶,让第一个奴隶去喝把所有的编号右边第一位为0的酒喝一口,这样如果他死了说明毒酒编号的最后一位为0,否则为1;然后让第二个奴隶去将编号右边第二位为0的酒喝一口,这样就又可以确定毒酒右边第二位的数字,依次下去,就可以确定毒酒的编号了,看到没有,当我们这样分配后,可以在发现一个奴隶毒发身亡后就确定毒酒的编号了,效率够强大吧!
二进制数果真是强大!
……

[阅读全文]

两道程序员面试题

1.能否不利用额外的辅助空间,将两个int型数交换顺序?

2.现有两根不均匀的香,它们每根都能燃烧整整一个小时,现在给你两跟这样的香,你能用它确定15分钟的间隔吗?

上次跟一个同学一起去吃饭,他问了我以上两个传说中是面试题的问题,觉得很有意思!不过可惜的是这两题是在他的提示下才搞定了。

第一题相对容易一点:通过下面三步操作就可以搞定了
a = a + b;
b = a - b;
a = a - b;
但是这个我感觉是有点问题的,因为要是a,b相加正好超过了int的上限,岂不是出现数据丢失,何谈两者交换呢?

第二题有点像脑筋急转弯,我费了好大劲都想不出来,在同学提醒了我一炷香两头一起烧就能确定半个小时了,我才恍然大悟。想了一会,便给出了答案:先将一炷香两头同时点燃,这是也将另一根香的一头点燃,在第一根香烧完后,就只有确定了第二根香还能烧半个小时了,这时将它的另外一边也点燃,它就只能支持15分钟了。

两道题其实都比较简单,但是需要脑袋要灵活,善于跳出常规的思路!^_^
……

[阅读全文]