台湾菜

第三年零三个月

twcai • programming

工作三年出头了,回头的时候突然觉得挺空荡的。

折腾了三年Perl,少许C,最近一年陆陆续续的玩了下python。除了Perl有系统的学习过,大部分的获得都是tricks,工作经验和一些软技能。这些不系统,没总结的过东西,太琐碎,拿不出手。工作时感到自豪的是自己总是有点强迫症的想把活做到最好,只是主观感受,做的也不一定那么完美,何况去年还被打击了一次。脱完水,真的没剩下多少干货。

所以今晚突然想着给下半年的业余时间做点计划,免得不知不觉就把时间给荒废过去了。顺便把以前的东西整理总结下,感觉会比较好。

  1. 系统的整理一下Perl
    • 把perlxs,perlapi,perlguts这几块内容看一看,感觉这是我学Perl的最后一座桥头堡
    • Learning Perl, Intermediate Perl再重新梳理一遍
    • Mastering Perl, Advanced Perl Programming有时间再完整的读一下 XD
  2. 好好补充下*nix服务端开发方面基础
    • 之前看了『Linux系统编程』的一小部分「文件读写」,感觉是本好书,下半年应该抽时间好好读一遍。
    • 其他方面的知识,还是看项目需要,选读一些资料和paper。
  3. 恢复算法和数据结构的练习
    • 这个还没有具体的计划,目前的想法是选一些感兴趣的算法和数据结构,然后去CF上找相关题目练习

先这样。

牢骚

twcai • some parts

该放手就得放手,长痛不如短痛。

啪哒

twcai • programming and some parts

今天看了一天的GNU Build System文档,一直到三个小时前,脑子里还是跟一团浆糊似的。

但是三个小时前,正好我在文档和configure.ac之间切来切去的时候,脑子里的东西在某一刻变得顺利成章。这个脚本的结构从一盘散沙突然变成一个搭好了脚手架的建筑工地。

人脑真的是很奇怪啊。

别矜持了混蛋

twcai • my life

我发现自己对博客文章质量总是高于实际的期许是我博客难产最重要的原因,既然这个是我私人的地方,那么其实我完全不必要这么有负担。爷的地盘爷做主!

但是这不代表我没意识到这么长一段时间以来文章质量一直都不咋地,恥ずかしい良い~~

今天这篇是因为半夜三更突然想总结一下最近的状态,这种事情在这种时间总是很容易发生的。

这几个月来我想的东西很多,想做的东西很多,但大多数也仅仅停留在想想和看看这一阶段。原因嘛,一个是因为没啥动力和压力,二是因为跟博客难产类似的困境:做为一个轻度完美主义者(请允许我臭屁的这么以为),总是在一开始就把目标定的太大太全,以至于下手之前就因为复杂度而畏缩。

所以,写这篇东西的目的就是为了鼓励自己有想法了就要冲动一点上去做。

恩,总结完了。

Clean up your own SHIT!

twcai • some parts

假如我是老板,上完厕所连自己的屎都不知道冲干净的程序员一定会被我炒鱿鱼!

特么的一个小阀门都用不好的程序员,你能指望他写出什么样的代码?

懒惰,可是七宗罪之一。

沙扬娜拉,MMIE

twcai • my life

年终总结要拖一下,这差不多是每年的惯例。

去年可能是我这两三年来最不用功的一年,却似乎是我收获最多的一年。为此我很感谢我身边的人,既然我都不怎么用功,那我收获的的肯定都是从慷慨的他们那里得到的。

先看一下去年写的计划(MMIE)好了。

1. 给自己买了5本非技术的书,要认认真真读完。

2. 计划以后每年去一到两个地方旅游,看时间吧,今年暂定春节去厦门看看。

3. 既然当了QA了,就要做好。从Perl帝手里接过来的事情一定不能干砸了,好好学学automation test。

4. 用动态编程语言(Perl, Python神马的)写一个有意思的东东。

5. 找一个C/C++开发的产品,读懂代码。

6. 要抓机会锻炼*nix environment programming,各读一本OO和设计的书。

7. 不要再装逼,至少要学着由内而外的装。

8. 经常让自己看看上面这些都做了没。

