语料库的开发见解

状态
主题已关闭, 停止回复.
申明:本人刚入门,从事计算机软件开发工作不久,以下叙述属个人意见,如有雷同,纯属意外。
刚来不久,初略的查看帖子,和一些个人经验来开,其实语料库的建设与发展主要的核心技术存在于检索部分,如何能让检索的信息准确和又效率高呢。这个就是技术的核心内容了,在数据概率统计方面其实不是开发的重点,因为有很多前人通过理论和实践总结出了很多计算公式,但计算公式最后得到的结果是取决于得到的数据是否准确或者是人们期望的信息值,目前国内有不少开发出来并且比较好的语料库,但比起国外的语料库来说无论是技术还是数据都还是差了一些,这个不能怪谁,因为毕竟那是别人母语,不能相提并论。我希望在做语料库研究的时候更多的不是如何使用语料库,而是如何去实现语料库,讨论更多的是在关键技术上的问题,例如我们可以将google所开发的“庖丁解牛”框架来做中文分词的处理和研究,不过不算很好,需要加以改进,对于语料库方面,GATE这个框架能实现多种功能,并附有源代码,在检索部分目前就java技术上就有lucene这个免费开源框架可做检索,虽然效率不差但就分词这点还是有比较大的问题,所以分词和智能联想又是语料库的一个最大问题,也是AI(人工智能)的一个大问题,所以我们也需要从这方面入手。其次就是数据处理的问题,当我们的数据拥有上亿条库时,需要对数据处理速度做优化,我还记得《新视野大学英语》辅助网站上有个搜索引擎,当我们搜索一个非常常见的单词时例如:hava,结果肯定是上万,但检索和输出的速度很慢,所以我们可以考虑采用某些框架来执行,例如java中的hibernet开源框架,这个框架可对数据库进行搜索,效率也高(效率是相对的,通过编写高质量的代码可以提高部分速度),同时需要数据结构掌握比较好的人来对存储结构和存储过程进行优化已达到更快检索速度。
综上所述,我们需要几方面甚至更多方面相关人员的配合才能达到一款软件的成熟开发,所以就像计算机一样,要想语料库在中国有飞速的发展就需要很多人参与与贡献而不是完全商业化运作,当然我也可以是一个贡献者,当开发团队需要我的时候,我们需要我们自己的技术发展,在目前世界上许多优秀的开发框架中寻找并根据我们的需要来改善框架甚至研发出自己的框架。
注:框架(Framework)是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法;另一种定义认为,框架是可被应用开发者定制的应用骨架。前者是从应用方面而后者是从目的方面给出的定义。
 
回复: 语料库的开发见解

Gate和Lucene都是基于java平台的,请教楼主,PERL在这方面有前途吗?最近在学perl, 好像不太适合做大型项目是吗?
 
回复: 语料库的开发见解

PERL是一个功能很强大的脚本语言,你要注意一点是脚本语言,就目前的情况下,因为它是脚本语言,运行速度慢而且不够稳定,所以不能很好够胜任巨大的后台数据分析和处理,而java、C++等是编译性语言,系统能得到相对的稳定性,不过就目前PERL的发展前景非常可观,建议继续研究,真正的大型项目并不光是一种语言做的,每个语言在开发过程中为产品带来的功能和长处各有不同,而且例如java和PERL是能结合起来开发的,所以不要有这方面的疑问,努力研究PERL
 
回复: 语料库的开发见解

好的,谢谢。java,c++有些恐怖啊,现在学perl蛮享受的,正则用的爽啊。继续努力!
 
回复: 语料库的开发见解

PERL是一个功能很强大的脚本语言,你要注意一点是脚本语言,就目前的情况下,因为它是脚本语言,运行速度慢而且不够稳定,所以不能很好够胜任巨大的后台数据分析和处理,而java、C++等是编译性语言,系统能得到相对的稳定性,不过就目前PERL的发展前景非常可观,建议继续研究,真正的大型项目并不光是一种语言做的,每个语言在开发过程中为产品带来的功能和长处各有不同,而且例如java和PERL是能结合起来开发的,所以不要有这方面的疑问,努力研究PERL

1)脚本语言不一定比编译性语言慢,主要看算法。
2)脚本语言非常稳定,有时比编译性语言还要稳定;另外java和C++的编译机制完全不同,不可同日而语。
3)脚本语言完全可以胜任海量数据的分析和处理
4)PHP可能比Perl更实用一些
 
回复: 语料库的开发见解

申明:本人刚入门,从事计算机软件开发工作不久,以下叙述属个人意见,如有雷同,纯属意外。
刚来不久,初略的查看帖子,和一些个人经验来开,其实语料库的建设与发展主要的核心技术存在于检索部分,如何能让检索的信息准确和又效率高呢。这个就是技术的核心内容了,在数据概率统计方面其实不是开发的重点,因为有很多前人通过理论和实践总结出了很多计算公式,但计算公式最后得到的结果是取决于得到的数据是否准确或者是人们期望的信息值,目前国内有不少开发出来并且比较好的语料库,但比起国外的语料库来说无论是技术还是数据都还是差了一些,这个不能怪谁,因为毕竟那是别人母语,不能相提并论。我希望在做语料库研究的时候更多的不是如何使用语料库,而是如何去实现语料库,讨论更多的是在关键技术上的问题,例如我们可以将google所开发的“庖丁解牛”框架来做中文分词的处理和研究,不过不算很好,需要加以改进,对于语料库方面,GATE这个框架能实现多种功能,并附有源代码,在检索部分目前就java技术上就有lucene这个免费开源框架可做检索,虽然效率不差但就分词这点还是有比较大的问题,所以分词和智能联想又是语料库的一个最大问题,也是AI(人工智能)的一个大问题,所以我们也需要从这方面入手。其次就是数据处理的问题,当我们的数据拥有上亿条库时,需要对数据处理速度做优化,我还记得《新视野大学英语》辅助网站上有个搜索引擎,当我们搜索一个非常常见的单词时例如:hava,结果肯定是上万,但检索和输出的速度很慢,所以我们可以考虑采用某些框架来执行,例如java中的hibernet开源框架,这个框架可对数据库进行搜索,效率也高(效率是相对的,通过编写高质量的代码可以提高部分速度),同时需要数据结构掌握比较好的人来对存储结构和存储过程进行优化已达到更快检索速度。
综上所述,我们需要几方面甚至更多方面相关人员的配合才能达到一款软件的成熟开发,所以就像计算机一样,要想语料库在中国有飞速的发展就需要很多人参与与贡献而不是完全商业化运作,当然我也可以是一个贡献者,当开发团队需要我的时候,我们需要我们自己的技术发展,在目前世界上许多优秀的开发框架中寻找并根据我们的需要来改善框架甚至研发出自己的框架。
注:框架(Framework)是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法;另一种定义认为,框架是可被应用开发者定制的应用骨架。前者是从应用方面而后者是从目的方面给出的定义。

