Tuesday, March 31, 2009

麦克阿瑟:"我还要回去"

第二次世界大战打到了1944年的6~7月时,艰难爬上诺曼底海滩的100余万盟军已在欧洲大陆上牢牢站住了脚,太平洋战场的盟军也开始向日军发起全面反攻。美国参谋长联席会议认为,盟军已经掌握了战争的主动权,可以大大加速太平洋战争的进程了。

摆在太平洋美军面前的迫切问题只有一个:确定太平洋战场的下一个战略打击目标。

但是,在麦克阿瑟的西南太平洋战区部队完全控制了新几内亚,尼米兹的太平洋战区部队控制了马里亚纳群岛之后,下一个进攻目标指向哪里?恰恰在这个问题上,美军内部存在着严重分歧。

众所周知,美国在太平洋战场上,唱主角的是太平洋舰队兼太平洋战区总司令、海军上将尼米兹和西南太平洋战区盟军总司令、陆军上将麦克阿瑟。

3月11日,参谋长联席会议最终确定的战略计划,再次肯定了从太平洋两个方向对日本发动钳形攻势的战略设想,并明确规定了各路部队的进攻目标和时间表。对于是进攻吕宋还是进攻台湾的问题,留待以后再定。把“再定”的时间期限划在1945年2月15日以前。

在 进攻吕宋还是台湾的问题上,也有两种意见。一方以麦克阿瑟为首,主张夺取吕宋岛,另一方以海军上将金为首,主张夺取台湾岛。金上将一直坚决主张“迂回菲律 宾而攻占台湾”。他建议绕过麦克阿瑟坚持的主攻目标——菲律宾,而由尼米兹指挥部队进攻台湾,并要麦克阿瑟将主力部队移交给尼米兹。

到了1944年6月,参谋长联席会议的其他两位成员马歇尔和阿诺德上将都同意了海军上将金直接进攻台湾的计划。麦克阿瑟对此当然愤愤不平。由于台湾位于中 太平洋战区,接下来在太平洋战争中唱主角的只能是尼米兹,而不是他麦克阿瑟。更重要的是,当年他在日军的强大攻势下几乎是只身逃出菲律宾,那里至今还囚禁 着成千上万的美军战俘,那里有美国在道义上有义务解放的1700万菲律宾人。绕过菲律宾,麦克阿瑟将无法实现自己离开菲律宾时发出的“我还要回来”的誓 言。所以,他态度鲜明地表示,“我认为这一战役是不会成功的”,日军在台湾的防御固若金汤,“没有空中支援,没有充足的军舰和基地来打击重兵防守的堡垒, 直接进攻就是自取灭亡。”他认为,占领菲律宾的吕宋岛,是建立进攻台湾的飞机基地的大前提。

12月22日,本间雅晴陆军中将亲率日军第14集团军主力5万余人,分乘80艘运输舰,在马尼拉西北110英里处的林加延湾登陆。作战仅仅进行两天两夜, 菲律宾守军就全线崩溃了。为避免全军覆没,麦克阿瑟主动撤退到有坚固设防的马尼拉湾西北面的巴丹半岛,准备在那里固守待援。然而,华盛顿当局坚定地奉行“ 欧洲优先”的原则,对这个日本人迟早要夺取的岛国没有派出一兵一卒。

后来在檀香山, 麦克阿瑟和尼米兹在罗斯福总统面前开始了一场精彩的辩论.

罗斯福首先说明此次会面的目的是“决定太平洋战争的下一阶段行动计划”。总统举起竹节教鞭,指向地图上的棉兰老岛——这是大家都同意收复的岛屿。可他问的却是:“道格拉斯,我们从这里再奔向什么地方?”麦克阿瑟回答道:“莱特湾,总统先生,然后是吕宋岛。”

但代表海军方面意见的尼米兹显然不同意这个计划,他端出了金海军上将攻打台湾的计划。为了说明这个计划,有备而来的尼米兹拿出了随身携带的各种地图、计划、手稿、各种统计表及其他形象化的东西。

按照金海军上将的计划,麦克阿瑟应当在棉兰老岛上建立强大的作战基地,以削弱日军在菲律宾的空中力量,但菲律宾的其余部分应该绕过,为在1945年的夏季 前做好进攻台湾的准备,只能把西南太平洋的美军“象征性”地留给麦克阿瑟两个师和几个飞行中队,其余部队都交给尼米兹指挥。

而麦克则开始了他各个方面的辩论:---------

“你不能把1700万虔诚的菲律宾基督教徒留给日本人。”麦克阿瑟首先从政治的角度说服罗斯福。他知道,总统对政治问题远比军事问题更感兴趣,尤其 是罗斯福刚刚接受民主党的提名,准备进行美国历史上破天荒的第四次总统竞选。他强调说:“总统先生,那时如果美国公众舆论谴责你,那也是有道理的。”

麦克阿瑟接着说,把菲律宾丢下不管,在远东人民的心目中也会引起强烈反响。他们会相信日本人的宣传,认为美国人不会为拯救他们的东方朋友而自己去流血。他还 谈到了被拘押在吕宋岛上的3700名美国战俘,并且说,如果在北面攻打台湾,在南面攻打棉兰老岛,就会使菲律宾中断外界的全部供应。到那时候,日本占领军 自己都没有吃的,大批菲律宾人和美国战俘就会活活饿死。

至于军事上的理由,麦克阿瑟也颇为理直气壮。他指出,攻占菲律宾后,在日本和荷属东印度之间设立封锁线,将切断日军继续作战所需的石油和金属等资源 供给。这将会卡得日本天皇喘不过气来,迫使他因此而投降。对于下一步首先进攻的目标,麦克阿瑟表示,不赞成为了配合攻台作战而攻占棉兰老岛,而是出于占领 菲律宾群岛的需要,必须首先攻占莱特岛、民都洛岛,将其作为上岸的踏脚石,或者在两个岛上同时修建机场,一旦机场修好,他的部队就可以在林加延湾登陆,并 在5周内进入菲律宾首都马尼拉。

“但是,道格拉斯,”总统想起不久前关于日军在马尼拉加强地面部队和航空兵力量的报告,而且那里聚集着50万日军部队,就打断了麦克阿瑟滔滔不绝的话头,“攻占吕宋岛需要付出惨重的代价,我们恐怕承受不了。我们似乎应当绕过它。”

麦克阿瑟在回答总统提问之前,脑海里浮想出尼米兹的海军陆战队前不久攻打日军弹丸之地塔拉瓦时付出重大牺牲的情景,便话中带刺地说 :“总统先生,我的损失不会大,决不会比过去大。正面进攻的时代过去了。现代化的步兵武器是致命的,正面进攻不合时宜了。只有平庸的指挥官才会那么干,优 秀的指挥官打仗是不会招致重大伤亡的。”他还补充说,“盟军可以从仇恨日本占领军的菲律宾人民那里得到一切可能的帮助。在有些地区,强大的菲律宾游击队在 驱逐日本占领者的战斗中已经取得了重大的进展。

