2009年1月11日星期日

去除MSN 9的广告

今天发现msn 9可以用在Windows Server 2008上了,像我这么喜新厌旧的家伙,怎么会放过她,立马上。

客观的说,msn 9做的还是很棒的,已经能够支持群聊,界面也大有改善,不过它那个超级大而又吸引眼球的广告仍是那么讨厌,搜了一下,发现1个2个...方法,各有优缺点,而又都没有去除干净:第1个只去除了主窗口(好友列表)下的大家伙,聊天窗口的广告没有被去除,而第2个方法功能比较饱满,但是不知道什么原因(可能是之前使用了第1中方法),开始的时候主窗口的广告反选第一个去广告选项可以去掉,后来无论正选反选都不行了,解决办法是先使用第2种方法,再使用第1种方法。

由于第2种方法的版本和最新的(14.0.8050.1202)有点不同,这里贴图如下:
这里有两个(第2个和第9个)去除广告的选项(Remove Advertisement),分别去除的是主窗口和聊天窗口的广告。点击左侧的选项,可以在右侧预览。


2009年1月6日星期二

Javascript无块级作用域

最近在做一系列Javascript压缩工具,语法压缩,语义压缩,字符串压缩均有涉及(p.s.有趣的是,压缩变量名之类的“有损压缩”不影响代码执行,但是字符串压缩这样的“无损压缩”却总是需要解压消耗)。

在实现压缩局部变量名时,最初的实现是将 if/else, for/in, do/while, switch/case/default, try/catch/finally, with和Object实例对象(后面统称为“块级作用域”)与function一样,都作为独立的作用域,但是测试发现在Javascript中并不是这么回事。

尝试着在块级作用域里声明定义变量:
if(true){
var bool=true;
}
document.write(bool); // output:true
会发现输出true,而且不论嵌套多深,也不论使用任何块级作用域进行嵌套,最后变量依然如同在调用处之前,而且同级的作用域中定义的一样;值为在此之前,在块级作用域中所做改变的结果。注意:如果块级作用域未被执行,则其中声明定义的变量会被声明(var name),但不被定义(即未被初始化,此时name为undefined,引用name时不抛异常)。
if(false){var bool=true;}
document.write(bool); // output:undefined
之前虽知道,IE里for(var i=0;...)里定义的i,在for之外也是可以使用的,但是现在才知道这种情况更为猖獗,大出我的意料之外,而更意外的是,IE 7(IE6?), Firefox 3(FF1,FF2?), Opera 9, Chrome 1, Safari 3表现均丝毫不差。

虽然与某些编程规范不同,但是js就是这样了,唉。早知道这样,压缩变量名这部分也就不用那么费劲了,只得重写了。

另外还有一些Javascript作用域方面的文章:
Javascript中的作用域,好像是realazy翻译的,文章很好,虽然有点文不对题(主要阐述this的作用域链问题)。
js变量作用域及可访问性的探讨,详细介绍了各种变量的作用域及其可访问性问题,有部分不准确/正确的,出处未知,遍地都是转载。

附A 测试代码:
// Global Scope
if (true){
var v10 = true;
}else {
var v00 = false;
}
document.write("v10 : "+v10+"<br />");
document.write("v00 : "+v00+"<br />");
document.write("<hr />");