1)检索一般使用预选生成的索引文件(lucene也是这样处理的),对数据库进行检索是不可取的
2)检索只是语言库研究的一个方面
3)目前国外的语料库技术其实也不是很理想,跟国内相比没有本质的差距
4)中文分词有自己的算法,目前普遍采取基于训练语料的算法,如CLAWS
5) 杨惠中老师的《语料库语言学概论》是本不错的入门教材,如果有兴趣可以读一下
 
回复: 语料库的开发见解

1)检索一般使用预选生成的索引文件(lucene也是这样处理的),对数据库进行检索是不可取的
2)检索只是语言库研究的一个方面
3)目前国外的语料库技术其实也不是很理想,跟国内相比没有本质的差距
4)中文分词有自己的算法,目前普遍采取基于训练语料的算法,如CLAWS
5) 杨惠中老师的《语料库语言学概论》是本不错的入门教材,如果有兴趣可以读一下

Applaud! 一味地追求程序语言和检索算法的改进并一定能带来技术的进步。就拿Mark Daves的BNC来说,速度够快的吧,而制胜的关键并不在于他用了什么神器,用他自己亲口告诉我的话来讲,不要迷信教科书。他的秘诀就在于索引时搞了很多教科书里看来所谓的“多余的表”。既然把能想到的都索引好了,即使是非常少用到的,也只是占了点不值钱的存储空间而已,而碰巧用户检索到时,自然是一触即发。冷兵器也能战胜火枪就在于是否善于使用……
 
回复: 语料库的开发见解

A Little Learning

by Alexander Pope


A little learning is a dangerous thing ;
Drink deep, or taste not the Pierian spring :
There shallow draughts intoxicate the brain,
And drinking largely sobers us again.
Fired at first sight with what the Muse imparts,
In fearless youth we tempt the heights of Arts ;
While from the bounded level of our mind
Short views we take, nor see the lengths behind,
But, more advanced, behold with strange surprise
New distant scenes of endless science rise !
So pleased at first the towering Alps we try,
Mount o’er the vales, and seem to tread the sky ;
The eternal snows appear already past,
And the first clouds and mountains seem the last ;
But those attained, we tremble to survey
The growing labours of the lengthened way ;
The increasing prospect tires our wandering eyes,
Hill peep o’er hills, and Alps on Alps arise !
 
回复: 语料库的开发见解

转载!!能充分说明几种编程语言的效率!因检索文本,所以java速度慢,但如果放在数据库里,那将会是java最快.因转载文本用的是jse1.3版本过老,所以导致速度下降,目前j2se1.6.0.9版本在速度上提高很多,测试速度完全提高。
本文适合初学编程的程序员阅读,它对比了几种编程语言在解决同一问题的时候的运效率。并通过具体的例子进行了量化分析。主要目的是帮助初学者认识各种编程语言的特质,并且能够理性的选择适合的编程语言来进行工作。

  选题
首先的,我们的选题中要使用的各种程序语言的最常用的要素。什么是最常用的要素呢?当然了,大家都有的就是赋值、数组操作、循环、判断等。另外,对IO的操作也是编程语言重要的内容。其次的,操作时间一定要长,否则,对于解释性的语言来说是极不公平的:解释器还没调入内存呢,人家编译派的已经运行完了。最后,就是程序不能太复杂。除了我没有那么大的毅力用各种语言完成一个复杂算法的决心外,程序过于复杂,算法在测试中起的作用就越来越大,影响运行效率的原因也就增加了。算法过于复杂,开发工具的扩展部分用得也就越多。于是就成了语言附加库之间的竞赛了,这是我不愿意看到的。

  考虑上述因素,我设计了一个简单的选题:从指定文本文件中搜索指定字符串,计算个数。并且打印出搜索到的个数作为结果输出。作为程序员的你粗粗过一下脑子,马上会想到这个算法里面包含了条件判断、循环、数组操作等基本的程序语言因素。这满足了上面第一个条件。另外的,为了满足第二个条件,我准备了一个多达2G的文本文件,总共有文本1500万行多。这保怔了足够的运行时间(但应该不会太长),而决不会一眨眼就执行完了。最后的,我们都知道,在文本串里面搜索子串的算法是数据结构课本中的一个典型的例子(考试也经常被考到的),也满足算法简单的要求。同时,为了让每个程序的环境都一样,我得每测试一次就重新启动一次机器,避免CACHE的影响。
  准备

  比赛嘛,就需要公平。首先的,硬件平台要统一。我找了一台看起来还不错的机器(服务器):两颗PIII800,1G内存。操作系统嘛,原来的机器上有新装的Windows2000Server版本。几乎没装什么别的应用。我偷懒了一下,没有重新安装OS,就这样用吧。

  第一个选手:PERL

  如果别人交给我这个题目,我会马上决定用PERL语言来做这件事。这个题目是完全的文本处理问题,还有比用PERL来做更合适的吗?因为PERL是专门为了文本处理而编制的语言。事实上也是这样,我用了2分钟,写了几行代码,就轻松实现了这个问题。这也说明了,选择适用的编程语言工具,比选择喜爱的工具更重要。