午餐后,麦克阿瑟准备做最后的努力,他问能否单独同总统谈10分钟。总统顾问塞缪尔·罗森曼和埃尔默·戴维斯对麦克阿瑟摇了摇头,说总统还有其他安排,但罗斯福总统在短暂的沉默之后,还是答应了麦克阿瑟的这一要求。

“ 总统先生,”在别人都退出去之后,麦克阿瑟直截了当地把话题拉到政治层面上,他知道眼下罗斯福最关心的也就是这个,“美国人民对昔日巴丹发生的事已经原谅 了你。你希望能够连任下一届美国总统,但是如果你批准一项让菲律宾处于侵略者铁蹄之下、直到签署和平条约时才能让他们获得解放的计划,会令1700万忠于 美国的基督徒因此而感到屈辱,美国人民也将永远不会原谅你的。你这样做也许有军事上的战略战术原因,但这样做,将毁了你的政治前途。现在,总统先生,司令 部里还有很多事情等着我,我要告辞了。”麦克阿瑟握了握总统的手,扭头要走,但罗斯福还是把他叫了回来,让他陪自己去做最后一次视察。

在整个会议期间,罗斯福并没有说过一句明显有倾向性的话。但麦克阿瑟心里明白,总统内心的天平已倒向了他一边。当飞机从希卡姆机场呼啸着腾空而起的时候,机舱内的麦克阿瑟收回俯视珍珠港港区的目光,转身向他的副官高兴地说:“我们的意见被采纳了。”

但罗斯福并不打算直接说服参谋长联席会议的成员们。这使将星闪耀的高级军官们陷入举棋不定的困惑之中。马歇尔受到总统的影响,转到了麦克阿瑟的立场 上;阿诺德空军上将和参谋长联席会议主席莱希知道了总统的意向后则为菲律宾群岛上的日军飞机和坚固的陆上防御而忧心忡忡;一贯十分固执的金海军上将不为所 动,仍然坚持进攻台湾。

在接下来的一段时间里,上百万的太平洋战场上的盟军官兵似乎失去了下一个作战的大目标,位于布里斯班的西南太平洋战 区司令部和位于珍珠港的太平洋战区司令部的参谋们,各自忙乎着自己顶头上司让他们干的事情,前者重新审定了不包括棉兰老岛在内的菲律宾作战计划,后者则按 照金海军上将的指示,忙着研究如何进攻中国台湾和厦门.

这里发生了戏剧性的一幕......

1944年9月29日至10月1日,美军海军总司令兼作战部长欧内斯特·金海军上将与美军太平洋战区高级指挥官们在旧金山召开了一次高级会议。

参 加这次旧金山会议的人员有:太平洋舰队司令官尼米兹上将、第5舰队司令官斯普鲁恩斯上将、陆军航空部队司令官哈蒙中将、第10集团军司令官巴克纳中将,此 外还有一位尼米兹手下负责作战的谢尔曼少将。会议的主要议题就是统一思想,确定美海军对太平洋战场下一个战略进攻目标的看法。众所周知,金上将一直坚决主 张“迂回菲律宾而攻占台湾”。由于7月26日罗斯福总统亲临太平洋战区召开檀香山军事会议,在会上会下,麦克阿瑟从政治和军事两个方面成功地说动了罗斯福 总统。参谋长联席会议受总统的影响,转到了麦克阿瑟的立场上,固执的金上将成了孤家寡人。

尼米兹当着作战部长金上将的面发表自己的看法。他说:“我认为,迅速攻占日本本土乃是美国的最高目的。为了达到这个最高目的,美军完全可以不经过台湾和中 国沿岸,而从吕宋岛、冲绳和马里亚纳、小笠原两个方向实施进攻。这样做,对美军更为有利。因为美军如果攻占了吕宋岛,美军就可以在菲律宾建立一些海空基 地,封锁中国海海域的海上交通,与此同时还可以削弱台湾的威胁,进而达到攻占冲绳的目的。如果美军攻占了冲绳、小笠原,美军就可以更加容易地对日本本土发 动进攻。从而,可以更快地结束这场战争。如果仅仅是为了获取海空基地而向台湾发动进攻的话,那么美军付出的代价可就太大了。”

[尾声]

1944年10月3日,美军参谋长联席会议对麦克阿瑟和尼米兹下达了命令。命令麦克阿瑟在1944年12月20日攻占吕宋岛,并为尔后尼米兹的军队占领琉球群岛提供支援。

10月5日,麦克阿瑟终于等到了从1943年春天以来一直苦苦期盼的那句话:“I shall return.”


The essence of all good strategy is simplicity. 一切优秀战略的精髓是简单。

战略千变万化中,有一条是永恒不变的,那就是 "目的".

Douglas MacArthur 独身反对美军高层到最后力挽狂澜的历史已成为经典进入了战争史诗的殿堂,作为西太平洋战区盟军总司令,他向我们展示一名战略家所独有的气质和人格.他对"战略"的理解,完全清晰而简明的浮现在每个读者的面前. 就单凭这点,MacArthur是我至今最欣赏的美军元帅.



Wednesday, March 25, 2009

代理配置记录

这篇文章的技术含量不会太高. 不过是简单记录点东西.

最近是用公司的网络搭建了一个代理,这样访问国际网会比较快. 用到的技术是2个, Squid服务器 和 Pac 代理管理. 这2个的相关问题 google 可以得到一大堆, 我就不介绍了.

然后在 Linux,MacOSX,Windows上进行浏览器配置. 这个都好说,我直接导入pac,然后重启浏览器OK .
接着的问题是在Linux 和 Mac 的命令行. 我以前笨的以为命令行是不能用代理的 O_0 ...
要解决问题很简单配置 ~/.bashrc (有的是bash_profile) 简单加n条

export http_proxy = http://username:password@adress:port
export ftp_proxy = http://username:password@adress:port
export https_proxy = http://username:password@adress:port

随后 source .bashrc(或者 source .bash_profile). 问题就可以解决了.

结果svn又遇到了问题 我开始想svn不是支持http协议么,结果又重新看了下文档. 编辑 /etc/subversion 或 ~/.subversion 中的

[global]
http-proxy-exceptions = *.
http-proxy-host = 211.112.86.213
http-proxy-port = 3305
http-proxy-username = freefis
http-proxy-password = xxxxxx

* 这里注意 要加在 [global] 下面.





Tuesday, March 24, 2009

erlang和python交互

最近开发 Erlang ,对其字符串处理能力无言至极,于是决定把它和python联合起来,打造一个强力的分布式系统,等将来需要系统级开发时,我再把 C++/C组合进来.

首先参考了 Erlang 官方文档和 http://blog.developers.api.sina.com.cn/?tag=erlang 以及 http://kazmier.net/computer/port-howto/ .

研读了将近24个小时, 才终于完全把问题解决. 开始 python 一直是broken pipe.后来才知道是管道中断导致的问题.