================ 我是2011和2012的分割线 =================

去年还有一些其他很值得纪录的事情。

去年,就在我生日前一天,那个MMIE里的可爱姑娘终于答应我问了一年多的请求!虽然我们有90%的日子都是分隔两地,但她带给我的快乐一直充满着我的生活。今年我再许一个愿望, 希望她能早点来杭州(^_^)

下半年美国客户来公司交流时给我颁发了一个刻着Rock Star的iPod touch。接着Boss又推荐我参加公司优秀员工评选,也终于在公司年会上得到了优秀员工奖。这些让我觉得过去一年自己并没有虚度。

另外,去年在豆瓣上一共纪录了124部电影为看过,一部分是以前看过的旧电影,但今年看的电影还是占了所有电影总数的三分之一。豆瓣fm推出以后,一共听了15601首歌,接触的音乐风格比我之前这20多年加起来都多。于是我打算恬不知耻的追认2011为我的文艺年哈哈。

今年的目标还没什么头绪。一是因为最近略忙没什么时间可以好好想想,二是有些事情真的在我能力以外。比如,今年爸妈都很辛苦,一直为家庭操劳,希望今年爸妈不再需要负担这么多事情,奶奶的病情也能够转好。

先写这么多吧。最近状态不是很对,所以才拖了这么久。

作为一个命令行偏执狂...

twcai • perl

好久没有更新博客了,这次更新的原因是上次贴的Perl脚本查Google字典因为Google关闭字典服务的关系已经不能再用了。

前段时间接触了一个新东西叫XPath,名字听起来挺酷的,于是拿来又折腾了一个脚本出来。这次查的是有道辞典,没有Google的好用,但因为Google字典已经不再提供服务,目前能找到的Google字典服务都是通过Unofficial API实现的,也不知道能用多久,就先这么凑合着用吧。

上图
youdao_dict

这次脚本代码还是主要分两部分,一部分是通过LWP::UserAgent抓网页内容,一部分是用HTML::TreeBuilder::XPath分析网页内容提取需要的数据。

直接对比下面一个脚本和上一个脚本中网页分析部分的代码,你就能直观的感受到上面提到这两种方法的差别。XPath的详细介绍和语法,请猛击W3school

#!/usr/bin/perl

use strict;
use warnings;
use utf8;

use Encode qw(encode);
use IO::Scalar;
use LWP::UserAgent;
use HTML::TreeBuilder::XPath;
use URI::Escape;
use Data::Dumper;