#!/usr/bin/perl
$filename="d:\access.log_";
$count = 0;
open(FILE , "<$filename");
while(<FILE>)
{
@match_list = ($_ =~ /HIT/g);
$count=$count+@match_list;
}
close(FILE);
print "Count = $count ";
exit


  PERL是一位语言学家Larry Wall发明的,事实上,早期这种语言是专门用于在UNIX平台处理文字文件的(Perl=Practical Extraction Report Language:实用报表析取语言)。后来人们发现有大量文本构成的HTML页面用PERL来做CGI程序生成动态页面再合适不过了。因为互联网的兴起,PERL跟着发大了起来。这种语言的语法和C语言基本类似,因此比较好掌握,并且的,其关于"正则表达式"处理的强大功能目前基本上无人能够望其项背。事实上,类似于"过滤出含有TOM或者ABC的、并且后者的第一个和第三个字母大写,前者最少出现2次,后者出现5次、而且中间间隔8个或4个字母或空格的文本行"。我猜你正在反复的揣摩这句话,事实上,这就是所谓正则表达式,这样的问题,在PERL只需要一行语句就可以完成。在C语言中需要多少语句才能实现呢。

  我略略解释一下上面的程序,让没有用过PERL语言的程序员也有个感性认识。

  第一行是在UNIX中才用得到,因为PERL是一种基于解释的脚本语言。

  第四行是打开文件

  下面的循环是一行一行的读文件的内容。循环中间的第一句话是把凡是文本行中含有的HIT全部放到一个数组中;循环中中的第二句话是统计一下刚才的数组中有几个HIT,然后累加起来。循环完成了,我们的任务也就完成了。怎么样,很简单吧?"/HIT/g"就是最简单的正则表达式。br />
  现在的PERL语言早已经不是原来的脚本语言形象了,现代PERL几乎具备了其特语言的所有特性,并且的在模块的功能帮助下,可以实现很大的应用。而且还增加了一些面向对象的特点。尽管大多数人仍然在用它处理大量的文本,但也有使用PERL完成大型应用的,尤其是在WEB方面。值得一提的是PERL也是一个跨平台语言。

  我的这个程序在测试平台上,使用PERL5.8解释器,用了8分18秒08完成了1500万行文本的扫描,并得出了正确的结果。

  第二个选手:纯C

  也许年龄大了,但是我真的很喜欢C语言。而且我最喜欢的就是使用指针和强制类型转换来任意操作数据。我甚至会在程序里通过指针手工拼凑一个长整性的数据。说句可能引起争议的话,我觉得JAVA语言抛弃可爱的指针的做法基本上就是逃避。因为掌握不好就不用,到头来就是牺牲了效率。

  本文这个题目,用C语言来实现应该还是比较不错的选择。下面的代码就是在VC下面实现的纯C代码的字符串搜索程序(为了避免图形界面的干扰,一律做成控制台程序)。编译的时候使用速度优先编译选项。


#include <stdio.h>
#include <string.h>

void main()
{
int len=2048;
char filename[20];//文件名
char buff[10000];//文件缓冲区
char hit[5];
FILE *fd;
int i,j,flag=0,over=0;
int max,readed;
int count=0;//最后的结果
strcpy(&filename[0] , "d:\access.log_");
strcpy(&hit[0] , "HIT");
buff[0]=0x0;
buff[1]=0x0;
//打开文件:
if((fd = fopen(&filename[0] , "rb"))==NULL)
{
printf("Error : Can not open file %s ",&filename[0]);
}
//读取文件内容
while(over != 1)
{
readed = fread(&buff[2] , 1 , len , fd);
if(readed < len)
{
over=1;
max=readed;
}
else
{
max=len;
}
for(i=0;i<max;i++)
{
for(j=0;j<3;j++)
{
if(hit[j] != buff[i+j])
{
flag=0;//一旦有一个不相同就退出并且标志为0
break;
}
else
{
flag=1;//一个相同为1,如果连续都相同最后结果定是1
}
}
if(flag==1)
{
count++;
i+=j-1;
}
else
{
if(j==0)
{
i+=(j);
}
else
{
i+=(j-1);
}
}
}
//把最后两个字符转移到前面两个字节以防止切断搜索串.
buff[0]=buff[max];
buff[1]=buff[max+1];
}
fclose(fd);
printf("count:%d ",count);
}


  程序很好懂,用的也是教科书上面的标准字符串搜索算法,但是比前面的PERL程序长多了吧?那是因为人家PERL已经帮你完成了大部分工作。但是看到上面这段程序的运行结果你可能会高兴起来,它最快一次只用了2分10秒52,最慢也只用了2分20秒59就完成了1500万行文本的搜索任务。平均2分15秒多。为什么每次时间不一样呢?我不清楚具体原因,但学过操作系统的朋友会明白,只有在单道单任务的系统中,代码才能有执行上的可再现性。

  有经验的朋友可能会说,你的缓冲区只用了2048字节,加大它速度还会增加呢。是的,而且我相信还有高手能作出更快的程序来,但这不重要,重要的是我们要考察的是不同语言完成同一件工作的效率。而且你能够明白,在程序中,改进什么能够提高效率,这就足够了。因为C语言程序中,这些都是自由可控的。

  第三个选手:C++

  C++和前面的C是亲戚。我简单的把前面的C代码移植过来,然后把文件输入部分改成了流类对象。至于算法部分嘛。跟前面的C是一模一样的。最后在编译的时候,除了使用速度最佳编译选项外,当然还用了C++的编译参数,因此执行文件的长度比前面的C要长一些,这说明我加的流类代码比标准C库要复杂。是的,C++应该说是目前流行的计算机编程语言中复杂度排名靠前的。其复杂的类和继承关系,以及各种初始化的次序和构造函数执行顺序等都需要考虑。还有多态以及动态联编技术等。C++也是我非常喜欢的语言,提供了面向对象的代码重用特性和足够的安全型,但是在效率上的确比纯C略逊一筹。你知道吗,大部分的操作系统核心几乎都是用纯C写成的,尽管很复杂,但很少有使用面向对象技术的。为什么,不是面向对象技术不好,也不是操作系统核心不够复杂(那什么复杂?),主要的考虑就是效率问题。


#include <stdio.h>
#include <string.h>
#include <fstream.h>