接着我排除了python的问题, 在erlang上面发现了Timeout, 感谢litaocheng的提醒,翻看了文档,把gen_server:call 的超时控制为infinity, 这样完全排除了超时的错误.

后来发现执行combin()之后,系统一直在等待. 我重新参考了下前面的2个成功案例,数据流的结尾应该
加上"\n"(windows为"\t\n"),并且erlang需要转换二进制,于是就是

String = list_to_binary("combine|"++_String++"\n"),

python端就成功收到了Stream.
这里combine只是我自己定的一个通信协议 合成请求|String1,String2 => string1+string2.








%%%-------------------------------------------------------------------  

%%% File    : gen_server_template.full  

%%% Designer  : free.Won <freefis@Gmail.com>  

%%% Description :  

%%%     Communication between Python and Erlang via Stdio.  

%%% Archieved :  Mar 24 2009 by  free.Wang <freefis@Gmail.com>  

%%%-------------------------------------------------------------------  

-module(comm).  

-behaviour(gen_server).  

%% API  

-export([start/0,combine/1]).  

%% gen_server callbacks  

-export([init/1, handle_call/3, handle_cast/2, handle_info/2,  

     terminate/2, code_change/3]).  

-record(state, {port}).

  

start() ->  

    gen_server:start_link({global, ?MODULE}, ?MODULE, [], []).