// Function Scope
function functionScopes(){
if (true){
// 以下均执行
var v11 = true;

do{
var v12 = true;
}while (false);

while (true){
var v13 = true;
break;
}

switch (1){
case 1:
var v14 = true;
default:
var v15 = true;
}

try{
var v16 = true;
throw new Error("");
}catch(e){
var v17 = true;
}finally{
var v18 = true;
}

for (var v19=0; v19<1; v19++){
var v1a=true;
}

with(v11){
var v1b = true;
}

function inn1(){
var inn11 = false;
}
}else {
// 以下均未执行
var v01 = false;

do{
var v02 = false;
}while (false);

while (false){
var v03 = false;
}

try{
var v06 = true;
throw new Error("");
}catch(e){
var v07 = true;
}finally{
var v08 = true;
}

switch (1){
case 1:
var v04 = true;
default:
var v05 = false;
}

for (var v09=0; v09<1; v09++){
var v0a=true;
}

with(v01){
var v0b = true;
}

function inn0(){
var inn01 = false;
}
}
document.write("v11 : "+v11+"<br />");
document.write("v12 : "+v12+"<br />");
document.write("v13 : "+v13+"<br />");
document.write("v14 : "+v14+"<br />");
document.write("v15 : "+v15+"<br />");
document.write("v16 : "+v16+"<br />");
document.write("v17 : "+v17+"<br />");
document.write("v18 : "+v18+"<br />");
document.write("v19 : "+v19+"<br />");
document.write("v1a : "+v1a+"<br />");
document.write("v1b : "+v1b+"<br />");
document.write("<hr />");
document.write("v01 : "+v01+"<br />");
document.write("v02 : "+v02+"<br />");
document.write("v03 : "+v03+"<br />");
document.write("v04 : "+v04+"<br />");
document.write("v05 : "+v05+"<br />");
document.write("v06 : "+v06+"<br />");
document.write("v07 : "+v07+"<br />");
document.write("v08 : "+v08+"<br />");
document.write("v09 : "+v09+"<br />");
document.write("v0a : "+v0a+"<br />");
document.write("v0b : "+v0b+"<br />");

document.write("<hr />");
try{alert(inn11);}catch(e){document.write((e.message||e)+"<br />");}
try{alert(inn01);}catch(e){document.write((e.message||e)+"<br />");}
}
functionScopes();

document.write("<hr />");
try{alert(v11);}catch(e){document.write((e.message||e)+"<br />");}
try{alert(v12);}catch(e){document.write((e.message||e)+"<br />");}
try{alert(v13);}catch(e){document.write((e.message||e)+"<br />");}
try{alert(v14);}catch(e){document.write((e.message||e)+"<br />");}
try{alert(v15);}catch(e){document.write((e.message||e)+"<br />");}
try{alert(v16);}catch(e){document.write((e.message||e)+"<br />");}
try{alert(v17);}catch(e){document.write((e.message||e)+"<br />");}
try{alert(v18);}catch(e){document.write((e.message||e)+"<br />");}
try{alert(v19);}catch(e){document.write((e.message||e)+"<br />");}
try{alert(v1a);}catch(e){document.write((e.message||e)+"<br />");}
try{alert(v1b);}catch(e){document.write((e.message||e)+"<br />");}

try{alert(v01);}catch(e){document.write((e.message||e)+"<br />");}
try{alert(v02);}catch(e){document.write((e.message||e)+"<br />");}
try{alert(v03);}catch(e){document.write((e.message||e)+"<br />");}
try{alert(v04);}catch(e){document.write((e.message||e)+"<br />");}
try{alert(v05);}catch(e){document.write((e.message||e)+"<br />");}
try{alert(v06);}catch(e){document.write((e.message||e)+"<br />");}
try{alert(v07);}catch(e){document.write((e.message||e)+"<br />");}
try{alert(v08);}catch(e){document.write((e.message||e)+"<br />");}
try{alert(v09);}catch(e){document.write((e.message||e)+"<br />");}
try{alert(v0a);}catch(e){document.write((e.message||e)+"<br />");}
try{alert(v0b);}catch(e){document.write((e.message||e)+"<br />");}

附B 测试结果(IE 7, FF 3, Opera 9, Safari 3, Chorme 1均同):
v10 : true
v00 : undefined

v11 : true
v12 : true
v13 : true
v14 : true
v15 : true
v16 : true
v17 : true
v18 : true
v19 : 1
v1a : true
v1b : true

v01 : undefined
v02 : undefined
v03 : undefined
v04 : undefined
v05 : undefined
v06 : undefined
v07 : undefined
v08 : undefined
v09 : undefined
v0a : undefined
v0b : undefined

'inn11' 未定义
'inn01' 未定义

'v11' 未定义
'v12' 未定义
'v13' 未定义
'v14' 未定义
'v15' 未定义
'v16' 未定义
'v17' 未定义
'v18' 未定义
'v19' 未定义
'v1a' 未定义
'v1b' 未定义
'v01' 未定义
'v02' 未定义
'v03' 未定义
'v04' 未定义
'v05' 未定义
'v06' 未定义
'v07' 未定义
'v08' 未定义
'v09' 未定义
'v0a' 未定义
'v0b' 未定义