void main()
{
int len=2048;
char filename[20];//文件名
char buff[10000];//文件缓冲区
char hit[5];
int i,j,flag=0;
int max;
int count=0;//最后的结果
strcpy(&filename[0] , "d:\access.log_");
strcpy(&hit[0] , "HIT");
buff[0]=0x0;
buff[1]=0x0;
//用输入流打开文件:
ifstream input(&filename[0]);
//读取文件内容
while(input)
{
input.getline(&buff[2] , len);
max = strlen(&buff[2]);
for(i=0;i<max;i++)
{
for(j=0;j<3;j++)
{
if(hit[j] != buff[i+j])
{
flag=0;//一旦有一个不相同就退出并且标志为0
break;
}
else
{
flag=1;//一个相同为1,如果连续都相同最后结果定是1
}
}
if(flag==1)
{
count++;
i+=j-1;
}
else
{
if(j==0)
{
i+=(j);
}
else
{
i+=(j-1);
}
}
}

}
printf("count:%d ",count);
}

  这段C++程序在测试平台上用了最快4分25秒95 到最慢5分40秒68的时间完成1500万行的文本检索,并在2G的文件中检索出10951968个"HIT"字符串。这结果是正确的。
  第四个选手:汇编

  本以为汇编程序能够达到前所未有的高速,把前面的选手远远抛在身后而笑傲江湖。这一想法支撑我完成了艰涩的代码。可事实上测试的结果缺让我大失所望,完全用机器指令书写的程序,去掉缓冲区才几百字节,算法和前面的C程序一模一样,扫描1500万行文本竟然最快也要2分14秒56!这甚至还比不过C语言的最快纪录。而平均下来,汇编程序的速度竟然和前面的C程序在伯仲之间。恐怕这样的结果也出乎大部分人的意外。因为我们从入行的那一天起,就被告知汇编是你所能够掌握的最快的语言!尽管代码坚涩难懂,但性能的代价是值得的。而从这里的测试看,你觉得向下面这样的代码,实现和C语言一样的速度和功能值得吗?

;堆栈段
STSG SEGMENT STACK 'S'
DW 64 DUP(?)
STSG ENDS

;数据段
DATA SEGMENT
rlength EQU 2048
fname DB 'access.log_',0
hit DB 'HIT$'
fd DW ? ;文件句柄
resault DB 'count : $' ;结果提示
count DD 0 ;存放结果
disflag DB 0 ;显示标志
buff DB 5000 dup(0) ;缓冲区
DATA ENDS

;代码段
CODE SEGMENT
MAIN PROC FAR
ASSUME CS:CODE,DS:DATA,SS:STSG,ES:NOTHING
MOV AX,DATA
MOV DS,AX
;我的代码开始:
mov ah,3dh ;打开文件
lea dx,fname
mov al,00h ;文件打开方式
int 21h ;开始操作
;这里就不作错误处理了,偷懒喽!
;CF=0表示正确,CF=1表示错误,AX是文件句柄或者是错误代码
mov fd,ax ;保存文件句柄

READ: mov ah,3fh ;读文件
mov bx,fd ;文件句柄
mov cx,rlength ;要读length字节

lea dx,buff ;给出读缓冲区指针
add dx,2 ;缓冲区指针向后错两个(目的是解决边界问题:有一个HIT正好横跨rlength界限)
int 21h ;开始读
;AX里面是实际读出的字节数
;读完了以后,扫描缓冲区
push ax ;保存AX字节数
cmp ax,0
jz ALLEND ;文件读完了就退出

sub dx,2 ;指针向前错2个,
mov si,dx
add dx,2 ;把指针回到原来的位置
add dx,ax ;计算结尾
LOD3: cmp si,dx ;到头了就重新读一次文件
jz OVR
lods buff
lea bx,HIT
cmp al,[bx]
jnz LOD3 ;读第一个字节不相等就重新读一个

cmp si,dx
jz OVR
lods buff
cmp al,[bx+1]
jnz LOD3 ;如果第一个字节相等,就读第2个字节,不行等就从第一个字节再重比较。

cmp si,dx ;如果第二个字节也相等的话,就比较第三个字节。
jz OVR
lods buff
cmp al,[bx+2]
jnz LOD3 ;第三个字节不相等再从头开始
;有一个HIT匹配
push bx
lea bx,count
add WORD ptr [bx],1 ;计数器增加一个
adc WORD ptr [bx+2],0 ;进位
pop bx
jmp LOD3

OVR: mov ah,[si-1]
mov BYTE ptr buff+1 , ah
mov ah,[si-2]
mov BYTE ptr buff , ah

pop ax ;恢复这次总共读出的字节数
cmp ax,rlength ;看看是不是最后一次(剩余的零头)
jz READ
;如果是最后一次读文件,

ALLEND: mov ah,3eh ;关闭文件
mov bx,fd ;文句柄
int 21h ;关闭文件

mov ah,9 ;显示结果字符串
lea dx,resault
int 21h

;转换2进制结果到10进制ACSII形式
mov bx, WORD ptr count
call TERN

mov ax,4c00h ;返回DOS
int 21h
;结束代码,最大的数字已经排到了最前面
MAIN ENDP