my $url     = 'http://dict.youdao.com/search?q=KEYW0RD&ue=utf8';
my %ele_map = (

    # Result area
    results_contents => q(//div[@id='results-contents']),

    # Word
    word     => q(//h2[@class='wordbook-js']),
    keyword  => q(//h2[@class='wordbook-js']/span[@class='keyword']),
    phonetic => q(//h2[@class='wordbook-js']/span[@class='phonetic']),

    # Trans
    basic_trans => q(//div[@id='phrsListTab']/div[@class='trans-container']/ul),
    addt_trans  => q(//div[@id='phrsListTab']/div[@class='trans-container']/p[@class='additional']),

    # C2E
    web_trans_title => q(//div[@id='tWebTrans']/h3/span/a[@rel='#tWebTrans']),
    web_trans       => q(//div[@id='tWebTrans']/div/div/span[@style='cursor: pointer;']),
    web_phase       => q(//div[@id='tWebTrans']/div[@id='webPhrase']/p),

    # Phase
    phase_list     => [ 'wordGroup', 'synonyms', 'relWordTab' ],
    phase_title    => q(//div[@id='eTransform']/h3/span/a[@rel='#REPLACEME']/span),
    phase_contents => q(//div[@id='transformToggle']/div[@id='REPLACEME']),
    phase_suffix   => [ '/p',        '/ul/*',    '/p' ],

    # Examples
    example_list     => [ 'bilingual', 'originalSound', 'authority' ],
    example_title    => q(//div[@id='examples']/h3/span/a[@rel='#REPLACEME']/span),
    example_contents => q(//div[@id='examples']/div/div[@id='REPLACEME']/ul),
);

my $keyword = $ARGV[0];
$keyword = uri_escape ($keyword);
$url =~ s/KEYW0RD/$keyword/;
die "You must pass a Chinese/English word as parameter" if (!$keyword);

my $ua = LWP::UserAgent->new;
$ua->timeout (30);
$ua->env_proxy;
my $page = $ua->get ($url);
die "Cannot connect to $url" unless ($page->is_success);

my $buffer;
my $screen = IO::Scalar->new (\$buffer);
my $tree = HTML::TreeBuilder::XPath->new_from_content ($page->decoded_content);

get_translation ($tree, $screen);
print encode('UTF-8', $buffer);

END {
    $tree->delete ();
}

sub get_translation {
    my ($tree, $screen) = @_;
    my $indent = 2;
    if (!$tree->exists ($ele_map{results_contents})) {
        $screen->print ("Something really goes wrong\n");
        return 1;
    }
    # No translation found
    elsif (!$tree->exists ($ele_map{word})) {
        $screen->print ($tree->findvalue ($ele_map{results_contents}));
        return 0;
    }
    # Get the spell and phonetic of the word
    else {
        $screen->print ($tree->findvalue ($ele_map{word}), "\n");
        $screen->print ("\n");
    }

    # Get the basic translation of the word
    if ($tree->exists ($ele_map{basic_trans})) {
        $screen->print ("基本释意\n");
        my @translation_list = ();
        if ($tree->exists ($ele_map{basic_trans} . '/li')) {
            @translation_list = $tree->findvalues ($ele_map{basic_trans} . '/li');
        }
        elsif ($tree->exists ($ele_map{basic_trans} . '/p')) {
            @translation_list = $tree->findvalues ($ele_map{basic_trans} . '/p');
        }
        push @translation_list, $tree->findvalue ($ele_map{addt_trans}) if ($tree->exists ($ele_map{addt_trans}));
        map { $screen->print (' ' x $indent, trim ($_), "\n") } (@translation_list);
        $screen->print ("\n");
    }

    # Get web translation of chinese word
    if ($tree->exists ($ele_map{web_trans_title})) {
        $screen->print ($tree->findvalue ($ele_map{web_trans_title}), "\n");
        map { $screen->print (' ' x $indent, trim ($_), "\n") } ($tree->findvalues ($ele_map{web_trans}));
        $screen->print ("\n");
    }
    if ($tree->exists ($ele_map{web_phase})) {
        $screen->print ("短语\n");
        map { $screen->print (' ' x $indent, trim ($_), "\n") } ($tree->findvalues ($ele_map{web_phase}));
        $screen->print ("\n");
    }

    # Get the phase of the word
    for my $index (0 .. $#{ $ele_map{phase_list} }) {
        my $phase         = $ele_map{phase_list}->[$index];
        my $title_path    = $ele_map{phase_title};
        my $contents_path = $ele_map{phase_contents};
        my $suffix        = $ele_map{phase_suffix}->[$index];
        $title_path    =~ s/REPLACEME/$phase/;
        $contents_path =~ s/REPLACEME/$phase/;
        if ($tree->exists ($title_path)) {
            $screen->print ($tree->findvalue ($title_path), "\n");
            if ($tree->exists ($contents_path . $suffix)) {
                my @phase_list = $tree->findvalues ($contents_path . $suffix);
                map { $screen->print (' ' x $indent, trim ($_), "\n") } (@phase_list);
            } else {
                # TODO
            }
        }
    }
    $screen->print ("\n");

    # Get the examples of the word
    for my $index (0 .. $#{ $ele_map{example_list} }) {
        my $example       = $ele_map{example_list}->[$index];
        my $title_path    = $ele_map{example_title};
        my $contents_path = $ele_map{example_contents};
        $title_path    =~ s/REPLACEME/$example/;
        $contents_path =~ s/REPLACEME/$example/;
        if ($tree->exists ($title_path)) {
            $screen->print ($tree->findvalue ($title_path), "\n");
            if ($tree->exists ($contents_path)) {
                my @example_list = $tree->findvalues ($contents_path . '/li/p[not(@class)]');
                while (my ($is_a_trans, $value) = each @example_list) {
                    $screen->print (' ' x $indent, ($is_a_trans & 1) && ($index < 2) ? "~ $value" : $value, "\n");
                }
            }
            $screen->print ("\n");
        }
    }
}

sub trim {
    my $string = shift;
    $string =~ s/^\s*(.*?)\s*$/$1/;
    return $string;
}

比赛的意义

twcai • acm/icpc and my life

今天是2011 World Finals ACM/ICPC的比赛日,晚上10点开始看比赛的直播,看着每个学校的队伍的排名交错的攀升,看到HDU-Knuth的两题AC,两题WA,突然脑海闪回过自己的大二暑假,背上出了点汗,手臂上起了鸡皮疙瘩。

杭电进World Finals已经不是第一次,但是今年退役后看比赛跟去年在役看比赛是两种心情。在役时似乎更多的是不甘心和一些小嫉妒,因为我显然WF无望。之前自然也知道想进World Finals很难很难,但还是难免偶尔会狂妄的暗暗把这当作自己的目标。现在的我,对自己宽容了,内心平静了,但有时候也怀疑自己是不是失去了激情和进取心。

记得以前北大的CICI姐姐写过一篇日志,大致是说她在World Finals看到一个非洲的大学的队伍,只AC两题,却在赛后激动的跟领队拥抱欢呼。虽然她一个人也可以做出两题,但是她却没有那些人的快乐。

想到这里,我释然了,ACM/ICPC竞赛可以给我的,我都已经得到了。

SVN导入外部项目到本项目版本库

twcai • programming

为什么需要导入外部项目到本项目版本库

在项目开发的过程中我们很可能会遇到这种情况:我们手头开发的项目可能要用到其他项目的内容。

最容易想到的办法是从其他项目的版本库里取出那部分内容,拷贝并添加到本项目的版本库中。但如果我们要令其他项目那过来的那部分内容保持最新,就是一个非常繁琐的工作了。

有没有高效的办法

如果本项目的版本库自动取得被引用的外部项目的更新,例如每次"svn update"或者"svn checkout"都可以去外部项目的版本库取得最新的内容,那实在是非常方便。我们可以通过Subversion的外部定义功能来实现这个目的。外部定义是一组本地文件路径和URL的映射,使svn可以取得外部版本库的文件到本地路径。

主要要用到以下几个svn指令
svn propset  svn:externals PROJECT_NAME
svn propedit svn:externals PROJECT_NAME
svn propget  svn:externals PROJECT_NAME

前两个指令都可以用来设置对应PROJECT_NAME的svn:externals属性,一般推荐用第二个。svn:externals的属性列表指定项目中模块的相对路径,版本信息和对应的URL。

例如:

third-party/foo         svn://example.com/repos/foo
third-party/bar -r 1234 svn://example.com/repos/bar

Subversion 1.5及以上的版本也支持以下的格式:

        http://example.com/foo third-party/foo
-r 1234 http://example.com/bar third-party/bar

第三个指令则可以查询项目的外部定义的属性列表。

Perl脚本查Google字典

twcai • perl

这个Perl脚本其实并不能算我的原创,是师傅Perl帝拿出来分享的。本来拿的是iciba的翻译,我另外改了一个上Google字典拿翻译的版本。

要修改的原因:
1. Google字典的英中字典,有双语解释;
2. Google字典的例句和相关短语这些资源要丰富的多,可以帮助理解单词使用的语境,写英文材料时非常有用;
3. 我习惯用Google字典,我一个G粉。

先上截图
gdict

为什么要用一个脚本查单词?对于命令行控来说,离开当前工作终端,开个网页查单词是很痛苦的事情,他们甚至根本不想让手离开主键盘区!有这样的一个脚本,然后扔进/user/bin/,就不用大费周章的移动手臂了。

这个脚本用LWP::UserAgent抓取网页,HTML::TokeParser解析网页,获取单词的翻译。

脚本实现了一个抓取和解析google字典的类,整体的逻辑在en2chs函数中:
1. 生成网址
2. 用LWP取得结果网页
3. 解析网页

_parse_html找到翻译信息所在的代码块——一个id叫“pr-root”的标签,然后主要的体力活就全都扔给_get_close_mean啦。

要通过html标签来定位自己想要的内容,还真是个蛮累人的事情。但是Chrome的Deveploper Tools让事情简单了很多,实在是让人心神舒畅啊。

来看代码吧

#!/usr/bin/perl 
use strict;
use warnings;

my $robot = Translater->new;
print $robot->en2chs( $ARGV[0] );

#
# Class Translator
#
package Translater;

use strict;
use warnings;
use LWP::UserAgent;
use HTML::TokeParser;
use URI::Escape;
use Carp qw(confess);
use IO::Scalar;

sub new {
    my $class = shift;
    my $self = bless {}, $class;
    $self->_init;
    return $self;
}

sub _init {
    my $self = shift;
    $self->{browser} = LWP::UserAgent->new;
    $self->{browser}->timeout(60);
    $self->{browser}->env_proxy;
    $self->{_url} = "http://www.google.com.hk/dictionary?q=KEYW0RD&hl=zh-CN&langpair=en|zh-CN&spell=1&oi=spell";
}

sub en2chs {
    my ( $self, $word ) = @_;
    my $buffer;

    $word = uri_escape($word);
    $self->{_url} =~ s/KEYW0RD/$word/;
    my $response = $self->{browser}->get( $self->{_url} );
    confess "Cannot connect to Google Dictionary " . $self->{browser}->status_line
        unless ( $response->is_success );
    my $content = $response->content;

    $self->{_io} = IO::Scalar->new( \$buffer );
    $self->_parse_html( \$content );
    return $buffer;
}

sub _parse_html {
    my ( $self, $ctxt_ref ) = @_;

    my $stream = HTML::TokeParser->new($ctxt_ref);
    while ( my $token = $stream->get_token ) {
        if (    $token->[0] eq 'S'
            and $token->[1] eq 'ul'
            and exists $token->[2]{id}
            and $token->[2]{id} eq 'pr-root' )
        {
            $self->_get_close_mean( $stream, $token );
            $self->{_io}->print("\n\n");
        }
    }
}

sub _get_close_mean {
    my ( $self, $stream, $token ) = @_;
    my $mean_counter = 1;
    while ($token) {
        if (    $token->[0] eq 'S'
            and exists $token->[2]{class}
            and $token->[2]{class} eq 'dct-tt' )
        {    # find dictionary entry
            $self->{_io}->print( $stream->get_trimmed_text('/span'), " " );
        }
        elsif ( $token->[0] eq 'S'
            and $token->[1] eq 'div'
            and exists $token->[2]{class}
            and $token->[2]{class} eq 'dct-em' )
        {
            $self->{_io}->print("\n\n  $mean_counter.  ");
            $mean_counter++;
        }
        elsif ( $token->[0] eq 'S'
            and exists $token->[2]{title}
            and $token->[2]{title} eq 'Part-of-Speech' )
        {    # find Part-of-Speech
            $self->{_io}->print( "\n\n", $stream->get_trimmed_text('/span') );
            $mean_counter = 1;
        }
        elsif ( $token->[0] eq 'S'
            and $token->[1] eq 'div'
            and exists $token->[2]{class}
            and $token->[2]{class} eq 'dct-ee' )
        {    # find example
            $self->{_io}->print("\n      ~ ");
        }
        elsif ( $token->[0] eq 'S'
            and $token->[1] eq 'span'
            and exists $token->[2]{class}
            and $token->[2]{class} eq "dct-tp" )
        {    # find spell
            my $spell = $stream->get_trimmed_text('/span');
            if ( $spell =~ /([\/\[].+[\/\]])/ ) {
                $self->{_io}->print( " ", $1 );
            }
        }
        elsif ( $token->[0] eq 'S'
            and $token->[1] eq 'div'
            and exists $token->[2]{class}
            and $token->[2]{class} eq 'dct-er' )
        {    # escape "See also" tag
            while ("true") {
                $token = $stream->get_token;
                if (    $token->[0] eq 'E'
                    and $token->[1] eq 'div' )
                {
                    last;
                }
            }
        }
        $token = $stream->get_token;
        if (    $token->[0] eq 'S'
            and $token->[1] eq 'h3' )
        {
            last;
        }
    }

}