之前写过一个算数独的程序,不过有点bug。前些日子有人突然又问起了这个程序于是改了一下,现在应该没啥大问题了,对于任何一个有唯一解的题都能很快给出答案。
程序
用vc 2008编译php扩展
今天又想试试写php扩展,先从编译sample开始。
首先用ext_skel_win32.php生成骨架代码。这个需要cygwin。如果cygwin不在c:cygwin,那么需要修改这个文件,指定cygwin的位置。比如:$cygwin_path = ‘d:cygwinbin’;
之后用vc2008打开生成的dsp工程文件,升级工程文件以后,把工程选项里的C/C++ -> Code Generation里的Runtime Library改成Multi-threaded DLL (/MD);C/C++ -> Advanced里的Compile As设成Compile as C Code (/TC)。
然后就编译,居然就没通过有几个语法错误…… 搜索了一下,发现还要改两个地方:
Zend/Zend.h:
把#include 搬到
#ifdef ZEND_WIN32
# include “zend_config.w32.h”
# define ZEND_PATHS_SEPARATOR ‘;’
#elif defined(NETWARE)
# include
# define ZEND_PATHS_SEPARATOR ‘;’
#elif defined(__riscos__)
# include
# define ZEND_PATHS_SEPARATOR ‘;’
#else
# include
# define ZEND_PATHS_SEPARATOR ‘:’
#endif
后面去。
还要修改zend_config.w32.h,把#define vsnprintf _vsnprintf这行注释掉。
之后总算可以编译通过了。加载进来运行一下,终于看到了那句Congratulations。
以后再想想写个什么扩展好,然后进一步研究下。
用flash上传文件
HTML提供了一个文件上传的控件,一般在web上上传文件全靠它。但是这玩意样式不好控制。在修改了文本框和按钮的样式后,这个文件域的样子就显得很突兀。虽然有一些结合CSS和JS的技巧可以模拟不同的样式,但是很麻烦,而且在浏览器兼容,以及稳定性方面都不太好。
好在Flash8开始提供了文件上传下载的功能,还由于有了更方便地与javascript交互的能力,完全可以不用Flash做界面(这样在页面修改的时候比较麻烦),而是把那个flash隐藏掉,用javascript和html来控制显示。此外,用flash上传还有一个很大的好处是可以显示上传进度,让用户在等待的时候感觉舒服一点。不过这种方法也有缺点,比如不能读取从服务器返回的结果,也不能通过post捎带其他的参数。
as和js代码都很简单,可以下载回去试试。
php的interface == 鸡肋?
话说当年,php5诞生的时候,向java,c++学了好多面向对象的语法元素。其中就有interface。在java圈子里,主流的观念是要面向接口编程而不是面向类编程,将接口与实现分离。记得在Spring in Action的例子里,要创建一个骑士对象,首先写一个Knight接口,里面定义了骑士可以做什么。具体怎么做则是在类KnightImpl里实现的。看上去似乎有一点麻烦。不过java大牛们教育我们,这是为了更好的灵活性。这样使用骑士的时候不用关心这是个什么样的骑士,而且可以在不修改代码,只修改工厂的配置文件,就可以用另一个实现来替换现有的实现。更寻常的说法是,比如Map接口,通常我们使用一个映射表的时候只要能够满足键值对应的操作就行。我们可以根据具体需要选用哈希表实现或是查找树实现,甚至自己定义一个存放在数据库中的Map。简单的说,接口定义了一组操作,实现了某个接口的对象也就能提供这个接口规定的一组操作。接口就像是一个协议或者说一个标准。这么说来,interface应该是一个好东西,似乎是面向接口编程所必不可少的,interface不就是接口的英文词么。那这篇文章的题目又是怎么回事呢?
还是拿例子说话。
interface Computer{
function add($a, $b);
function minus($a, $b);
….
}
class Mainframe implements Computer{
function add($a, $b){
//…
}
function minus($a, $b){
//…
}
}
class Calculator implements Computer{
function add($a, $b){
//…
}
function minus($a, $b){
//…
}
}
不管是大型机还是小计算器,都可以算加减法。想要算加法的时候只要拿一个实现了Computer接口的东西就行。比如
function doAdd(Computer $c, $a, $b){
return $c->add($a, $b);
}
看起来不错。不过能算加减法的不仅仅是那些用电的东西。我国古代劳动人民的智慧结晶——算盘也能算加法。那么如果要拿算盘算加法,就要有一个算盘类。那这个算盘类该不该实现Computer接口呢?这个算盘似乎也算不上是一个计算机啊。或许该另外建立一个Calculatable的接口,让计算机和算盘都去实现这个接口,这样似乎就不错了。有时候我们只需要用到Calculatable的一部分功能。比如Calculatable定义了基本的加减乘除,但我们买菜算账的时候通常只需要加法。这时候如果用Calculatable的话就需要接受一些无用的东西。再用一个php的SPL里的例子。SPL里有一个ArrayAccess接口,可以让对象像数组一样用下标去访问。这是一个很有意思的语法糖。不过有时候只需要按下标读功能就可以了,但是为了要满足规定,还是要把ArrayAccess接口的所有方法都实现一遍,于是就要写好几个throw new Exception(‘xxx is not writable’);之类的东西。这实在有点罗嗦。
为什么不换一个写法呢?扔掉那些interface,直接写
class Mainframe{
function add($a, $b){
//…
}
function minus($a, $b){
//…
}
}
class Calculator{
function add($a, $b){
//…
}
function minus($a, $b){
//…
}
}
class Abacus{
function add($a, $b){
//…
}
function minus($a, $b){
//…
}
}
在需要计算的时候,也不用限定一定要是Computer,把对象直接扔过去。
function doAdd($c, $a, $b){
return $c->add($a, $b);
}
如果在试图在一个不能算加法的东西比如易拉罐上执行add,解释器自然会报错。也就是说,只有满足接口的对象才能够正常运行。你看,不用interface一样可以实现面向接口编程,而且更加灵活。
不用interface搞面向接口编程这听起来还是有点玄乎。毕竟这个interface就是接口的英文单词啊。好吧,interface确实是英文单词,但并不代表interface这个语法元素就等价于接口。就好像把狗的尾巴也叫做腿,这狗也不是就有了五条腿。interface只是叫了这个名字而已。当然,interface在一定程度上也为面向接口编程提供了方便,比如提供了编译期的检查。而且对于强类型语言来说,这样的语法元素是很重要的,比如java。而对于弱类型语言,比如php,却不是必须的。而对于一些更动态的语言,比如python,ruby来说,更是很难相容的。
从ruby的面向对象说起
ruby是一个完全面向对象的语言。在许多其他语言里不是对象的东西在ruby里也都是对象,比如整数。更神奇的是,类也是对象。为什么说神奇呢?看一下下面的代码:
class A
end
puts A.class
结果是Class。可以看到,类A 也是一个对象,它是类Class的实例。接着看
puts Class.class
原来Class是Class类自己的实例。
puts Class.superclass
puts Module.superclass
puts Object.superclass
puts Object.class
类Class的爸爸是Module,Module的爸爸是Object。而Object是所有类的祖宗。但是类Object还是类Class的实例。很神奇吧。
我们知道,对象是类的实例,是由类创建的。而在ruby中,类本身也是一个对象,这就形成了一种鸡生蛋蛋生鸡的循环。似乎这是周而复始,无穷无尽的,就像下面那幅埃舍尔的名画
这两个相互描绘的手就像是ruby中的类和对象。两只手相互描绘在现实世界里是不可能的。那么在ruby中,又是如何打破这个僵局的呢?其实很简单,这两只相互描绘的手只是一幅画而已。而让两只手相互描绘则是画家。在画面内部无法解决的矛盾跳到画面外面就很简单了。在ruby里也是如此。在语言层面,类和对象似乎也像是这两只相互描绘的手,但不要忘了,还有一个ruby虚拟机的存在。一切的对象都是由它创建,而语言里的ClassName.new并没有直接创建对象。在一切的开始,虚拟机创建了Class,Module,Object,之后,这三个东西就可以滋生出整个对象世界。虚拟机就像是至高无上的神,创建了整个世界。但是在一个ruby代码的世界里,能看到的却只有对象,而不能直接看到虚拟机的存在。我们可以写完全面向对象的代码,而不用去操心这些对象究竟是怎么来的。
回到现实世界。不知道大家有没有想过我们的现实世界是怎么来的。根据被普遍接受的物质不灭理论,物质不会消失,也不会凭空出现。那么这些物质最初又是怎么来的呢?话说我们的宇宙是由一个很小的核心爆炸,不断膨胀而产生的,又话说宇宙在膨胀到极限后又会重新收缩,又变回一个很小的核心,周而复始。那么这一切又是如何开始的呢?在这个宇宙之外又是什么样呢?如果这些问题存在答案的话,要么接受一个创造这一切的“神”的存在,要么就只能说,物质是无须创造而自然存在的。
include的返回值
php有一个include,顾名思义,就是用来包含文件的。相应的还有require,以及两个带once的。不知道大家有没有注意过include的返回值。通常情况下,包含成功返回1,包含失败返回false。但是,如果被包含文件中用return返回,那么这个值就是return的值。比如:
<?php
//1.php
$ret = include ‘2.php’;
echo “$retn”; //输出wakaka
?>
<?php
//2.php
return ‘wakaka’;
?>
从1.php的输出就可以看到,这里include的返回值就是2.php中return的那个’wakaka’。
这个特性有什么用呢? 我是利用它来方便地使用php文件来做配置。比如,一个配置文件可以这么写:
<?php
//config.php
return array(
‘db’=>array(
‘host’ => ‘localhost’,
‘user’ => ‘root’,
‘password’ => ‘wakaka’,
‘name’ => ‘test’,
‘encoding’ => ‘utf8’
)
);
载入的时候,只需要$conf = include ‘config.php’;就可以了。
PHP实现的事件
内置了事件机制的语言不多,php也没有提供这样的功能。事件(Event)说简单了就是一个Observer模式,实现起来很容易。但是有所不同的是,事件的监听者谁都可以加,但是只能由直接包含它的对象触发。这就有一点点难度了。php有一个debug_backtrace函数,可以得到当前的调用栈,由此可以找到判断调用事件触发函数的对象是不是直接包含它的对象的办法。
<?php
/**
* 事件
*
* @author xiezhenye
* @since 2007-7-20
*/
class Event {
private $callbacks = array();
private $holder;
function __construct() {
$bt = debug_backtrace();
if (count($bt) < 2) {
$this->holder = null;
return;
}
$this->holder = &$bt[1][‘object’];
}
function attach() {
$args = func_get_args();
switch (count($args)) {
case 1:
if (is_callable($args[0])) {
$this->callbacks[]= $args[0];
return;
}
break;
case 2:
if (is_object($args[0]) && is_string($args[1])) {
$this->callbacks[]= array(&$args[0], $args[1]);
}
return;
default:
return;
}
}
function notify() {
$bt = debug_backtrace();
if ($this->holder &&
((count($bt) >= 2 && $bt[count($bt) – 1][‘object’] !== $this->holder)
|| (count($bt) < 2))) {
throw(new Exception(‘Notify can only be called in holder’));
}
foreach ($this->callbacks as $callback) {
$args = func_get_args();
call_user_func_array($callback, $args);
}
}
}
找到全部中国大陆IP段
可怜的学校的BBS,为了不让国外流量把计费器耗完,要找到全部中国大陆的IP段。首先是要有IP地址的数据,这在APNIC的网站上可以找到。
国家IP段数据下载地址:http://ftp.apnic.net/apnic/dbase/data/country-ipv4.lst
有了数据就好办了,写几行程序,把其中大陆的ip段抽出来即可。
file = File.open(‘country-ipv4.lst’)
file.each do|line|
next if line =~ /^#/
items = line.split(‘ ‘)
puts “#{items[0]} – #{items[2]}” if items[6] == ‘cn’
end
顺便试试ruby。
中文分词方法简介(毕业论文节选)
中文与西方文字不同,西方文字如英文的单词间有空格作为分隔,计算机很容易把一个个词分开。而中文句子里的之间没有分隔,要把中文句子拆分成词就需要使用中文分词技术。由于全文索引采用的是倒排索引技术,所以分词的效果直接决定了搜索的效果。
目前的中文分词技术主要有:n元切分、最长匹配、最大压缩、统计语言模型等方法。n元切分,即机械切分。就是把中文句子每n个字分成一个“词”。比如,“我是大学生”用一元切分的结果就是“我”、“是”、“大”、“学”、“生”。二元分词如果采用串分割,结果就是“我是”、“大学”、“生”,采用交叉分割结果就是“我是”、“是大”、“大学”、“学生”。通常,都采用交叉切分,以免在搜索“学生”时无法搜索到结果。同时,交叉分割可以保证查询和索引切分的一致性。但交叉分割的索引大小是串分割的n倍。机械切分并不是真正意义上的中文分词,因为它并没有把句子根据词法和语义分成有意义的词。这种方法实现简单,切分效率高,但会产生很多无用词。Lucene自带的StandardAnalyzer对中文的切分就是一元切分。Lucene Sandbox中的CJKAnalyzer采用的是交叉二元分词。
最长匹配法是使用词典来切分的。比如,“我是大学生”根据词典通常会被拆分为“我”、“是”、“大学生”。最长匹配法又分正向最长匹配和反向最长匹配,即从句子的正方向或方向匹配单词。通常反向最长匹配的效果要好于正向最长匹配。也可以把两者结合起来,即双向最长匹配。最长匹配法实现也较简单,分词速度较快,但准确率比较低。采用词典的分词方法的效果很大程度上取决于词典的质量。同时,在遇到词典中没有的词往往就束手无策了。而且,基于词典的分词对于新词,还有人名、地名等往往不能很好地识别。Lucene Sandbox中的ChineseAnalyzer就是采用的反向最长匹配。
统计语言模型方法简单的说,就是通过从文本库中统计出字与字之间结合和分开的概率来分词的。比如“我是大学生”,“是”和“大”结合的概率要小于“大”和“学”结合的概率。这种方法不依赖于词库和语法定义,可以适应新词以及人名地名等。但实现复杂,分词速度慢。在上下文信息较少的情况下效果不够好。
廉颇老矣
今天有百度Astar程序设计竞赛。也去凑了一把热闹。结果发现,
年纪大了,不中用了啊~~~~ T_T
看了一下4个题,觉得第2个最好做,就拿它下手。但是因为好久没碰C++了,一些东西的用法都要先google才知道怎么用。好久没做题了,这个不难的题也弄了n久。最后时刻好不容易搞对了,提交上去却是编译错误,说没有itoa。我这边是用的dev c++,怎么说也是g++啊,难道win上的g++和linux上的g++还不一样?不过我的那个程序效率也不是很高,就算这边对了,会不会TLE还不知道。
BTW: 似乎百度的竞赛系统还可以,至少不像以前acm竞赛的时候经常提交都提交不上去。
反正就这样了。下面把这次的题目放上,有兴趣的可以看看。
http://syre.blogbus.com/files/11801933250.txt
又玩了一场。貌似第二场的比第一场的题简单点。这次提了一个题。然后发现,原来百度居然没有OJ的…………。前面关于百度系统的评价收回。