TERN PROC ;这个子程序是转换并显示2进制数字的
mov cx,10000
call DEC_DIV
mov cx,1000
call DEC_DIV
mov cx,100
call DEC_DIV
mov cx,10
call DEC_DIV
mov cx,1
call DEC_DIV
ret
TERN ENDP
DEC_DIV PROC
mov ax,bx
mov dx,0
div cx
mov bx,dx
mov dl,al
add dl,30H
mov ah,disflag ;read flag
cmp ah,0
jnz DISP ;已经显示过有效数字了
cmp dl,30H
jz NODISP
mov disflag,1 ;作用是第一个有效数字出现前不显示0
DISP: mov ah,2
int 21H
NODISP: ret
DEC_DIV ENDP
CODE ENDS
END MAIN

  上面这段代码我猜你也懒得仔细阅读。其实他不能"显示结果"。因为最后这段负责把最终结果转换成可显示ASCII码的程序实际上只能转换二进制十六位的数据,而最终的结果高达1000万挂零,显示会出错。由于这最终结果的显示已经和程序的运行没有大关系了,因此,我也就懒得去写一个32位的ASCII转换程序了。就这样吧。

  第五个选手:JAVA

  JAVA是一个不能不参加比赛的选手。有如此多的人热爱他,他们中的一半人是因为JAVA的面向对象特性以及良好的跨平台特性。而另一半人纯粹就是因为JAVA不姓"微(软)",这就是意识形态在程序员头脑中对某种语言的注释。单纯从语言元素上来说,我还是比较喜欢JAVA的。因为他的语法干净、简洁。环境也好。虽然用虚拟机系统(JVM)的做法来实现跨平台特性并非什么了不得的创意(像不像30年前的BASIC解释器?别跟我说什么中间代码?几乎所有的解释器都是把语言因素翻译成中间代码的,JVM不过是分成2步来实现罢了,但从运行机制上应该是差不多的。),但JVM仍然将JAVA的跨平台特性做到了前所未有的地步。而且JVM是一个很干净的系统,让人用起来赏心悦目。说到这里我忍不住想提一下J2EE企业应用框架了。不知道有多少人能够看懂SUN出的J2EE的"理论著作"?满纸充斥着各种生造的概念,洋溢着溢美之词。JAVA的企业应用框架实在是比较复杂的东西,虽然赶不上后来的.NET框架,但足以让大多数初学者望而却步。一句话,东西太多了。事实上JAVA的企业级应用并没有想象的成功,iPlanet就随着电子商务概念的全面垮台而渐渐淡出。现在换了个名叫“SUNONE”――SUN公司员工原话。

  我们回到JAVA的语言元素上来说,实际上JAVA可以被理解为被纯化的C++。JAVA去除了C++为了兼容C而增加的一些"非面向对象特质",用其他的一些变通办法实现C++直接实现的功能,比如:多继承。在实现机制上,JAVA的程序会先编译成.CLASS文件,然后这种跨平台的中间代码就可以"一次编译,到处运行"了。当然必须运行在有JVM虚拟机的环境中,连图形什么的都可以照搬。换句话说,你用JAVA程序在PC屏幕上画一个圆,在JAVA-PDA上它还是圆的。

  我在本次测试中,写了下面的代码,用JAVA做了同样的测试,测试中实际上用到了:JAVA的文件流类,运行了循环、条件判断、数组操作等基本的语言因素。环境是J2SE1.3.1-06。JAVA程序做1500万行的文本扫描用了8分21秒18。应该说是几种语言中最慢的,基本上和纯解释的PERL是在同一水准。J2EE的JVM环境还是经过优化的所谓HOTSPOT。

import java.io.*;
public class langtest
{
public static void main(String[] args)
{
String filename = "d:\access.log_";
try
{
count(filename);
}
catch (IOException e)
{
System.err.println(e.getMessage());
};
}

public static void count(String filename) throws IOException
{
long count=0;
long len;
String strline = "";
char hit[] = {'H','I','T'};//要搜索的字符串
char buff[] = new char[2100];

Reader in = new FileReader(filename);//用FileReader类构造产生一个Reader类对象
LineNumberReader line = null;//生成一个空指针
try
{
line = new LineNumberReader(in);//建立LineNumberReader类对象
while((strline = line.readLine()) != null)
{
//到这里已经读出一行了,用下面的代码分析这行有几个HIT
int i=0,j=0,max=0,flag=0;
buff = strline.toCharArray();//转换成字符数组
max = strline.length();

for(i=0;i<max;i++)
{
for(j=0;j<3;j++)
{
if(hit[j] != buff[i+j])
{
flag=0;//一旦有一个不相同就退出并且标志为0
break;
}
else
{
flag=1;//一个相同为1,如果连续都相同最后结果定是1
}
}
if(flag==1)
{
count++;
i+=j-1;
}
else

{
if(j==0)
{
i+=(j);
}
else
{
i+=(j-1);
}
}
}
}
System.out.println("Count : "+count);
}
catch (IOException e)
{
System.err.println(e.getMessage());
}
finally
{
try
{
if(in != null) in.close();
}
catch (IOException e)
{
}
}
}
}


后记
事实上,本文测试中有一个大大的不公平之处,相信仔细的读者已经发现了:其中C和ASM都是使用缓冲区直读的办法,不管三七二十一就进行判断(最后用指针检查缓冲区边界)。而C++等其他的语言虽然用了非常方便的流按行读出,但是多做了很多事情:每一个字符都要判断其是不是回车换行符,而按行读近来,每次缓冲的也要少很多。因此其他几种语言就大大的吃亏了。不过这并不影响结论性的东西,因为测试本身就说明越方便就效率越低。事情总是要有人做,不是吗?
 
回复: 语料库的开发见解

笔者侧重于阐述 Perl 与 C 或 Java 不同的独特之处。您一定会为 Perl 这些在其他语言中看不到的特性而心花怒放:操作符的容错能力、一项任务多种实现、标点、正则表达式以及变量机制等。所有这些都赋予您的手指更灵活的魔力。在某些方面 Perl 的确能给 C 和 Java 程序员很多有用帮助,可惜目前它还远达不到众所周知的程度。因此,抓紧机会提高您的 Perl 水平吧!​
Perl 有时甚至令有经验的程序员也觉得头疼,因为他们发现一不小心就会写出模棱两可的语句。但这种在结构、特性体系方面的模糊性从另一方面显示了 Perl 语言强大的能力。毕竟 Perl 语言最开始的设计初衷就是希望能用多种方式来达到同一目的。
这里我们将要探讨 Perl 5.6 中那些最容易混淆的特性,并将它们与 C/C++/Java 中相应的特性做比较。主要围绕 "Natural Language Principles in Perl" 中的原则展开(Larry Wall 著,参看本文末尾的 资料 部分),因为它们是 Perl 最能与 C、C++ 和 Java 语言区分开的地方。此外,关于 Perl 语法的结构可以在 "perldoc perlsyn" 参考文档中找到,另一本值得推荐的 Perl 指南读物则是 Programming Perl
Perl 解释器
初学者马上就会发现 Perl 中似乎没有任何编译器。事实上,Perl 脚本大多是由 Perl 解释器直接运行的,例如 UNIX 系统下的 "Perl"、DOS/Windows 下的 "perl.exe" 就是 Perl 解释器。而在 MacOS 中则不需要这些解释器。您可以试试看如何运行 Perl 脚本。首先在您的操作系统上启动相应的 Perl 解释器,或是在 MacOS 系统中直接运行。在大多数系统中,文件尾标志(UNIX 系统中为 Control-D)用来表示用户输入结束。因此在 UNIX 系统中,下面这个脚本将能得到 "5+6" 的计算结果:
从最简单的 Perl 程序入手
> perl(Perl is waiting for user input here, because no script name is given)print 5+6You press Control-D here11


