posted @ 2007-08-29 18:10 dennis 閱讀(4735) | 評(píng)論 (6) | 編輯 收藏
1.fopen與open
標(biāo)準(zhǔn)I/O使用fopen函數(shù)打開(kāi)一個(gè)文件:
FILE* fp=fopen(const char* path,const char *mod)
其中path是文件名,mod用于指定文件打開(kāi)的模式的字符串,比如"r","w","w+","a"等等,可以加上字母b用以指定以二進(jìn)制模式打開(kāi)(對(duì)于*nix系統(tǒng),只有一種文件類(lèi)型,因此沒(méi)有區(qū)別),如果成功打開(kāi),返回一個(gè)FILE文件指針,如果失敗返回NULL,這里的文件指針并不是指向?qū)嶋H的文件,而是一個(gè)關(guān)于文件信息的數(shù)據(jù)包,其中包括文件使用的緩沖區(qū)信息。
*nix系統(tǒng)使用open函數(shù)用于打開(kāi)一個(gè)文件:
int fd=open(char *name,int how);
與fopen類(lèi)似,name表示文件名字符串,而how指定打開(kāi)的模式:O_RDONLY(只讀),O_WRONLY(只寫(xiě)),O_RDWR (可讀可寫(xiě)),還有其他模式請(qǐng)man 2 open。成功返回一個(gè)正整數(shù)稱(chēng)為文件描述符,這與標(biāo)準(zhǔn)I/O顯著不同,失敗的話返回-1,與標(biāo)準(zhǔn)I/O返回NULL也是不同的。
2.fclose與close
與打開(kāi)文件相對(duì)的,標(biāo)準(zhǔn)I/O使用fclose關(guān)閉文件,將文件指針傳入即可,如果成功關(guān)閉,返回0,否則返回EOF
比如:
printf("Error in closing file");
而*nix使用close用于關(guān)閉open打開(kāi)的文件,與fclose類(lèi)似,只不過(guò)當(dāng)錯(cuò)誤發(fā)生時(shí)返回的是-1,而不是EOF,成功關(guān)閉同樣是返回0。C語(yǔ)言用error code來(lái)進(jìn)行錯(cuò)誤處理的傳統(tǒng)做法。
3.讀文件,getc,fscanf,fgets和read
標(biāo)準(zhǔn)I/O中進(jìn)行文件讀取可以使用getc,一個(gè)字符一個(gè)字符的讀取,也可以使用gets(讀取標(biāo)準(zhǔn)io讀入的)、fgets以字符串單位進(jìn)行讀取(讀到遇到的第一個(gè)換行字符的后面),gets(接受一個(gè)參數(shù),文件指針)不判斷目標(biāo)數(shù)組是否能夠容納讀入的字符,可能導(dǎo)致存儲(chǔ)溢出(不建議使用),而fgets使用三個(gè)參數(shù):
char * fgets(char *s, int size, FILE *stream);
第一個(gè)參數(shù)和gets一樣,用于存儲(chǔ)輸入的地址,第二個(gè)參數(shù)為整數(shù),表示輸入字符串的最大長(zhǎng)度,最后一個(gè)參數(shù)就是文件指針,指向要讀取的文件。最后是fscanf,與scanf類(lèi)似,只不過(guò)增加了一個(gè)參數(shù)用于指定操作的文件,比如fscanf(fp,"%s",words)
*nix系統(tǒng)中使用read函數(shù)用于讀取open函數(shù)打開(kāi)的文件,函數(shù)原型如下:
ssize_t numread=read(int fd,void *buf,size_t qty);
其中fd就是open返回的文件描述符,buf用于存儲(chǔ)數(shù)據(jù)的目的緩沖區(qū),而qty指定要讀取的字節(jié)數(shù)。如果成功讀取,就返回讀取的字節(jié)數(shù)目(小于等于qty)。
4.判斷文件結(jié)尾,如果嘗試讀取達(dá)到文件結(jié)尾,標(biāo)準(zhǔn)IO的getc會(huì)返回特殊值EOF,而fgets碰到EOF會(huì)返回NULL,而對(duì)于*nix的read函數(shù),情況有所不同。read讀取qty指定的字節(jié)數(shù),最終讀取的數(shù)據(jù)可能沒(méi)有你所要求的那么多(qty),而當(dāng)讀到結(jié)尾再要讀的話,read函數(shù)將返回0.
5.寫(xiě)文件:putc,fputs,fprintf和write
與讀文件相對(duì)應(yīng)的,標(biāo)準(zhǔn)C語(yǔ)言I/O使用putc寫(xiě)入字符,比如:
putc(ch,fp);
第一個(gè)參數(shù)是字符,第二個(gè)是文件指針。而fputs與此類(lèi)似:
fputs(buf,fp);
僅僅是第一個(gè)參數(shù)換成了字符串地址。而fprintf與printf類(lèi)似,增加了一個(gè)參數(shù)用于指定寫(xiě)入的文件,比如:
fprintf(stdout,"Hello %s.\n","dennis");
切記fscanf和fprintf將FILE指針作為第一個(gè)參數(shù),而putc,fputs則是作為第二個(gè)參數(shù)。
在*nix系統(tǒng)中提供write函數(shù)用于寫(xiě)入文件,原型與read類(lèi)似:
ssize_t result=write(int fd,void *buf ,size_t amt);
fd是文件描述符,buf是將要寫(xiě)入的內(nèi)存數(shù)據(jù),amt是要寫(xiě)的字節(jié)數(shù)。如果寫(xiě)入成功返回寫(xiě)入的字節(jié)數(shù),通過(guò)result與amt的比較可以判斷是否寫(xiě)入正常,如果寫(xiě)入失敗返回-1。write函數(shù)僅僅是將數(shù)據(jù)寫(xiě)入了緩沖區(qū),何時(shí)寫(xiě)入磁盤(pán)由內(nèi)核決定,如果要強(qiáng)制寫(xiě)入硬盤(pán),那么在open的時(shí)候選擇O_SYNC選項(xiàng),或者調(diào)用fsync函數(shù)
6.隨機(jī)存取:fseek()、ftell()和lseek()
標(biāo)準(zhǔn)I/O使用fseek和ftell用于文件的隨機(jī)存取,先看看fseek函數(shù)原型
int fseek(FILE *stream, long offset, int whence);
第一個(gè)參數(shù)是文件指針,第二個(gè)參數(shù)是一個(gè)long類(lèi)型的偏移量(offset),表示從起始點(diǎn)開(kāi)始移動(dòng)的距離。第三個(gè)參數(shù)就是用于指定起始點(diǎn)的模式,stdio.h指定了下列模式常量:
SEEK_SET 文件開(kāi)始處
SEEK_CUR 當(dāng)前位置
SEEK_END 文件結(jié)尾處
看幾個(gè)調(diào)用例子:
fseek(fp,0L,SEEK_SET); //找到文件的開(kāi)始處
fseek(fp,0L,SEEK_END); //定位到文件結(jié)尾處
fseek(fp,2L,SEEK_CUR); //文件當(dāng)前位置向前移動(dòng)2個(gè)字節(jié)數(shù)
而ftell函數(shù)用于返回文件的當(dāng)前位置,返回類(lèi)型是一個(gè)long類(lèi)型,比如下面的調(diào)用:
fseek(fp,0L,SEEK_END);//定位到結(jié)尾
long last=ftell(fp); //返回當(dāng)前位置
那么此時(shí)的last就是文件指針fp指向的文件的字節(jié)數(shù)。
與標(biāo)準(zhǔn)I/O類(lèi)似,*nix系統(tǒng)提供了lseek來(lái)完成fseek的功能,原型如下:
off_t lseek(int fildes, off_t offset, int whence);
fildes是文件描述符,而offset也是偏移量,whence同樣是指定起始點(diǎn)模式,唯一的不同是lseek有返回值,如果成功就返回指針變化前的位置,否則返回-1。因此可以通過(guò)下列方法模擬ftell函數(shù)來(lái)返回當(dāng)前偏移量:
off_t currpos;
currpos = lseek(fd, 0, SEEK_CUR);
whence的取值與fseek相同:SEEK_SET,SEEK_CUR,SEEK_END,但也可以用整數(shù)0,1,2相應(yīng)代替。
最后,以一個(gè)例子結(jié)尾,通過(guò)c語(yǔ)言編寫(xiě)linux系統(tǒng)的cp指令,先看看使用標(biāo)準(zhǔn)I/O版本的:
#include<stdlib.h>
void oops(char *,char *);
int main(int ac,char *av[])
{
FILE *in,*out;
int ch;
if(ac!=3){
fprintf(stderr,"Useage:%s source-file target-file.\n",av[0]);
exit(1);
}
if((in=fopen(av[1],"r"))==NULL)
oops("can not open ",av[1]);
if((out=fopen(av[2],"w"))==NULL)
oops("can not open ",av[2]);
while((ch=getc(in))!=EOF)
putc(ch,out);
if(fclose(in)!=0||fclose(out)!=0)
oops("can not close files.\n"," ");
return 0;
}
void oops(char *s1,char* s2)
{
fprintf(stderr,"Error:%s %s\n",s1,s2);
exit(1);
}
再看一個(gè)使用unix io的版本:
#include<stdio.h>
#include<fcntl.h>
#define BUFFERSIZE 4096
#define COPYMODE 0644
void oops(char *,char *);
int main(int ac,char *av[])
{
int in_fd,out_fd,n_chars;
char buf[BUFFERSIZE];
if(ac!=3){
fprintf(stderr,"useage:%s source-file target-file.\n",av[0]);
exit(1);
}
if((in_fd=open(av[1],O_RDONLY))==-1)
oops("Can't open ",av[1]);
if((out_fd=creat(av[2],COPYMODE))==-1)
oops("Can't open ",av[2]);
while((n_chars=read(in_fd,buf,BUFFERSIZE))>0)
if(write(out_fd,buf,n_chars)!=n_chars)
oops("Write error to ",av[2]);
if(n_chars==-1)
oops("Read error from ",av[1]);
if(close(in_fd)==-1||close(out_fd)==-1)
oops("Error closing files","");
return 0;
}
void oops(char *s1,char *s2)
{
fprintf(stderr,"Error:%s",s1);
perror(s2);
exit(1);
}
顯然,在使用unix i/o的時(shí)候,你要更多地關(guān)注緩沖問(wèn)題以提高效率,而stdio則不需要考慮。
posted @ 2007-08-22 16:47 dennis 閱讀(1333) | 評(píng)論 (0) | 編輯 收藏
posted @ 2007-08-10 18:40 dennis 閱讀(386) | 評(píng)論 (1) | 編輯 收藏
posted @ 2007-08-06 12:37 dennis 閱讀(7976) | 評(píng)論 (7) | 編輯 收藏
不過(guò)我分析了這個(gè)測(cè)試?yán)锏腅rlang代碼,存在問(wèn)題,并沒(méi)有完成所有的循環(huán),進(jìn)程就結(jié)束了,這對(duì)比較結(jié)果有較大的影響。原始代碼如下:
-module(zog).我將process修改一下:
%% This is a test program that first creates N processes (that are
%% "connected" in a ring) and then sends M messages in that ring.
%%
%% - September 1998
%% - roland
-export([start/0, start/1, start/2]).
-export([run/2, process/1]). % Local exports - ouch
start() -> start(16000).
start(N) -> start(N, 1000000).
start(N, M) -> spawn(?MODULE, run, [N, M]).
run(N, M) when N < 1 ->
io:format("Must be at least 1 process~n", []),
0.0;
run(N, M) ->
statistics(wall_clock),
Pid = setup(N-1, self()),
{_,T1} = statistics(wall_clock),
io:format("Setup : ~w s", [T1/1000]),
case N of
1 -> io:format(" (0 spawns)~n", []);
_ -> io:format(" (~w us per spawn) (~w spawns)~n",
[1000*T1/(N-1), N-1])
end,
statistics(wall_clock),
Pid ! M,
K = process(Pid),
{_,T2} = statistics(wall_clock),
Time = 1000*T2/(M+K),
io:format("Run : ~w s (~w us per msg) (~w msgs)~n",
[T2/1000, Time, (M+K)]),
Time.
setup(0, OldPid) ->
OldPid;
setup(N, OldPid) ->
NewPid = spawn(?MODULE, process, [OldPid]),
setup(N-1, NewPid).
process(Pid) ->
receive
M ->
Pid ! M-1,
if
M < 0 -> -M;
true -> process(Pid)
end
end.
process(Pid) ->然后執(zhí)行下zog:run(3,3),你將發(fā)現(xiàn)消息繞了兩圈就結(jié)束了,第三圈根本沒(méi)有進(jìn)行,不知道測(cè)試者是什么用意。依照現(xiàn)在的執(zhí)行300萬(wàn)次消息傳送竟然只需要3毫秒!我修改了下了下代碼如下:
receive
M ->
Pid ! M-1,
io:format("form ~w to ~w~n",[self(),Pid]),
if
M < 0 -> -M;
true -> process(Pid)
end
end.
-module(zog).
%% This is a test program that first creates N processes (that are
%% "connected" in a ring) and then sends M messages in that ring.
%%
%% - September 1998
%% - roland
-export([start/0, start/1, start/2]).
-export([run/2, process/2]). % Local exports - ouch
start() -> start(16000).
start(N) -> start(N, 1000000).
start(N, M) -> spawn(?MODULE, run, [N, M]).
run(N, M) when N < 1 ->
io:format("Must be at least 1 process~n", []),
0.0;
run(N, M) ->
statistics(wall_clock),
Limit=N-N*M+1+M,
Pid = setup(N-1,Limit,self()),
{_,T1} = statistics(wall_clock),
io:format("Setup : ~w s", [T1/1000]),
case N of
1 -> io:format(" (0 spawns)~n", []);
_ -> io:format(" (~w us per spawn) (~w spawns)~n",
[1000*T1/(N-1), N-1])
end,
statistics(wall_clock),
% io:format("run's Pid=~w~n",[Pid]),
Pid ! M,
K = process(Pid,Limit),
% io:format("run's K=~w~n",[K]),
{_,T2} = statistics(wall_clock),
Time = 1000*T2/(M+K),
io:format("Run : ~w s (~w us per msg) (~w msgs)~n",
[T2/1000, Time, (M+K)]),
T2/1000.
setup(0,Limit, OldPid) ->
OldPid;
setup(N,Limit, OldPid) ->
NewPid = spawn(?MODULE, process, [OldPid,Limit]),
setup(N-1, Limit,NewPid).
process(Pid,Limit) ->
receive
M ->
Pid ! M-1,
% io:format("from ~w to ~w and M=~w~n",[self(),Pid,M]),
if
M <Limit -> -M;
true -> process(Pid,Limit)
end
end.
修改之后,執(zhí)行zog:run(3000,1000),也就是3000個(gè)進(jìn)程,1000次消息循環(huán),總共300萬(wàn)次消息傳遞,結(jié)果在2.5秒左右,這也是相當(dāng)驚人的結(jié)果。有人用haskell和scheme各實(shí)現(xiàn)了一個(gè)版本,有興趣的看看這里和這里
posted @ 2007-08-04 17:46 dennis 閱讀(1531) | 評(píng)論 (0) | 編輯 收藏
C的5種存儲(chǔ)類(lèi):
自動(dòng)——在一個(gè)代碼塊內(nèi)(或在一個(gè)函數(shù)頭部作為參量)聲明的變量,無(wú)論有沒(méi)有存儲(chǔ)類(lèi)修飾符auton,都屬于自動(dòng)存儲(chǔ)類(lèi)。該類(lèi)具有自動(dòng)存儲(chǔ)時(shí)期、代碼塊的作用域和空鏈接(no linkage),如未初始化,它的值是不確定的(java要求局部變量必須初始化)
寄存器——在一個(gè)代碼塊內(nèi)(或在一個(gè)函數(shù)頭部作為參量)使用修飾符register聲明的變量屬于寄存器存儲(chǔ)類(lèi)。該類(lèi)與自動(dòng)存儲(chǔ)類(lèi)相似,具有自動(dòng)存儲(chǔ)時(shí)期、代碼塊作用域和空連接,聲明為register僅僅是一個(gè)請(qǐng)求,而非命令,因此變量仍然可能是普通的自動(dòng)變量,但是仍然無(wú)法獲取地址。。如果沒(méi)有被初始化,它的值也是未定的。
靜態(tài)、空鏈接——在一個(gè)代碼塊內(nèi)使用存儲(chǔ)類(lèi)修飾符static聲明的局部變量屬于靜態(tài)空連接存儲(chǔ)類(lèi)。該類(lèi)具有靜態(tài)存儲(chǔ)時(shí)期、代碼塊作用域和空鏈接,僅在編譯時(shí)初始化一次。如未明確初始化,它的字節(jié)將被設(shè)定為0.
靜態(tài)、外部鏈接——在所有函數(shù)外部定義、未使用static修飾的變量屬于靜態(tài)、外部鏈接存儲(chǔ)類(lèi)。改類(lèi)具有靜態(tài)存儲(chǔ)時(shí)期、文件作用域和外部鏈接,僅在編譯時(shí)初始化一次。如未明確初始化,它的字節(jié)也被設(shè)定為0.
靜態(tài)、內(nèi)部鏈接——與靜態(tài)、外部鏈接存儲(chǔ)類(lèi)不同的是,它使用static聲明,也定義在所有函數(shù)外部,但是具有內(nèi)部鏈接(僅能被與它在同一個(gè)文件的函數(shù)使用),僅在編譯時(shí)初始化一次。如未明確初始化,它的字節(jié)也被設(shè)定為0.
兩個(gè)關(guān)鍵字:volatile和restrict,兩者都是為了方便編譯器的優(yōu)化。
volatile告訴編譯器該被變量除了可被程序修改意外還可能被其他代理修改,因此,當(dāng)要求使用volatile 聲明的變量的值的時(shí)候,系統(tǒng)總是重新從它所在的內(nèi)存讀取數(shù)據(jù),而不是使用寄存器中的緩存。比如
val1=x;
val2=x;
如果沒(méi)有聲明volatile,系統(tǒng)在給val2賦值的時(shí)候可能直接從寄存器讀取x(假定聰明的編譯器優(yōu)化了),而不是從內(nèi)存的初始位置,那么在兩次賦值之間,x完全有可能被被某些編譯器未知的因素更改(比如:操作系統(tǒng)、硬件或者其它線程等)。如果聲明為volatile,編譯器將不使用緩存,而是每次都從內(nèi)存重新讀取x。
而restrict是c99引入的,它只可以用于限定指針,并表明指針是訪問(wèn)一個(gè)數(shù)據(jù)對(duì)象的唯一且初始的方式,考慮下面的例子:
int ar[10];
int * restrict restar=(int *)malloc(10*sizeof(int));
int *par=ar;
這里說(shuō)明restar是訪問(wèn)由malloc()分配的內(nèi)存的唯一且初始的方式。par就不是了。
那么:
for(n=0;n<10;n++)
{
par[n]+=5;
restar[n]+=5;
ar[n]*=2;
par[n]+=3;
restar[n]+=3;
}
因?yàn)閞estar是訪問(wèn)分配的內(nèi)存的唯一且初始的方式,那么編譯器可以將上述對(duì)restar的操作進(jìn)行優(yōu)化:
restar[n]+=8;
而par并不是訪問(wèn)數(shù)組ar的唯一方式,因此并不能進(jìn)行下面的優(yōu)化:
par[n]+=8;
因?yàn)樵趐ar[n]+=3前,ar[n]*=2進(jìn)行了改變。使用了關(guān)鍵字restric,編譯器就可以放心地進(jìn)行優(yōu)化了。這個(gè)關(guān)鍵字據(jù)說(shuō)來(lái)源于古老的FORTRAN。有興趣的看看這個(gè)。
posted @ 2007-08-04 15:34 dennis 閱讀(9482) | 評(píng)論 (1) | 編輯 收藏
消息傳遞機(jī)制通俗地來(lái)講就是類(lèi)似于馬路上到處投遞小廣告的投遞者,它采取的是Send and Pray策略,既不關(guān)心消息是否能精確的傳送到真正需要消息的接收者,而是以廣播的方式把消息發(fā)送給所有人,然后通過(guò)回饋來(lái)確定消息接收者的類(lèi)型(引自《失蹤的鏈環(huán)》)。因此,動(dòng)態(tài)語(yǔ)言的duct typing是消息傳遞風(fēng)格,智能對(duì)象是消息傳遞風(fēng)格,顯然,Erlang的process間的通信機(jī)制同樣是消息傳遞風(fēng)格(Process之間完全通過(guò)send message來(lái)進(jìn)行控制和指示,不確定接收方是否具有處理消息的能力 ,異步的,接收的確認(rèn)也要等待reply)。
再來(lái)說(shuō)說(shuō)lambda算子理論,推薦下g9老大的lambda算子系列文章,這是開(kāi)篇《lambda算子簡(jiǎn)介1.a》,以及另外一篇《康托爾、哥德?tīng)枴D靈——永恒的金色對(duì)角線(rev#2)》。lambda算子理論是函數(shù)式編程的理論基礎(chǔ),通過(guò)9條公理就可以推到出一個(gè)圖靈完備的形式系統(tǒng),其中的Y combinator的推導(dǎo)簡(jiǎn)直是魔法(為了表示遞歸),再次領(lǐng)略了計(jì)算理論的魅力。另外,最近讀sicp第三章《模塊化、對(duì)象和狀態(tài)》,也理解了最初的面向?qū)ο笏枷雭?lái)自何處,在引入了內(nèi)部狀態(tài)模擬時(shí)間變化之后,對(duì)象的最初思想也產(chǎn)生了,同時(shí)也帶來(lái)了賦值導(dǎo)致的Side-Effect,而其實(shí)這正是動(dòng)態(tài)OO語(yǔ)言中的對(duì)象的理念,通過(guò)消息來(lái)決定對(duì)象的type(ducktyping)。可現(xiàn)代的靜態(tài)OO語(yǔ)言,在type和clas
之間畫(huà)上了等號(hào),java里面說(shuō)一切都是object,其實(shí)他想表達(dá)的卻是一切都是class,通過(guò)type以及函數(shù)簽名等來(lái)決定消息的分派(message dispatch),導(dǎo)致更多的代碼集中在消息分派,而不是真正的計(jì)算任務(wù)上,可以說(shuō)靜態(tài)OO已經(jīng)偏離原始的對(duì)象模型很遠(yuǎn)。
一點(diǎn)胡思亂想吧,我沒(méi)有科班經(jīng)歷,所有的東西都是自己在學(xué),在摸索,如有理論和常識(shí)上的謬誤,請(qǐng)不吝賜教。
posted @ 2007-08-03 09:23 dennis 閱讀(1266) | 評(píng)論 (0) | 編輯 收藏
(define rand
(let ((x 3))
(lambda(arg)
(cond((eq? arg 'generate)
((lambda()(set! x (rand-update x)) x)))
((eq? arg 'reset)
(lambda(init) (set! x init) (set! x (rand-update x)) x))
(else
error "Unkonown OP")))))
簡(jiǎn)單解釋下,當(dāng)參數(shù)是generate時(shí),直接調(diào)用匿名lambda函數(shù)(lambda()(set! x (rand-update x)) x) ,最后返回隨機(jī)值。而當(dāng)?shù)谝粋€(gè)參數(shù)是reset時(shí),返回匿名函數(shù)(lambda(init) (set! x init) (set! x (rand-update x)) x),這個(gè)匿名函數(shù)接受一個(gè)新的初始值,并賦值給x,然后調(diào)用rand-update
習(xí)題3.7,引入賦值的代價(jià)就是引入了副作用以及相應(yīng)的復(fù)雜性,3.3小節(jié)提出了命令式語(yǔ)言與函數(shù)式語(yǔ)言的基本差別。這一題,首先修改習(xí)題3.3(參見(jiàn)《sicp 3.1小結(jié)習(xí)題嘗試解答》),增加一個(gè)檢查密碼是否正確的功能,用以檢查輸入的原始帳戶密碼是否正確,make-account修改一下
;習(xí)題3.3
(define (make-account2 balance passwd)
(define (checkpwd pwd)
(eq? pwd passwd))
(define (withdraw amount)
(if (>= balance amount)
(begin (set! balance (- balance amount)) balance)
"余額不足"))
(define (deposit amount)
(set! balance (+ balance amount))
balance)
(define (dispatch pwd m)
(if (eq? pwd passwd)
(cond ((eq? m 'withdraw) withdraw)
((eq? m 'deposit) deposit)
((eq? m 'checkpwd) checkpwd)
(else
(error "Unknow request--MAKE-ACCOUNT" m)))
(lambda(x) "Incorrect password")))
dispatch)
那么,make-joint可以寫(xiě)為:
(define (make-joint account-name account-pass new-pass)
(if (account-name 'checkpwd account-pass)
(lambda (passwd m)
(if (eq? new-pass passwd)
(account-name account-pass m)
(error "Incorrect password")))
(error
"Incorrect password to the original account")))
首先是檢查原始帳戶的密碼是否正確,正確返回匿名函數(shù)(兩個(gè)參數(shù)passwd m),此匿名函數(shù)檢查密碼以及調(diào)用原始帳戶操作;如果不正確就提示消息。
測(cè)試一下:
> (define dennis-acc (make-account2 100 '123))
> (define zane-acc (make-joint dennis-acc '123 'abc))
> ((dennis-acc '123 'withdraw) 10)
90
> ((zane-acc 'abc 'withdraw) 10)
80
>
習(xí)題3.8,這一題比較簡(jiǎn)單了,在內(nèi)部維持一個(gè)狀態(tài)變量即可
(define f
(let ((y 1))
(lambda(x) (set! y (* x y)) y)))
測(cè)試可知,在(f 1) (f 0)執(zhí)行的順序不同時(shí),返回的值不同。
posted @ 2007-08-01 15:45 dennis 閱讀(405) | 評(píng)論 (0) | 編輯 收藏
plan.setCost(0.0);
else
plan.setCost(prj.getCost());
我們?cè)诤芏嗟胤接蓄?lèi)似的檢查對(duì)象是否為null,如果為null,需要一個(gè)默認(rèn)值等等這樣的場(chǎng)景。顯然,代碼重復(fù)是壞味道,怎么消除這個(gè)壞味道呢?答案就是使用NullObject替代之,Null Object繼承原對(duì)象。
public boolean isNull(){
return true;
}
}
class Project{
private double cost;
private String projectId;

public boolean isNull(){
return false;
}
}
那么,原來(lái)的代碼可以改寫(xiě)為:
plan.setCost(0.0);
else
plan.setCost(prj.getCost());
如果Null Object的引入僅僅是帶來(lái)這個(gè)好處,似乎沒(méi)有理由讓我們多敲這么多鍵盤(pán)。問(wèn)題的關(guān)鍵是類(lèi)似上面這樣的判斷也許出現(xiàn)在很多處,那么有價(jià)值的技巧出現(xiàn)了,我們?cè)贜ullObject覆寫(xiě)getCost,提供缺省值:
public boolean isNull(){
return true;
}
public double getCost(){
return 0.0;
}
}
請(qǐng)注意,只有那些大多數(shù)客戶端代碼都要求null object做出相同響應(yīng)時(shí),這樣的行為才有意義。比如我們這里當(dāng)工程id為null,很多地方要求費(fèi)用就默認(rèn)為0.0。 特殊的行為我們?nèi)匀皇褂胕sNull進(jìn)行判斷。
當(dāng)然,另外在需要返回NullObject的地方,你應(yīng)該創(chuàng)建一個(gè)null object以替代一般的對(duì)象,我們可以建立一個(gè)工廠方法:
private double cost;
private String projectId;


public boolean isNull(){
return false;
}
public Project createNullProject(){
return new NullProject();
}
}
Null Object模式帶來(lái)的好處:減少了檢查對(duì)象是否為null的代碼重復(fù),提高了代碼的可讀性,通常這些Null Object也可以為單元測(cè)試帶來(lái)簡(jiǎn)便。
posted @ 2007-07-31 17:48 dennis 閱讀(5187) | 評(píng)論 (7) | 編輯 收藏
還是回到比賽,比賽中我一個(gè)細(xì)節(jié)讓我印象深刻,伊拉克的一個(gè)后防隊(duì)員奮不顧身地用頭去解圍一個(gè)低空球,沙特的隊(duì)員的腳堪堪從他的耳朵旁大力射門(mén)劃過(guò),解說(shuō)員說(shuō)伊拉克人真是拼了,連受傷都不怕了,那時(shí)我想,對(duì)于伊拉克隊(duì)員們來(lái)說(shuō),死亡都不可怕,更何況是區(qū)區(qū)的受傷。他們是在用靈魂在戰(zhàn)斗,他們希望用勝利來(lái)告慰苦難中的祖國(guó)和逝去的生命,這已經(jīng)不是一場(chǎng)普通的球賽,這也不是一場(chǎng)戰(zhàn)爭(zhēng),讓我想到的詞語(yǔ)竟然是悲劇,一幕讓人激昂而又悲哀的悲劇。
posted @ 2007-07-30 13:10 dennis 閱讀(228) | 評(píng)論 (0) | 編輯 收藏