2009年1月2日星期五

开放篮球场地.地标工程

作为业余篮球爱好者,作为他乡游子,每到一个陌生的地方,都想尽快找到一个能施展球技、或者仅是活动一下筋骨的场地。就地解决当然也可以,但是如果有自由开放的美好世界,为什么不加入进去呢?

一般来说,大多数开放的篮球场地都在学校里(大学最多,中小学一般不对外开放),有一些公益场所也有简易的设施,所以希望能有一些学生朋友能给予帮助,我将尽力争取得到帮助。



基于这样的想法,我展开了一个“开放篮球场地.地标工程”的想法,为广大篮球爱好者服务。首先做好了几个我所知道的地标:http://labs.xianyun.org/OpenBasketballField/landmark/OpenBasketballField.All.in.One.kmz

将地址复制粘贴到Google Maps谷歌地图)搜索栏,回车即可;或是下载到本地,使用Google Earth打开。


全国现有完整地标(All in One):
http://labs.xianyun.org/OpenBasketballField/landmark/OpenBasketballField.All.in.One.kmz

杭州:
http://labs.xianyun.org/OpenBasketballField/landmark/HangZhou.kmz

北京:
http://labs.xianyun.org/OpenBasketballField/landmark/BeiJing.kmz


另有在线地标,直接Google Maps上编辑即可(所有登录了Google账户的网友都可以编辑)
--
主要定位开放的篮球运动场地,默认为室外,尤其是免费的场地,室内或收费场地另附说明。

标题格式:地名[.开放封闭未知][.室外室内][.免费收费]

使用默认图标(期待志同之士提供更形象的图标)
红色不带圆点:
蓝色不带圆点:
紫色不带圆点:开放程度未知
红色带圆点:开放,人气旺
蓝色带圆点:开放,人气一般
紫色带圆点:
黄色不带圆点:封闭

以卫星地图为准。所有地标和文字描述仅作参考,欢迎修正(所有登录了Google帐号的朋友都可以进行修订)。

在线地标列表:
http://maps.google.com/maps/user?uid=108314985261981078822&hl=en

北京:
http://maps.google.com/maps/ms?hl=en&ie=UTF8&lr=lang_zh-CNlang_zh-TW&oe=UTF8&msa=0&msid=108623690175354072173.00045480d1fa17e9056ac

杭州:
http://maps.google.com/maps/ms?hl=en&ie=UTF8&lr=lang_zh-CNlang_zh-TW&oe=UTF8&msa=0&msid=108623690175354072173.00045f6778d694e0d3cc3

2008年12月30日星期二

Family Tree Builder 3.0发布

收到MyHeritage.com的邮件通知:Family Tree Builder 3.0发布了,比之前29种语言又新增了5种,纵然如此,亦纵然很久之前就有了MyHeritage中文站,但是软件的中文语言包还是迟迟未出。我之前为2.0版汉化的语言包仍然还可以凑合着用,注意:一些新版里添加的词条,默认显示为TODO。

其他功能没有细查,不知道有什么改善,虽然之前的中文乱码问题仍然存在(还没有中文语言包的情况下,这个可以理解,而且乱码部分不是太多),不过安装程序倒比2.0“大气”了些,而且默认选中将MyHeritage.com设为主页的选项,很智能的,另外还默认选中自动将族谱/家谱发布到网站的选项也很智能。

2008年12月29日星期一

Javascript String 方法效率大比拼

最初是通过梅子梅花雪)关于大型字符串拼接效率(12)的研究得到启发,最近又看到never-online的从trim原型函数看js正则表达式的性能 ,里面有介绍正则表达式效率陷阱等问题,并提出解决方法。我向来对这些鸡毛蒜皮感兴趣,也开始对大型字符串各种方法实现的效率进行比较,并尝试提高这些方法的效率。

1. 大型字符串拼接
梅子所言,使用数组的join方法确实是最好的实现,可以根据这个思路设计StringBuilder, StringBuffer类。