可以看到 Perl 解释器运行了这个只有一行的脚本程序,并输入该表达式的计算结果 11。
Perl 解释器有许多选项。例如,-e 选项表示将命令行的输入作为脚本文件来执行,因此上面的脚本例子也可以这样实现:在命令行输入 [FONT=新宋体]perl -e'print 5+6'[/FONT](注意,要用单引号将命令括起来)。而 -i 选项则类似于通过一个过滤器,允许在文件中不同的位置进行编辑。-n 和 -p 选项能让程序员打开或关闭输出。-w 选项与 C/C++ 中的 "-Wall" 编译选项类似,都能够对程序中潜在问题给出警告信息,但与 -Wall 不同的是,-w 功能在程序运行时也是被激活的。
速度和 Benchmark
人们常常拿 Perl 与 C/C++ 比较,并抱怨 Perl 运行速度不够快。某些时候这的确是事实,但是并非永远如此。我建议您在认为 C 或 C++ 更快之前,使用 Benchmark 模块试试看 (perldoc Benchmark)。此外,Perl 能很方便地与 C/C++ 代码或库连接,且某些 Perl 内置函数并不比 C 代码慢,如排序或打印等。这里再次提醒您在坚信 C/C++ 比较快之前,先使用一下 Benchmark 模块。
要记住,过早的优化往往是错误的根源。如果您在 Perl 中写了一个原型,并用其他语言来重写是没有问题的。原型意味着能够方便地开发。
与 Java 相比而言,Perl 也能够很好地工作。Perl 不象 Java 那样擅长于线程,但它的 Tk GUI 界面工具箱却比 Java 的 Swing GUI 库要好。并且 Java 代码总是能够连接到 Perl 程序中,反之亦然。因此,有时您可以通过某种程度上的结合,使得程序在两方面都做得很好。
异常、编译和文档
Perl 通过 CPAN 中的模块或是其内置函数 eval() 来抛出异常。就好像在 C++ 或 Java 中通过 try/catch 代码块来处理异常一样,eval 函数能处理某个代码段或某个字符串操作的异常。
事实上,Perl 程序在运行之前还是需要编译的,只是和 C/C++/Java 的编译方式不相同。在设计和效果上,它和 Java 的字节解释过程很相似。关于编译的更详尽内容,可以参阅 "perldoc perlrun" 和 "perldoc perlcc" 文档。
可用 POD 格式来将文档嵌入 Perl 程序。这种文档嵌入方式比 Javadoc 格式(仅适合于 API 文档)要通用,但比不上 C/C++/Java 的注释。
即使和 C、C++ 或 Java 比较起来,Perl 程序也不算是一种结构性强的语言。例如,BEGIN 语句块会被首先执行,但它可以在程序中多次说明。定义、变量和函数体可以在程序的任意位置出现,Perl 所提供地强大功能可以最好地满足这种随意性。
由于这种松散结构、嵌入的注释、以及为追求方便而导致的令人混淆的语句,使得书写 Perl 程序更像是在写一封英文信件。
Perl 的容错能力
Perl 比 C/C++/Java 更能容忍一些模糊地写法。例如可以用逗号来分隔语句或函数的参数:
语句之间或函数参数之间的分隔符
print 'Hello', ' ', 'there.', "\n"; # print "Hello there\n"foreach (1..10){ my $i; $i = $_ * 2, print "$i\n"; # print evens from 2 to 20}


Perl 能尽最大可能地消除这些语句可能引起的歧义。当然,有些时候仍有无法解决的歧义(在这一点上,Perl 就象英语一样)。
Perl 中另一个容易引起歧义的地方在于:变量经常会被隐含使用。例如,"print" 语句缺省时会打印 $_ 变量的值。在其他一些含混的语句操作中,$_ 变量也是它们的缺省值,这就造成了一种混乱。例如:
隐含使用变量
$_ = "hello";s/hello/hi/; # $_ is "hi" nowprint; # prints "hi"