init([]) ->  

    process_flag(trap_exit, true),

    Port = open_port({spawn, "python -u /home/freefis/Desktop/comm.py"},[stream,{line, 1024}]),

    {ok, #state{port = Port}}.

handle_call({combine,String}, _From, #state{port = Port} = State) ->  

    port_command(Port,String),

    receive

        {Port,{data,{_Flag,Data}}} -> 

            io:format("receiving:~p~n",[Data]),

            sleep(2000),

            {reply, Data, Port}

    end.

handle_cast(_Msg, State) ->  

    {noreply, State}.  

handle_info(Info, State) ->

    {noreply,State}.

%    {stop, {port_terminated, Reason}, Port}.

terminate(_Reason, Port) ->  

    ok.

code_change(_OldVsn, State, _Extra) ->  

    {ok, State}.  

  

%%--------------------------------------------------------------------  

%%% Internal functions  

%%--------------------------------------------------------------------

sleep(T) ->

    receive

        after T ->

            true

    end.

combine(_String) ->

    String = list_to_binary("combine|"++_String++"\n"),

    gen_server:call({global,?MODULE},{combine,String},infinity).








#!/usr/bin/env python

# -*- coding:utf-8 -*-

#-------------------------------------------

# Designer  : Free.Wang

# E-mail    : freefis@Gmail.com

# Licence   : License on GPL Licence

# Archieved : Dec 27 2008

#-------------------------------------------







import sys



def handle(_string):

    if _string.startswith("combine|"):

        string = "".join( _string[8:].split(","))

    return string







"""waiting for input """

while 1:

    # Recv. Binary Stream as Standard IN

    _stream = sys.stdin.readline()

    if not _stream: break

    # Scheme, Turn into  Formal String

    inString  = _stream.strip("\r\n")

    # handle String

    outString = handle(inString)

    # send to port as Standart OUT

    sys.stdout.write("%s\n" % (outString,))

    sys.exit(0)


Saturday, March 14, 2009

python发送post保存cookie封装

近期刚写完某SNS的一个刷人气机器人(仅仅做技术测试),然后封装了这个功能。

socket = Httpsocket()
创建一个请求实例,并初始化 cookie容器

socket.connect(url,param)
请求处理post的url和post 参数 执行完后 会返回该socket

socket.openurl(url)
通过持有上一步中得到的cookie来访问接下来的网络地址。


# auther: free.Wang<freefis@gmail.com>

import urllib
import urllib2

class Httpsocket:
 """ Build for Make a full HttpRequest via POST/GET """
 def __init__(self):
# login url to recieve param , Post/Get param
# opener will be used to open Url with cookie.
# build the cookie container.
  self.cookies = urllib2.HTTPCookieProcessor()
  self.opener = urllib2.build_opener(self.cookies)
  urllib2.install_opener(self.opener)

 def connect(self,loginurl,param):
 """make a Container for Cookie"""
  self.loginurl = loginurl
  self.param = param
  self.encodeparam = urllib.urlencode(self.param)
  try:
   urllib2.urlopen(urllib2.Request(self.loginurl,self.encodeparam))
  except:
   pass

 def openurl(self,url):
 """ get Response. """
# serialize the Param
# Login to Get Cookie ,Which will be Push into self.opener.
# get request with Cookie.
  return self.opener.open(url)

Sunday, February 8, 2009

Erlang 分布式爬虫

下面这个是主模块,后面2段代码分别是数据库模块,和一个附属功能模块.

%%%-------------------------------------------------------------------
%%% File : Distributed-Crawler
%%% Designer : free.Wang
%%% Description :
%%% Archieved : Jan 28, 2009
%%%-------------------------------------------------------------------
-module(crawler_server).
-behaviour(gen_server).
%% API
-export([start/1,login/0,crawl/0,check/0]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).


-define(MasterNode,'aaa@219.141.12.84').
-define(Downloadpath,'/Users/winfree/Project/distriawler/urlsea').
%-define(Mastername,).
%if this node is master.node , comment delow row.
%-define(Selfname,changjiangyihao).

%=====================================================================
% gen_server : {global,node()}
% send : mnesia:lookup(pid_table,node)
%=====================================================================

%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------

globalname(Mode) ->
if
Mode =:= 'slave' ->
[Name,_] = string:tokens(atom_to_list(node()),"@");
Mode =:= 'master' ->
[Name,_] = string:tokens(atom_to_list(?MasterNode),"@")
end,
list_to_atom(Name).




start(Mode) ->
if
Mode =:= 'master' ->
gen_server:start_link({global, globalname(master)}, ?MODULE, [], []);
Mode =:= 'slave' ->
net_adm:ping(?MasterNode),
gen_server:start_link({global, globalname(slave)}, ?MODULE, [], []),
login()
end.




%%====================================================================
%% gen_server callbacks
%%====================================================================

%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([]) ->
database:start(),
{ok,["success"]}.

%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%% {reply, Reply, State, Timeout} |
%% {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, Reply, State} |
%% {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------

%handle_call(_Request, _From, State) ->
% Reply = term(),
% {reply,ok,State};

login() ->
gen_server:call({global,globalname(master)},{login,{globalname(slave),node()}}).


handle_call({login,{Globalname,Node}},_From,State) ->
io:format("starting handling form ~p~n",[_From]),
[Id,Hostname] = string:tokens(atom_to_list(Node),"@"),
database:insert_address(Id,Hostname,Globalname),
% database exe. in MasterNode.
Reply = database:select_all(address),
{reply,Reply,State};

% this will be executed by Slave Node.
handle_call({crawl,Urls},_From,State) ->
lists:foreach(fun(Url) ->
io:format("~p~n",[Url]),
spawn(fun() ->
Command = " curl " ++ Url ++ " > " ++ lists:nth(2,string:tokens(Url,"//")),
% os:cmd("cd"++?Downloadpath),
os:cmd(Command),
io:format(" downloading :~p~n",[Command])
end)
end,Urls),
{reply,ok,State}.


%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast(_Msg, State) ->
{noreply, State}.

%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info(_Info, State) ->
{noreply, State}.

%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
ok.

%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.

%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------


allurls() ->
["http://www.g.cn","http://www.sina.com.cn","http://www.xiaonei.com",
"http://www.erlang.org","http://www.microsoft.com","http://www.yahoo.com",
"http://www.google.com","http://www.dell.com","http://www.doban.com"].

% communication with others.





check() ->
Nodes = database:select_all(address),
LoopNum = length(Nodes),
UrlGroups = tools:seperate_list(allurls(),LoopNum,[]),
io:format("~p~n------~p~n",[Nodes,UrlGroups]).

% this will deploy the CrawlerDuty by MasterNode.
crawl() ->
Nodes = database:select_all(address),
LoopNum = length(Nodes),
AIG = length(allurls()) div LoopNum,
UrlGroups = tools:seperate_list(allurls(),AIG,[]),
Final = lists:zip(Nodes,UrlGroups),
lists:foreach(fun(One) ->
{Node,UrlGroup} = One,
{_ ,_ ,_ , Globalname} = Node,
io:format("executed by ~p~n",[Globalname]),
spawn(fun() ->
gen_server:call({global,Globalname},{crawl,UrlGroup})
end)
end,Final).


下面是数据库模块:


-module(database).
-import(lists, [foreach/2]).
-compile(export_all).
-include_lib("stdlib/include/qlc.hrl").

%% API
-export([start/0,select_all/1,delete/2,insert_address/3,
insert_urltable/2]).



% address: {Globalname,id@hostname} to send Message.
% urltable:{"http://www.freeis.cn/new/year/","freeis.cn"}
-record(address, {id,hostname,globalname}).
-record(urltable, {url,domain}).



ready() ->
mnesia:create_schema([node()]),
mnesia:start(),
mnesia:create_table(address, [{attributes, record_info(fields, address )}]),
mnesia:create_table(urltable, [{attributes, record_info(fields, urltable)}]),
mnesia:stop().


start() ->
ready(),
mnesia:start(),
mnesia:wait_for_tables([address,urltable], 20000).

select_all(Table) ->
do(qlc:q([X || X <- mnesia:table(Table)])).

do(Q) ->
F = fun() -> qlc:e(Q) end,
{atomic, Val} = mnesia:transaction(F),
Val.

example_tables() ->
[%% The address table
{address, "jack", "10.1.225.117", "<0.33>"},
{address, "allen", "abc.example.com", "<7.313>"},
{address, "Foy", "chat.gogogo.com", "<1.214>"},
%% The urltable table
{urltable, "http://gogogo.sina.com", "sina.com"},
{urltable, "http://A.sohu.com", "sohu.com"},
{urltable, "http://B.avial.com", "avial.com"},
{urltable, "http://C.wgi.com", "wgi.com"}
].

insert_address(Id, Hostname, Globalname) ->
Row = #address{id=Id, hostname=Hostname, globalname=Globalname},
F = fun() ->
mnesia:write(Row)
end,
mnesia:transaction(F).

insert_urltable(Url,Domain) ->
Row = #urltable{url=Url,domain=Domain},
F = fun() ->
mnesia:write(Row)
end,
mnesia:transaction(F).

delete_address(Item) ->
Oid = {adress, Item},
F = fun() ->
mnesia:delete(Oid)
end,
mnesia:transaction(F).

delete_urltable(Item) ->
Oid = {urltable, Item},
F = fun() ->
mnesia:delete(Oid)
end,
mnesia:transaction(F).

delete(Table,Item) ->
if
Table =:= urltable ->
Oid = {urltable,Item};
Table =:= address ->
Oid = {address,Item}
end,
F = fun() ->
mnesia:delete(Oid)
end,
mnesia:transaction(F).

reset_tables() ->
mnesia:clear_table(urltable),
mnesia:clear_table(address),
F = fun() ->
foreach(fun mnesia:write/1, example_tables())
end,
mnesia:transaction(F).

get_plan(PlanId) ->
F = fun() -> mnesia:read({design, PlanId}) end,
mnesia:transaction(F)




这段是附加功能模块:

-module(tools).
-export([seperate_list/3,check/0]).

allurls() ->
["http://www.g.cn","http://www.sina.com.cn","http://www.xiaonei.com",
"http://www.erlang.org","http://www.microsoft.com","http://www.yahoo.com",
"http://www.google.com","http://www.dell.com","http://www.doban.com"].

% seperate_list([1,2,3,4,5],2,[]) ---> [ [1,2],[3,4],[5] ].
% seperate_list([1,2,3,4,5,6],2,[]) ---> [ [1,2],[3,4],[5,6] ].
seperate_list(List,NOP,LatestList) ->
% NOP = Number of One Page.
Rem = length(List) rem NOP,
Grp = lists:sublist(List,NOP+Rem),
Ext = lists:subtract(List,Grp),
if
length(Grp) > 0 ->
seperate_list(Ext,NOP,[Grp|LatestList]);
length(Grp) =:= 0 ->
LatestList
end.


check() ->
Allurl = allurls(),
seperate_list(Allurl,2,[]).

Thursday, January 1, 2009

Python2.5和python3.0性能循环测试

测试环境 Ubuntu 8.04 linux 2.6.24-23-386 i686 GNU/Linux

运行环境 python2.5/python3.0

分析命令:Top

测试结果分析:
分界线上段代码的用的无限循环。然后到某个值后跳出
1,2分别为python2.5,python3.0的执行环境
分界线下段代码的用的是遍历range
3,4分别为python2.5,python3.0的执行环境

先分析Python2.5
1,3 做横向比较, 1的CPU的消耗略小于2,而内存消耗明显高于2。
原因很简单:1 中 对i做累加,所以略占用计算资源。而2则是在内存中实现一组长序列(请原谅我未曾深入python C源码学习,range实现的数据结构我不了解)显然内存空间被大量占用。

再分析python3.0
2,4做横向比较, 内存消耗相差不大。因为python3.0中的range实质是从List->Iterator.但是CPU消耗是很大的。这个我们最后总结。

纵向比较1,2和3,4, 1的各方面都优于2。而3,4分别优于CPU和内存。

总结: python3.0 对 python2.5的数据结构做了很大改进(这里我们谈实现性能)。总体方向是很成功的。问题是,细节上,需要优化的地方太多太多,就像Guido之前所说。而至于python3.0 在实现上的改进的所有内容,请参见python.org.

就目前主流的 python2.5而言。把range作为循环的容器显然是很消耗资源的行为。
所以以后就该有个好习惯,如果循环序列很大,那么就用while做为一个好习惯。

虽然我们常说空间换时间,但在这种情况下,无非舍本逐末。

#!/usr/bin/env python
#-*- coding:utf-8 -*-

#-------------------------------------------
# Designer : free.Won
# Licence : License on GPL Licence
# Archieved : Jan 1st 2009
#-------------------------------------------

import threading

class Multirun(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
i=1
while 1:
if i== 10**7:
break
print(i)
i+=1

if __name__ == '__main__':
tset = []
for i in range(10000):
t = Multirun()
tset.append(t)
for one in tset:
one.start()
one.join()
1.
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
17305 freefis 20 0 23152 12m 1524 S 10.3 0.7 0:03.36 python

2.
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
17451 freefis 20 0 29836 18m 1720 S 28.0 1.0 0:08.92 python3.0


===========================================

#!/usr/bin/python3.0
#-*- coding:utf-8 -*-

#-------------------------------------------
# Designer : free.Won
# Licence : License on GPL Licence
# Archieved : Jan 1st 2009
#-------------------------------------------

import threading

class Multirun(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
for i in range(10**7):
print(i)

if __name__ == '__main__':
tset = []
for i in range(10000):
t = Multirun()
tset.append(t)
for one in tset:
one.start()
one.join()

3
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
17389 freefis 20 0 176m 166m 1524 S 9.0 8.8 0:02.76 python

4
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
17494 freefis 20 0 29836 18m 1720 S 47.9 1.0 0:10.46 python3.0

Wednesday, December 31, 2008

神州令

神州令 征天庭

年少梦狂吞江海 初醒摆弓射日月;傲雪炎阳铸经纬 孤舟昆仑祭轩辕。

笑谈浮生千杯过 醉悯天下苍生怜;棋点江山书韬略 峥嵘岁月惘然间。

天罡封臣凌烟阁 中原六路聚英贤;东海陈兵八千万 踏云乘风诛群仙。

怒火燎原斩鬼神 九州归定霜寒剑;古今豪杰出我辈 百年山河一朝卷。

Sunday, December 28, 2008

求数组中最长递增子序列

今天睡前翻看《编程之美》,这是每晚的必读。程序与数学在这帮Microsoft的天才工程师们手中结合得通俗易懂。
当中有个题目:写一个时间复杂度尽可能低的程序,求数组最长递增子序列 的解答方式让我觉得是那么不尽人意。
原题:求一个1维数组(N个元素)中的最长递增子序列的长度。
例如 L = [1,-1,2,-3,4,-5,6,-7] 的最长子序列是 1,2,4,5.

书上的解答思路过于注重了各个元素之间相互的比较。从系统工程学来解释就是内耗过大。所以在这样的思路下,书上的最低时间复杂度为O(n*logn).

于是我干脆跳出了这样一个思路。把时间复杂度控制到了O(n).

1 取出第1个元素E1为新的序列S1,
2 取出第2个元素E2和E1做比较,如果E2>E1,则追加到S1中; 否则,把E2放到新序列S2中
3 取出第3个元素E3和所有已有序列的最后一个元素比较,判断法则如同步骤2
.......
当序列遍历完成时,程序结束。

这是一个动态规划的思想。 就拿L为例,遍历L:每个虚线之间代表一个步骤
------------------------------
1
------------------------------
1
-1
------------------------------
1,2
-1,2
------------------------------
1,2
-1,2
-3
------------------------------
1,2,4
-1,2,4
-3,4
------------------------------
1,2,4
-1,2,4
-3,4
-5
------------------------------
1,2,4,6
-1,2,4,6
-3,4,6
-5 ,6
------------------------------
1,2,4,6
-1,2,4,6
-3,4,6
-5 ,6
-7
------------------------------


遍历结束。这样就列举出了所有的递增子序列,自然最长序列一目了然。但是这样的空间消耗又大了。 那么我们可以在这个基础上优化空间。
我们观察上面的各个步骤可以发现:
如果各个序列组的最后一个元素相同,那么非最长序列就一定不可能是后结果。(相关证明请参见文章末尾)
这样的话,我们重新遍历L。
------------------------------
1
------------------------------
1
-1
------------------------------
1,2
-1,2
------------------------------
1,2
-1,2
-3
------------------------------
1,2,4
-1,2,4
-3,4 该序列可删除
------------------------------
1,2,4
-1,2,4
-5
------------------------------
1,2,4,6
-1,2,4,6
-3,4,6 该序列可删除
-5 ,6 该序列可删除
------------------------------
1,2,4,6
-1,2,4,6
-5 ,6
-7
------------------------------
(完)


证明: 如果各个序列组的最后一个元素相同,那么非最长序列就一定不可能是后结果。
blogspot不支持TeX公式所以这里用文字叙述。

对于S1,S2....SN满足S1.LastElement = .... = SN.LastElement
=> 当L中取出元素E作比较,
如果E>S1.LastElement,则E>Sx.LastElement, 其中x属于1......N.
如果E无论L中的元素为何值,则S1到SN之间的长度大小关系不变。
=>S1到SN中的最长序列Smax不变(注意不是长度不变,而是序号不变)。
=>如果Sx不是最长序列,则Sx必然不可能成为整个程序的最终结果。


Saturday, December 27, 2008

Python3.0 Urllib体验

首先推荐大家接触python3.0. 有争议的东西毕竟是好东西.
http://python.org/ftp/python/3.0/Python-3.0.tar.bz2 可下载python3.0 可以推荐大家玩玩. 如果担心和当前python环境影响没关系.解压后进入该目录.

sudo su
mkdir /opt/python3.0
./configure --prefix=/opt/python3.0 && make && make install
ln -s /opt/python3.0/bin/python3.0 /usr/bin/python3.0



python3.0出了之后.像我这样热衷爬虫的偏执狂最关心的就是它的urllib.
可以去python.org看看文档,urllib是修改比较剧烈的模块.urllib2和urllib被整合到了urllib包.相应的方法都被归到不同模块中. 在下面的代码中,urlopen, urlencode 被定义在request,parse模块.




#!/usr/bin/python3.0
# -*- coding:utf-8 -*-

#-------------------------------------------
# Designer : Free.Wang
# E-mail : freefis@Gmail.com
# Licence : License on GPL Licence
# Archieved : Dec 27 2008
#-------------------------------------------

from urllib import request,parse

class Makesocket:
""" Build for Make a full HttpRequest via POST/GET """
def __init__(self,loginurl,param):
# login url to recieve param , Post/Get param
self.loginurl = loginurl
self.param = param

def cookie(self):
"""make a Container for Cookie"""
self.cookies = request.HTTPCookieProcessor()
# opener will be used to open Url with cookie.
# build the cookie container.
self.opener = request.build_opener(self.cookies)
request.install_opener(self.opener)

def run(self):
""" get Response. """
# serialize the Param
# Login to Get Cookie ,Which will be Push into self.opener.
# get request with Cookie.
self.encodeparam = parse.urlencode(self.param)
request.urlopen(self.loginurl,self.encodeparam)
conn = self.opener.open("http://www.hellofreefis.com/profile/")
print(conn)

if __name__ == '__main__':
oginurl = "http://www.hellofreefis.com/info_oper.php?tag=signin&pageref="
param = {
'name':"freefis",
'pwd':"freefis' passwod"
}
conn = Makesocket(loginurl,param)
conn.cookie()
conn.run()


测试成功.Py3.0的改动还是很大的.但愿Python4.0出现的时候,不会再来革命.我个人角度对这次改动还是很肯定.感谢Guido把reduce函数保留到functools模块中.
希望各大公司和*nix操作系统发行者能尽快支持python3.0.

P.S:以上代码让我觉得很"悲剧".blogspot转义空格的支持不够到位.若要迁移代码的朋友,请自行缩进.

Opensocial和F8的博弈

这次Google hackathon的深刻印象,有来自Google名扬天下美食,还有Google 天才工程师和各大SNS平台的大牛们,还有在Shawn Shen的推荐下做了一次GAE的临时工程师.
当然最大的收获在于有一次把Idea变成了现实.EasyWrite 让我从此把庞大复杂的Gmail变成了一个简单的备忘录. 最近在看Turing Award演讲集.其中1974年的唐纳德.克努特的<计算机编程艺术>让我觉得:
最自由的程序员的就是随时把自己的idea变为现实.虽然我一直不把自己定位于一个Programmer.

Opensocial的出现是备受争议的.不管是宏观到产品发展战略,还是围观到它的设计哲学.
但不论怎样,我该关心的,就是它是否适合开发者.在这里,衡量的标准,我坚持Jobs的Easy & Useful.

简单的搜索了一下关于 OPENSOCIAL的开发讨论.不少朋友都认为开发曲线比较陡峭.特别是需要对Javascript的能力.不过,任何一个杰出的行动都要经历3个步骤:贬低,讨论,行动.无论F8,OPS.
不管怎样,自己尝试是最好的. 说说我的理解:

区别1:微观的API调用行为.
OPS(这里我们简称OpenSocial)的是基于Javascript实现API容器.而F8是基于Http请求URL. 起初我觉得单独的一个URL访问是很直接明了的.然后和Google工程师Niucl做了一下讨论.他给了我一个很简单的回答,那就是OPS封装了这些具体的请求过程.从而使用户把注意力完全集中到了API中.而且OPS提供了更为丰富的API内容. 所以问题就在于,Google是假设用户具有一定Javascript开发水平的.

区别2:宏观的设计模式.
F8是放到了服务器.而OPS则是放到了客户端.然后再发回服务器.开始我不理解OPS为什么要多这一条生产环节.目前我觉得合理的理解是1.减轻应用程序服务器负担,2.用户体验更加直接.这样来自API返回数据的处理,是先交给了javascript,当然也可以再通过Ajax发回APP-SERVER,但从效率和性能上来说,这就不言而喻了. APP本身规模和返回数据的复杂性而言,也许并不需要交给BLL层. 不管这是不是处于OPS的本意,我认为这点还是符合我对APP的定位的.因为基于SNS的APP不需要太复杂的逻辑.否则这和用自己服务器搭建一个地道的SAAS有什么区别?


还有一个我很赞同的设计理念是统一了API SDK.OPS是强制于Javascript.JS的解释环境是主流浏览器都具备的. 对于F8标准,不同语言需要不同的SDK. 比如国内某些SNS的F8-xAPI的Python SDK就是我上海的一个朋友Damien从FACEBOOK的SDK改的.而且不仅不同开发语言间切换有难度,在不同SNS的API环境中切换也是一个难度.

而F8的优势,在于直接明了.SDK通过嵌入到BLL使得开发过程更加集中,因而提高了APP代码本身的内聚性,也更传统.OPS虽然通过 JS来试图延缓生产瓶颈,但正如我刚才提到的APP的定位,或许它本身的计算强度对BLL来说轻而易举.

所以总的来说.Google和Facebook的开放平台标准之争已经形成了一个纳什均衡的博弈格局. 竞争才有进步,这是我最希望看到的.
而将来我会采取什么标准来开发,把开发成本+挑战难度+心情结合起来吧.呵呵.

其实我最关心的是Google什么时候能举办一次关于App Engine 的Hackathon. :)

[原创]OpenSocial开发模式 TDM

上周参加了 google hackthon. 才了解到这个opensocial战略.我曾经开发过国内各大SNS APP 都是基于的F8标准.所以 第一反应是,OPS(opensocial)和F8 (facebook的开放平台,目前很多SNS公司都采用)各自的优势和特点是什么.

这里很明显的区别是它们提供容器环境的方式不同.OPS是通过Javascript API,而F8是通过一个HTTP请求一个固定的URL.


应用程序展示

我的开发项目是一个igoogle的gadget.
或者在blog下方的easywrite的widget进行测试.

Opensocial doc 中有这样一句话: All OpenSocial applications are gadgets. 所以Opensocial App和gadget的开发是完全一样的----- 用固定格式的XML包装.然后把相应的前端代码装进去.


开发框架Django/Python,服务器是Google Appengine, 所以我的开发模式是基于MVC而拓展的.

我先来展示我的应用:EasyWrite.我用它来嵌入到IGOOGLE或者BLOG中. 这样当发现有好的内容时,就可以及时在当中记录,然后发送到自己的E-mail中作为收藏.

OPS首先要定义一个XML文件.它有固定的格式.然后把前端代码嵌入到其中.
我把前端代码分成了2部分------A:html. B:Javascript.

A部分代码就是一个id="show"的div标签.

B部分代码如下:
function mail() {
var url = "http://testbyfree.appspot.com/gadget/mail/";
var action = "POST";
var param = {};
param['addr'] = $('#addr').attr("value");
param['content'] = $('#content').attr("value");
request(url,param,action);
}

function response(data) {
$("#show").empty();
if (data.text.length>=5) { document.getElementById('show').innerHTML = data.text; }
else { request("http://testbyfree.appspot.com/gadget/mail/"); }
}

function request(url,cgidata,action) {
var params = {};
params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.TEXT;
params[gadgets.io.RequestParameters.POST_DATA] = gadgets.io.encodeValues(cgidata);
params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.GET;
if (action == 'POST') { params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.POST; }
gadgets.io.makeRequest(url, response, params);
}

var initurl = "http://testbyfree.appspot.com/gadget/mail/";
request(initurl);


这里我简单介绍js逻辑---
用户在提交后触发mail(),然后通过request()向APP SERVER发出请求,最后response()接到响应并输出到show的div中.
而UI的html代码(点击此处查看,GOOGLE BLOG不会转义html,所以我没列出来) 并没有写在XML中,而是放在服务器端.其实就是3个input button textarea标签.




App服务器端的python代码

# running as Django App on Google App Enging
def mail_page(request):
""" the BLL handle for Http://testbyfree.appspot.com/gadget/mail/ """
if request.POST.has_key('content'):
# this Mail API supported by GAE.
from google.appengine.api import mail
content,addr = request.POST['content'], request.POST['addr']
# the sender master be the admin of GAE APP.
message = mail.EmailMessage(
sender="freefis@gmail.com",
subject="deliver from easywrite"
)
message.to , message.body = addr , content
message.send()
return render_to_response('debug.html',{'info':"ok!","mail":"mail"})
else:
return render_to_response("mail.html")


设计模式 (Two District Mode):

以上就是这次开发的全部代码. 对于刚接触OPS的开发者来说,更关心的是javascript部分和XML部分.所以我针对这个问题,最后归纳出了这样一个开发模式:

虚线代表应答,实现代表请求。流程图有2种方式
显示区 -> JS逻辑 -> API 服务器 -> JS逻辑 -> 显示区
显示区 -> JS逻辑 -> APP服务器 -> JS逻辑 -> 显示区

橙色部分 代表XML文件包含的内容.
蓝色部分 代表服务器端.
绿色部分 代表API服务器.

我来解释一下我的设计思想:
XML文件的代码部分我分成了2个代码区:javascript区和html区. 前者包含了这个APP程序的所有javascript代码.后者就是一个简单的div标签 id="show".

一.html区.
1.服务器端显示层的HTML代码的BOBY部分可以完全原封不动的被response到div中.所以相关的设计者可以完全专注在现实层中的UI开发,不必考虑在XML中要如何设计.
2.javascript区从api-server中取得的数据,可以完全在本区中组织成相应的HTML然后返回到div中.
这样1和2的HTML设计就各自独立.不用考虑相互之间的影响.

二.javascript区
1.OPS的主要问题是对javascript的依赖,这么做可以不至于使相关代码在前端开发中过于混乱.更为关键的是集中javascript代码,使得不同代码之间的耦合松散.
2.统一了同APP服务器和API服务器的通信.明显区分了代码的功能性部分和非功能性部分.


这样一个模式的设计和F8应用程序设计的区别仅仅在于,当各开发者在后者的相关代码只需要2步:
1.把JS分离到XML文件上,2.修改相应API接口(这步对于任何开发模式都无法避免) 就可以迁移到OPS上.
这样可以旧代码迁移的成本, 而对于新的代码,可以显得思路更加清晰,分工度明细.

这就是这个相关设计模式的雏形.希望有很多朋友能够交流自己的想法.共同改进 : )



Wednesday, December 24, 2008

阅读笔记 Dec.08

近期阅读了大量的书籍.远远超过我在BIT前3年的阅读量.BETTER LATE THAN NEVER.

<分布式算法导论> <系统工程> <博弈战略学> 这三本是交换着看的.

之所以如此刻意安排, 是因为我想把计算机的思路往整个工业生产相结合. 计算机领域的很多牛人都是非本专业诞生,思考下原因,就我的接触面和体会来说,有2点:
1.兴趣, 2.更广的知识领域.
关于1的理解,我是同不少CS SE 专业的朋友讨论,感觉有不少人都迷失于本专业的前景. 为什么? 教育的引导问题? 这个我没资格讨论. 因为至今我没有上过任何一门计算机课程.
而关于2的理解, 我接触了自身的精力. 好比我自身是学习AA(Aeronautics and Astronautics)领域的,也许像我这样的CS偏执狂能从新的领域或者是视野来看待计算机工程本身领域和相关工业领域的交融.

分布式算法导论 我想将来会成为主流级书籍.毕竟这是计算机发展的未来关键.从大型计算机到小型计算机集群,再到计算机神经网络.

系统工程,则是将复杂生产归结为一个总体,然后从不同角度去细化成多个部分加以研究,按照各个目标进行权衡,全面求得最优解的方法,并使各组成部分能够最大限度地相互协调.以求取最大合作效果和最低成本.

战略博弈学则是一种大局上的思考.和系统工程的目的相同,那就是取得最优解.

同时推荐另外两本读物: 一本是<图灵奖演讲集(1966-1985)>,另一本是 <美国制宪辩论记录>.

前者是计算机界的大师级人物的经典著作. 正本书没有多少代码.,大都从数学和逻辑学,美学,系统科学去描述了计算机科学的思想精髓. 大师们的计算机本身深刻的思考和独到的理解,我相信在阅读之后, 留给我们的就是对计算机更本质学习.
当然在这个过程中,我一直专注于对计算机的学习,一是由于自己的兴趣,而是希望能够把对CS的理解延伸到整个系统工程类领域,无论在人工智能,力学体系,引信系统,还是在宏观经济学,最终到整个航空航天学领域,我想都是很有用的.

美国制宪记录我之所以有兴趣,一是因为对1787年立宪过程的那次经典辩论的着迷,二是本身这就是一个博弈的经典案例. 关于它的阅读笔记,我想在我通读完成之后,再单独写一次笔记.

Wednesday, December 17, 2008

分布式gen_server结构

在programming in Erlang中 16章最后给出了my_bank.erl的一个gen_server 实现. 当时一直在琢磨分布式的问题.把C/S分离.后来做了测试. gen_server实现的6个必要条件是 start_link , init , handle_call , handle_cast , handle_info , terminate.
换句话说,这是最基本的gen_server实现. 那么我们只需要把这6个函数放在一个执行在服务器端的模块里.
然后其他逻辑就可以转移到客户端节点了.

而客户端的主要逻辑是:
gen_server:call(Name,Request).

这样一个部署逻辑就实现了. 在具体code过程中,
server:start_link 的Name 要用 {global,arbitary_atom}.

client:gen_server:call 的Name 要用 {global,arbitary_atom}.


最后集群启动 erl -name id@domain
发送简单的 net_adm:ping(server_id@domain).
(其实server ping client 也行,但是对于伸缩性集群来说,新节点加进来的时候,当然第一行动是它发出请求,而不是server.所以这里应当养成良好习惯)

这一步的作用是建立了节点之间的通信,在完成之后,节点间会把global注册了的全局名称发布到集群中.

gen_server的分布式结构就完成了. 当我们构建一个强大多应用的服务器时,就可以把gen_server的逻辑分成N部分,然后部署到N个节点中.

这样,ERLANG的巨大威力就完美的发挥了.

Monday, December 8, 2008

Erlang Mapreduce分析

最近在研究Erlang上的MapReduce框架. 本段代码摘自<>20章 Multicores Programming.

开始----


% 代码来自phofs.erl 模块. phofs is short for Parrallel Highrt-Order Function.
-module(phofs).
-export([mapreduce/4]).

-import(lists, [foreach/2]).

%% F1(Pid, X) -> sends {Key,Val} messages to Pid
%% F2(Key, [Val], AccIn) -> AccOut


mapreduce(F1, F2, Acc0, L) ->
S = self(),
Pid = spawn(fun() -> run(S, F1, F2, Acc0, L) end),
receive
{Pid, Result} ->
Result
end.

% 本段代码的主函数 mapreduce由4个参数组成.
% F1 - Map函数
% F2 - Reduce函数
% Acc0 - 累加器的初始值, 函数最后会返回Acc0的最后累加结果
% L - 运算数据,装载在一个序列中.
run(Parent, F1, F2, Acc0, L) ->
process_flag(trap_exit, true),
ReducePid = self(),

%% 启动 Map运算
%% 对 序列L 中的每一个单元e 进行F1(e)运算. 并为每个单元创建一个进程
%% 此步是ERLANG优势的最大体现.最大化计算性能
foreach(fun(X) ->
spawn_link(fun() -> do_job(ReducePid, F1, X) end)
end, L),

% 获得 序列L 的元素量N.也就是总进程数N,并等待来自N个进程的结果,存到Dict0.
N = length(L),
Dict0 = dict:new(),
Dict1 = collect_replies(N, Dict0),

%% 启动Reduce运算,F2 代表某种Reduce 法则.用户可以自己选择并归的方案.
%% 对Dict1 哈希序列的key-value元素e 运行F2(e)并返回Acc0的累加器.
%% dict:fold函数我研究了好久,后来不断的查看文档,搜结果,讨论(可能本身愚钝:P), 其相当于lists:foldr
%% 也就是相当于python的reduce函数 e.g: reduce(lambda x,y:x+y,range(5)) ===> 1+2+3+4+5.运算集合来自Dict1,运算法则来自F2.
Acc = dict:fold(F2, Acc0, Dict1),

%% 最后把结果返回主函数mapreduce. 并打印.
Parent ! {self(), Acc}.

%% collect_replies 函数 负责处理Map进程的返回结果.并存入某个容器(这里用的是进程字典,同样,Ets,Dts都是可选容器)
%% 当某个Map 任务完成,会自动结束起进程proc,并发送'EXIT',这时候collect_replies会把这个proc 从等待进程序列删除.
>%% 当N=0时,所有map任务完毕,就会把所有相同key值的元素整合到同一个序列中作为新value,并重新对应相应的key.
%% 最后形成一个完整的 key-value Hash Table, 并返回到上面的 Dict1.

collect_replies(0, Dict) ->
Dict;
collect_replies(N, Dict) ->
receive
{Key, Val} ->
case dict:is_key(Key, Dict) of
true ->
Dict1 = dict:append(Key, Val, Dict),
collect_replies(N, Dict1);
false ->
Dict1 = dict:store(Key,[Val], Dict),
collect_replies(N, Dict1)
end;
{'EXIT', _, _} ->
collect_replies(N-1, Dict)
end.

%% Call F(Pid, X)
%% F must send {Key, Value} messsages to Pid
%% and then terminate

do_job(ReducePid, F, X) ->
F(ReducePid, X).


-----完

提示:鉴于对MapReduce函数的设计,Reduce必须等待所有相关Map进程返回才能开始启动.所以合理的配置Map-Reduce节点是关键,避免时间冗余,最小化等待时间,是M/R 架构师能力的重要体现


Wednesday, December 3, 2008

[原创]搜索引擎分词算法

最近在写www.bitok.cn的时候,一直在想方设法的优化搜索引擎的人工智能程度. 主要在中文分词上,自动分析用户的想法.

1 .比如有朋友搜索 "北京 大学". 那么SE(以后简称代替搜索引擎) 会根据 空格 把 关键字 分成 "北京"和"大学".

2. 但是如果把 空格去掉,输入 "北京大学". 那么就是一个不同的结果,精确搜索结果.

但如果要保证最大模糊度+最高精确度,这个就是难度了.

举个例子,我输入"北京大学", 我希望把 直接匹配的 "北京大学"(参见2的搜索结果) 排在前面,同时兼顾"北京" "大学"的分词,把相应模糊结果跟着搜索出来. 这个就是难点了.

参考了google算法之美. 我发现了一些方法. 比如某个关键词 "发展中国家" .

他主要的分词归类是 1."发展中"+"国家", 2. "发展"+"中国"+"家"

最普遍的方法是,根据词库,把1,2 的组合 带入到 词库(这里我们先忽略词库本身的不确定因素)中,去判断1和2所出现的概率, 最高的为最优.

-----------

但是最近闲看空气动力学的计算力学分析,其方法就是把力学问题的数学模型进行尽可能小粒度的微分,然后分析后综合.


所以我想到一个方法,就是把关键词(比如 "发展中国家" )全部按 字 来分开,比如 "发"+"展"+"中"+"国"+"家",

代入到搜索SE中用搜索,结果为 QuerySet0.

然后重新组成序列:
------------ "发展"+"展中"+"中国"+"国家"+"发展中"+"展中国"+"中国家"+"发展中国"+"展中国家".

然后带入到词库中. 假设词库足够的常用词汇统计,那么他就会把序列缩减为:
------------ "发展"+"中国"+"国家"+"发展中".

这时候再对QuerySet0分别对以上4个词进行查询,得出Query1,Query2,Query3,Query4.(每个集合中肯定有结果是重复的)

然后把Query1~4 进行整合.统计出每个结果被重复的次数.其中重复次率最高的分词结果则是最优分词结果.

(完)

Monday, December 1, 2008

django WEEKDAYS_ABBR 错误解决方案

最近在用django写学校的搜索导航.结果在ADMIN 页面 遇到了 WEEKDAYS_ABBR 未定义的情况.

这个问题是 django 0.96到1.0的 BUG. 在最新的django trunk版本就已经fix了. 所以遇到这种情况下,最彻底的方法就是重新安装最新django.

而学校最近在封外网代理. 所以我干脆直接COPY了 WEEKDAYS_ABBR .


在 django.utils.dates 中 添加:


WEEKDAYS_ABBR = {0:_('Mon'), 1:_('Tue'), 2:_('Wed'), 3:_('Thu'), 4:_('Fri'),5:_('Sat'), 6:_('Sun')}


Problem resolved.

linux matlab 2008 中文支持解决

操作系统:ubuntu 8.04
matlab版本:r2008b

matlab的GUI是用JAVA完成的。 所以基于matlab的中文问题的实质就是java环境的解决。

0.安装最新java环境 sun-java6-jre

1.复制其文件夹/usr/jvm/sun...到 $MATLABPATH/sys/java/jre/glnx86/ 并改名为jre

2.cd 进 $MATLABPATH/sys/java/jre/glnx86/jre/lib/fonts/ ,并makedir fallback

3.把 windows系统中的 WINDOWS/Fonts/simsun.ttc 复制到 fallback中

4.cd fallback && mkfontscale

5. cd .. && cat fallback/fonts.scale >> fonts.dir && chmod 666 fonts.dir

6. chmod 766 fallback/*

Sunday, November 30, 2008

学习计划

好久没来这里. 没想到google blog  通过了 gfw.
简单拟一下学习计划.

0.www.bitok.cn上网导航人工智能模型(python,C)

1.分布式爬虫架构(Python+Erlang+C)

2.计算机神经网络和分布式算法研究(MatLab+Erlang+Python+C)

3.空气动力学模型Matlab研究.

4.搞定vc. 

5.深入学习Matlab+Lua.

6.图灵机及相关算法研究

希望在明年3月份以前完成这些的简单模型.   我希望我明年6月份的毕业答辩会用我惊世骇俗的研究成果像老师表明一下小龙的野心.

Followers