2. 大型字符串trim
其实never-online在他的文章里有一些说的不准确的地方,代码也不算很精炼。既然这是鸡毛蒜皮的小事,这些零碎东西当然要斤斤计较了。
我很久以前有收集到这样一些实现,I:
String.prototype.trim = function(){
return this.replace(/(^\s+)(\s+$)/g, '');
};
为了避免正则表达式使用括号带来的消耗,可以写成这样,II:
String.prototype.trim = function(){
return this.replace(/(?:^\s+)(?:\s+$)/g, '');
};
另外有一套实现是这样的,III:
String.prototype.lTrim = function(){
return this.replace(/^\s+/, '');
};
String.prototype.rTrim = function(){
return this.replace(/\s+$/, '');
};
String.prototype.trim = function(){
return this.lTrim().rTrim();
};
其实调用函数也会多少有一点消耗,写成这样或许会快一点点(开个玩笑,这样写会带来一些冗余代码,这时候就需要基于效率(时间)、代码量(空间)和可维护性方面的考量了),IV:
String.prototype.trim = function(){
return this.replace(/^\s+/, '').replace(/\s+$/, '');
};
后来我对正则表达式有了更多的了解,知道了贪婪与非贪婪匹配,于是自作聪明写了这一段:
String.prototype.trim = function(){
return this.replace(/^\s*(.*?)\s*$/, "$1"); // 两端空白字符贪婪匹配,中间字符非贪婪匹配。
};
我曾经为这段代码自鸣得意了好长一段时间,不过后来想到点号不包括换行符,字符串中间有换行符时,返回值就不正确了,于是不情愿的改成这样(多行模式效率也很低),V:
String.prototype.trim = function(){
return this.replace(/^\s*((?:.\n)*?)\s*$/, "$1");
};
谁知,这样的代码遇到大家伙时效率会一落千丈,哎,失败。

原以为String.replace方法比String.substr、String.substring效率低,于是想,只使用正则表达式获得两头(或者一头)的索引位置,然后使用substring方法取出子串,VI:
String.prototype.trim = function(){
var l=this.length;
/^[\s]*/.test(this);
// /(?=[^\s])/.test(this);// -- never-online
var s = RegExp.lastIndex;
if(1==s && !Char.isBlank(this.charAt(0)))s=0;
if(s==l){return '';}
// /\s*$/.test(this);
// var e=RegExp.index;
var e=$lastIndexOf(function(c){return !Char.isBlank(c);});
e=-1==e?l:e+1;
return this.substring(s,e);
};
而最土的方法,莫过于两头都使用循环获得索引了,VII:
String.prototype.trim = function(){
var f=function(c){return !Char.isBlank(c);};
var l=this.length, s=this.$indexOf(f), e=this.$lastIndexOf(f);
if(-1==s)s=0;
e= -1==e?l:e+1;
return this.substring(s, e);
};
代码里老是for啊for的一大串,为了节省字节,而且有可能的话,也准备再优化一下循环,就实现了$indexOf和$lastIndexOf两个方法,可以传递一个返回boolean值的函数作为参数(本来还想也支持正则表达式参数的,想想以前扩展indexOf和lastIndexOf方法后的效率,就算了),这样就可以求得第一个非空白和最后一个非空白字符的位置了。
String.prototype.$indexOf = function(f){
for(var i=0,c,l=this.length; i=0; i--){
c=this.charAt(i);
if(f(c)){return i;}
}
return -1;
};
String.prototype.$lastIndexOf = function(f){
for(var i=this.length-1,c; i>=0; i--){
c=this.charAt(i);
if(f(c)){return i;}
}
return -1;
};
至于说要扩展到支持更长子串和起始索引,以后有需要再说了(顺便说一下,子串越长,有优化算法可以得到更高效率)。
另一个辅助方法:
var Char = {
isBlank:function(c){
//return /\s/.test(c);
return ' '==c '\t'==c '\r\n'==c '\n'==c '\r'==c;
}
};
到了永不在线(Google Translate翻译为“永远在线”)的算法,VIII:
String.prototype.trim = function(){
var s = this.replace(/^\s+/, '');
var l=s.length, e=l;
if(0==l){return '';}
for(var i=l-1; i>=0; i--){
if(!Char.isBlank(s.charAt(i))){
e=i+1;
break;
}
}
return this.substring(0,e);
};
最初这个算法让我很兴奋,直觉上,感觉这样效率肯定要高,不过事实并不是这么简单。

说到这些实现的效率,无法一概而论,因为不同的字符串,它们的效率比也大不同,甚至异乎寻常。