这里你可以看到,使用缺省变量能让编程方便简洁。也就是说,Perl 和英语类似,通过某种模糊性来简化表达式。
一项任务多种实现
(There's more than one way to do it ,TMTOWTDI)

所有的语言在解决问题时都有自己的方法。在 C 里面,for() 循环是在一定范围内重复的最好方法;在 Java 里,静态函数的调用是直接通过类而不是某个实例。
但对于同一件事情,Perl 至少有两种解决方法。TMTOWTDI 原则就是 Perl 的座右铭,各种处理上的差异在 Perl 编程中是深受鼓励的。
下面来看一个打印数组元素的例子。所有的表达方法都达到同一目的。
打印数组元素
print foreach @array;foreach (@array) {print};map {print} @array;print @array;


要理解以上这些代码的唯一途径就是掌握所有 Perl 的语法。不要担心哪种方法才是正确的,因为实现同一目标总是有多种正确的方法。考虑这些不同的表达方式,您可以更深刻体会 Perl 的这个座右铭。
另外,虽然一个任务的实现有多种方法,但这并不意味着所有方法都是正确的。通常情况下,更有可能写出的是一些错误代码。为了保证代码的正确性,最好尽量使用那些 Perl 内置的函数,而较少使用自己所编写的函数,并且注意证明并记录这些不那么显然的方法。
正则表达式
如果没有初始化,正则表达式很有可能造成一片混乱。大多数人都相信正则表达式是由 Kalahari bushmen 发明的,它渗透到了大学的计算机科学编程的所有方面。
Perl 的正则表达式是从 shell 脚本程序以及 awk/grep 工具中继承而来的。但它的能力却远远超出了原来的模型。
基础的正则表达式是非常容易书写的,但难以读懂。例如 "con\w+" 和 "contra"、"contrary" 匹配,但与 "pro" 或 "con" 都不匹配。然而在 Perl 5.6.0 中,正则表达式被固化了。Unicode 字符集、模式内任意代码操作、flag toggles、条件表达式以及其他特征都被添加到正则表达式库中。
对于初学者的一个最好的建议就是:首先学习最基本的正则表达式(参阅 资料 部分,或 "perldoc perlre" 参考手册),稍后才进一步学习那些复杂的高级特性。由于正则表达式必须全写在一起,中间没有办法添加注释,这就让它们成为所有 Perl 代码中最难读懂的一部分。因此建议大家书写已成型的代码。
在 C/C++/Java 中正则表达式属于外部的函数包,但 Perl 是目前最佳的正则表达式搜索和代换工具。在极少数情况下,它可能会比纯 C 程序慢一些,但对于那些纯粹面向正则表达式的问题,Perl 依然是您首选的工具。
标量、数组和哈希散列
和 C、C++、Java 中变量不同的是,Perl 的变量的类型是由其名字决定的,并且会自动初始化成相应类型。这一点让 Perl 初学者觉得很不习惯,但它非常地直观而易于理解。
笔者推荐使用 "use strict"。通过它可以保证变量在使用之前声明,从而避免打字错误等引起的程序错误。


如果没有做到这一点,则有可能遇到下面这样的问题:
一个常见的打字错误
$i = 5;print $j; # print $i


此例子中,程序员本来要打印变量 i 的值,结果却敲成了 j。Perl 并不会觉得这段代码有什么问题,它会继续执行打印语句,显示 $j 的值即什么都没有。有些时候,Perl 的自动生成对象的确很有用,但以我的经验来说,最好还是用 "use strict" 来关掉这一自动功能,从而避免上述问题。
Perl 变量可以是标量 (scalars)、数组 (arrays)或哈希散列 (hashes,又叫做关联数组)。(事实上,Perl 中有多种数据类型,但是程序员并不会直接面对它们。)此外也可以是引用,通常它们也是一种标量类型。其中标量名称以 "$" 开头,数组名以 "@" 开头,而散列则以 "%" 开头。
标量是 Perl 中最简单的数据类型。每个标量都有唯一的值,或者是字符串或者是引用。在必要的时候,字符串和数字可以互相转化。这常让初学者觉得欣喜异常。看一下这个例子:
标量
$i = "hi there";print 1+$i; # prints 1


其中标量 $i 的值是字符串 "hi there",它对应的数值为 0。因此 1 + "hi there" 的值为 1,程序运行结果为 1。
不过这并不意味着 Perl 解释器在对某个标量分别考虑其字符串类型和数字类型。事实上,在内存中只是一个含有某个值的标量。如果在数值运算的语句中(如加法),这个标量值就转化成数值形式;如果在字符串操作语句中(例如打印),则以字符串形式执行。但无论以什么形式运算,该标量变量实质上只有一个值。
未定义的标量的值为 "undef"。如果在 C/C++/Java 程序中,您可以将其他值与 null 比较,但在 Perl 中却不能拿任何东西来与 "undef" 做比较。可用这样使用 defined() 函数:
Use of the 'defined()' function
$i = "hi there";print $i if defined $i; # prints "hi there"undef $i; # set $i to be undefprint $i if defined $i; # prints nothing


数组实质上就是一组标量。如果需要,数组大小可以自动改变,有点象 Java 中的 Vector 类。C 和 C++ 中没有与 Perl 数组类型相当的东西,但它们也有一些提供类似功能的库(如 STL)。数组的一个有趣特性在于,数组的标量数值等于它的元素个数:
数组中的元素个数
@a = ("hi there", "nowhere");print scalar @a; # prints 2push @a, "hello"; # add "hello" at the endprint scalar @a; # prints 3


散列与数组类似,但里面的标量并不是按照位置排序的,而是通过另一个标量(必须是唯一值)来进行索引。例如一个用 social security number 作索引的名字列表就是一个散列。将某个键值插入到散列后,该散列会自动扩展。散列与 Java 中的 HashMap 和 Hashtable 类很相似。
引用类型其实也是标量,它们类似于 C 语言中的指针,能够指向任何东西。这就允许 Perl 生成一个散列的数组、数组的散列、散列的散列、或是数组的数组(多维数组)。有多种方法来获得引用所指向的内容,或者直接使用引用的名字、或者使用 "->" 操作符。引用是一个涉及范围非常广的问题,可以参考 "perldoc perlref" 参考文档来获得更多相关信息。
C 和 C++ 只有一些固定类型的标量。当程序员要使用数组或哈希散列时,不得不去使用钩子 (hoop) 或是 STL 等外部库。


Java 中有相当于 Perl 里数组或散列功能的类库,但它们在 Java 语言中并不是那么直接。比如说要对散列上所有元素做操作所需要的时间大约是 Perl 的三倍。
对散列中所有元素进行操作的 Java 代码
import java.util.Enumeration;import java.util.Hashtable;Hashtable hi = new Hashtable();// fill in hi's values// we can use an Iterator, still a lot of typingfor (Enumeration enum = hi.elements(); enum.hasMoreElements();){ Object o = enum.nextElement(); // do something with o}


对散列中所有元素进行操作的 Perl 代码
# note that this even includes the definition and initialization of# the hash, and still is more compact than the Java code!%hash = { a => "hi", b => "hello" };foreach (values %hash){ # do something with $_}


Perl 的缺憾
Perl 缺少 C、C++ 和 Java 中的许多特性,但它毕竟是一门完全不同的语言。这几种语言中甚至有许多特性是互相矛盾的。例如 Java 只支持单一继承,而 C++ 则可以有多个父类。这种有冲突的情况下当然不可能继承所有语言的特性,Perl 有它自己处理问题的方法。
由于 Perl 程序能够被连接到 C 的库中(事实上,这也就是 Perl 应用广泛的原因之一),这就使得几乎没有任何 C 或 C++ 能做而 Perl 不能的事情。
与 C 和 C++ 相比而言,Perl 有时欠缺的是运行速度。这的确是一个问题,但是通过良好的编程算法以及 Perl 内置函数的使用,就能够克服这一缺点。
Perl 还不能直接使用 C 和 C++ 的库。必须通过不同的模块和绑定,才能够将这些库中的常量以及函数功能转化成适应 Perl 的样子。这就会导致开发和程序运行的速度降低。但由于 CPAN 库中发布了大量这些方面的模块,因此这个问题并不是那么难以解决,
在训练编程技巧方面,Perl 并不象 C 和 C++ 那样深入人心。它是一门年轻的语言,虽然很受欢迎,但并未被人们普遍接受。然而,大多数的 UNIX 系统上都安装了 Perl,且其他的操作系统也都支持 Perl。
Perl 支持单一继承或多继承、封装以及多态,但这仅仅是通过外部模块或程序员的协同来实现的。也就是说,Perl 语言本身并没有严格的面向对象编程规则,需要程序员自己来实现面向对象。这一点有好也有坏,这就要取决于程序员或项目本身了。
Perl 的线程以及统一字符编码(Unicode)支持远远落后于 Java,也稍微次于 C/C++。Java 从最开始设计就支持线程和 Unicode,而 C/C++ 则比 Perl 拥有更多的时间来调整这方面的正确支持。在 Perl 中,对线程和 Unicode 的支持仍处于起步阶段,但 5.6.0 之后的稳定版本发布之后这一点将得到改观。
Perl 的优势
对于 C/C++/Java 程序员而言,Perl 在某些方面的优势是无价的。例如正则表达式在 Perl 中的实现是轻而易举的,但在 C、C++ 或 Java 中实现起来却很麻烦。隐含的函数声明、不严格的语法、以及象日用文档似的程序结构使得 Perl 更具吸引力。
Perl 并不适合于所有人。它需要读者去适应,却接受它的所有缺点和优点。我们并不是觉得 Perl 酷才采用它,而是因为它的确是一种非常好的工具。如果在解决某个问题时使用其他语言更合适,那么就应该放弃 Perl。一个好程序员的手头总是有好几种有用的工具。
Perl 有一些小的不足,但那些不知疲惫的程序员会忽略掉这些缺点。如果的确需要线程和 Unicode 支持,或是严格的面向对象编程,那么你只好根据这些需要来选择其他更合适的语言了。
Perl 是一门通用的灵活的语言,可以象胶水一样将其他许多不同的模型粘合起来。它能够实现任何过程或函数的算法。通常情况下,Perl 会大大减少开发的时间,因为它对某些常见的任务(例如对散列表中的所有元素做操作)只需要少量的代码。最重要的是,Perl 编程总是相当于一个有趣的学习过程。
 
回复: 语料库的开发见解

hacker 的转帖是毫无道理可言,这样来比较不同编程语言的速度完全是误导。所谓一知半解能带来多大的害处!

读硬盘是非常慢的操作,他所说的程序算法相同,结果所花费的时间差别大,完全是在于磁盘缓冲的处理不同。每个语言对此都有不同的默认值。

事实上,采取顺序检索,查询the出现的次数和查询abacadabra几乎都要花相同的时间。但是要是你做索引,基本是就是读几下盘的时间(几个毫秒。)

奉劝对计算机一知半解的人,多读一下算法。至于编程语言,那是个人爱好,无关大局。萝卜青菜各有所爱。
 
回复: 语料库的开发见解

hacker 的转帖是毫无道理可言,这样来比较不同编程语言的速度完全是误导。所谓一知半解能带来多大的害处!

读硬盘是非常慢的操作,他所说的程序算法相同,结果所花费的时间差别大,完全是在于磁盘缓冲的处理不同。每个语言对此都有不同的默认值。

事实上,采取顺序检索,查询the出现的次数和查询abacadabra几乎都要花相同的时间。但是要是你做索引,基本是就是读几下盘的时间(几个毫秒。)

奉劝对计算机一知半解的人,多读一下算法。至于编程语言,那是个人爱好,无关大局。萝卜青菜各有所爱。
我汗颜。。。 LUCENE不就是做索引的吗?楼主写的你没看到?
 
回复: 语料库的开发见解

hacker 的转帖是毫无道理可言,这样来比较不同编程语言的速度完全是误导。所谓一知半解能带来多大的害处!

读硬盘是非常慢的操作,他所说的程序算法相同,结果所花费的时间差别大,完全是在于磁盘缓冲的处理不同。每个语言对此都有不同的默认值。

事实上,采取顺序检索,查询the出现的次数和查询abacadabra几乎都要花相同的时间。但是要是你做索引,基本是就是读几下盘的时间(几个毫秒。)

奉劝对计算机一知半解的人,多读一下算法。至于编程语言,那是个人爱好,无关大局。萝卜青菜各有所爱。
这位仁兄对编程的语言有所欠缺,每门语言的特长和功能都不一样。个人兴趣爱好不谈,C能操作计算机硬件, java行吗?至少目前不行,java对网络的处理速度很快而c做得到吗?不能吧?我不用举例了吧。。。。有大脑的而且有实际经验的人都知道,还有就是有个问题,每次新加内容都要重新索引文件。。。在使用起来有一定的缺陷!
 
回复: 语料库的开发见解

"有大脑的而且有实际经验的人都知道"。。。"脑子进水了"。。。"我汗颜!!!"

我等不懂技术,但是汉字还是看得懂的。各位仁兄讨论问题何必出言不逊,非要用一些攻击性、侮辱性语言呢?
 
回复: 语料库的开发见解

奉劝对计算机一知半解的人,多读一下算法。至于编程语言,那是个人爱好,无关大局。萝卜青菜各有所爱。
这个是mandel 所发表的一句话,说自己看法可以,难道说,这样批判性的文字就不是一些攻击性、侮辱性语言?
 
回复: 语料库的开发见解

算了,再也不来这里了,一开始就个错。。。“大师们”的“权威性”继续!
 
状态
主题已关闭, 停止回复.
Back
顶部