显示标签为“Code”的博文。显示所有博文
显示标签为“Code”的博文。显示所有博文

2010年5月20日星期四

用 Google Code 管理与发布 Wiki

由于 Dropbox 的墙掉,https 协议访问 dl 或 dl-web 子域 的方法也随之失效,虽然可以通过修改 hosts 来继续同步文件,但是 Public 目录再也 不能输出文化了。

我之前 在 Dropbox Public 目录搭建的博客和 Wiki 系统 也无法在线浏览了,为了能够继续 辅助文化局输出文化,我便利用万恶的资本主义国家 的 Google Code 来为我们服务了。

由于可以继续使用 Dropbox 来自动同步私有文件,所以可以保持 .wiki 文件在 Dropbox 中,其他自动同步的软件也可以用来做类似的事情。

将 Vimwiki 的 path_html 修改为 Google Code 的某个 svn 或 Hg 目录:

let g:vimwiki_list = [{...},
\ {...},
\ {...},
\ {
\ 'path' : 'D:\My Dropbox\blog',
\ 'path_html' : 'D:\hotoo\blog',
\ 'html_header' : 'D:\My Dropbox\blog\template\header.tpl',
\ 'html_footer' : 'D:\My Dropbox\blog\template\footer.tpl'
\ }
\ ]

虽然 Vimwiki 目前的 toHTML 方法还不支持重新生成仅更新过的 wiki 文件,但是 svn 可以判断文件是否有真正被修改过。

另外 http://hotoo.googlecode.com/svn/wiki 目录存放的是 Google Code 的 Wiki 文件, 这里面的 wiki 文件无需通过 Vimwiki 导出 HTML,Google Code 会自动完成这一工作, 并可以通过 http://code.google.com/p/hotoo/w/list 浏览。而 Vimwiki 是 Google Code Wiki 语法的一个子集,所以基本可以天衣无缝的配合使用。

这也是不错的一个方案,除了需要 commit 之外,Google Code 比 Dropbox 的 Public 有过之而无不足,域名也是杠杠的。

另外不小心发现还有其他的托管方案,让防火墙筑的更高些吧,当局者把自己当猪圈养起来 比较好,做个专职的脑子又笨,目光又短浅的墙脚之猪也可以提高幸福指数的。

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' 未定义

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. 根据目标字符串,选择合适的实现。