影响trim方法效率的,主要与字符串的总长度,前面空白字符串长度,后面空白字符串长度,以及前中后的比例有关。详细的效率对比表有时间再上,这里只简要提一下:

对于较小的字符串,各种实现都有不错的表现,而对于大型字符串,则实现III, IV表现较为稳定,甚至可以处理超大型字符串(修正:之前有误写成I,II两个较为稳定)。




3. 大型字符串字节长度
即双字节长度为2。注意:这个提法其实也不正确,Javascript是使用Unicode字符集的,所有的字符都(有可能)是双字节字符。将汉字等转换为双字节长度主要是为了某些应用。
最土的方法还是循环遍历所有字符,I:
String.prototype.bytes = function(){
var l=this.length, r=l, n=0xff;
for(var i=l; i>=0; i--){
if(this.charCodeAt(i)>n){
r++;
}
}
return r;
};
这里判断字符是否双字节有很多方法,效率较高的之间相差(大概)不大。

另一种实现则看起来很轻灵,寥寥几行,II:
String.prototype.bytes = function(){
return this.replace(/[^\x00-\xff]/g,"xx").length;
};
多动脑子,则想法愈多(也常把简单的事情复杂化),我想如果可以快速取得表达式(双字节/单字节)匹配次数,两值相加应该比较高效,III:
String.prototype.bytes = function(){
return this.length+this.replace(/[\x00-\xff]/g,"").length;
};
IV:
String.prototype.bytes = function(){
return this.length+(this.match(/[^\x00-\xff]/g)"").length;
};
另外看到梅花雪用数组能提供字符串拼接速度,也想:把字符串split为数组,不想对大型字符串而言,这split一步就慢得不行。

bytes方法的效率:使用Javascript脚本循环大型字符串(I),确实远不如内置的replace方法(II)快,而使用正则表达式match方法(IV)又比replace方法(III)稍快,排名第二。

总结:
1. replace方法因匹配而被替换的子串愈长,效率愈低。
2. 根据目标字符串,选择合适的实现。

2008年12月24日星期三

foobar2000 Ctrl+Alt+Z 之灵异

最近发现一个怪异的事情,按Ctrl+Alt+Z键,不能够调出TM(QQ)的对话框,并且,当焦点在编辑器等可输入文本的区域时,按这个组合键会粘贴出一行字符(后来确认是Foobar2000当前播放曲目信息),Google搜到唯一的一个谈论这个的帖子,结论好像是说卸载(非禁用)Foobar2000的foo_amip插件就可以了。

p.s. 这个插件挺有用,卸载了可惜,还是该TM的快捷键算了。

2008年12月9日星期二

2=1?

花了好长时间才解出一个初中生都会的算术题,囧啊。

微趣上看到一组数学漫画,里面有这样一张:
最初一步步看下来,貌似还真的没有错,,,但是题解结论却得到一个驳论。回头想了好久,才看出端倪。

这里的算式是有条件的,即:a!=0, a!=b,但是这里的前提条件戏剧性的又是a=b,所以最长的那个表达式两边是不可以化简的(a-b=0,两边=0,0=0)。。。

2008年12月4日星期四

囧:梦见心脏掉出来了。。。

这两夜均是通宵过来而白天睡觉(生活太没规律),但是今天做了一个邪乎的怪梦:

大概是看《倚天屠龙记》的缘故,梦见了张三丰(已成仙),而且我帮他做了一件什么事,并(一厢情愿的)称他为师父。
后来,大概是床上什么东西顶住了心脏部位,我觉得心脏不对劲,轻轻掀开T袖,发现心脏滑出来了。
我怕心脏完全掉出来,也不敢有大动静,甚至都没看胸口现在是什么样子了。
我隔着衣服轻轻托着白萝卜一样的心脏,心急,四处寻找师父。
好久,终于在一个小池子旁边寻见师父,连连喊师父救命。
师父把我之前胡乱塞在胸口的萝卜型心脏取出来,又植入进去。
后来醒来,并没有发现能顶住身体的物件,难道是自己的拳头?
直到现在,还觉得心脏有点异样。

口才文笔不好,这么好的题材,都不能讲出好故事,甚至连文字都不流利,只能记流水帐了,惭愧。