Archive for erlang

名字服务 功能:向服务器提交一个名字,然后服务器返回一个与这个名字关联的值。

-module(kvs).
-export([start/0,store/2,lookup/1]).

start() -> register(kvs,spawn(fun() -> loop() end)).
store(Key,Value) -> rpc({store,Key,Value}).
lookup(Key) -> rpc({lookup,Key}).

rpc(Q) ->
    kvs ! {self(),Q},
    receive
        {kvs,Reply} ->
            Reply
    end.
loop() ->
    receive
        {From,{store,Key,Value}} ->
            put(Key,{ok,Value}),
            From ! {kvs,true},
            loop();
        {From,{lookup,Key}} ->
            From ! {kvs,get(Key)},
            loop()
    end.
4> c(kvs.erl). {ok,kvs} 5> kvs:start(). true 6> kvs:store({location,joe},"Stockholm"). true 7> kvs:store(weather,raining). true 8> kvs:lookup(weather). {ok,raining} 10> kvs:lookup({location,joe}). {ok,"Stockholm"} 11> kvs:lookup({location,jane}). undefined 执行kvs:start(),将创建服务器进程,将接受消息并操作。匹配{From,{store,Key,Value}} 用于接收需要存储的键和值,存储后,给客户端发送保存成功的消息。 {From,{lookup,Key}}取得Key对应的值,并返回该值。 执行6句,将向服务进程发送key和value,第八句则是取值。 同一台机器上,客户端运行一个节点而服务端运行一个节点 需要打开两个终端。 启动一个名为s的erlang节点。并启动服务 D:\erlang5.9.1\code>erl -sname s@localhost (s@localhost)1> kvs:start(). true 启动名为a的erlang节点,然后使用erlang标准库模块prc来调用kvs模块中的函数。 D:\erlang5.9.1\code>erl -sname a@localhost (a@localhost)1> rpc:call(s@localhost,kvs,store,[weather,fine]). =PROGRESS REPORT==== 10-Aug-2012::11:53:03 === supervisor: {local,inet_gethost_native_sup} started: [{pid,<0.49.0>},{mfa,{inet_gethost_native,init,[[]]}}] =PROGRESS REPORT==== 10-Aug-2012::11:53:03 === supervisor: {local,kernel_safe_sup} started: [{pid,<0.48.0>}, {name,inet_gethost_native_sup}, {mfargs,{inet_gethost_native,start_link,[]}}, {restart_type,temporary}, {shutdown,1000}, {child_type,worker}] true (a@localhost)2> rpc:call(s@localhost,kvs,lookup,[weather]). {ok,fine} 在a节点验证weather的值 (s@localhost)2> kvs:lookup(weather). {ok,fine} 函数rpc:call(Node,Mod,Func,[Arg1,Arg2,...,])在Node上执行一个远程调用。被调用的函数是Mod:Func(Arg1,Arg2,...,).

Continue

1.带超时的receive。 防止消息不来,receive语句陷入无限等待中。   只有超时的receive 功能:让当前进程暂停Tms。

sleep(T) ->
    receive
    after T ->
        true
    end.
超时时间为0的receive 超时时间为0的语句会立即触发一个超时,但在此之前,会尝试对邮箱进行模式匹配。
flush_buffer() ->
    receive
        _Any ->
	    flush_buffer()
    after 0 ->
        true
    end.
程序分析: 触发超时之前,会先进行模式匹配,_Any匹配到,又会调用flush_buffer(),一直到清空进程邮箱中的所有消息。 没有超时子句,在邮箱为空情况下,flush_buffer()会永久暂停,不会返回。
priority_receive() ->
    receive
        {alarm,X} ->
	    {alarm,X}
    after 0 ->
        receive
	    Any ->
	        Any
	end
    end.
触发超时前,进行模式匹配时,如果没有消息与{alarm,X}匹配那么执行后面会接收邮箱中的第一个消息。如果没有消息,会在内层的receive上暂停,然后返回它受到的第一个消息。 只有在邮箱中的消息都进行过模式匹配后才会检查after段是否需要进行运算。 2.注册进程。 4个BIF管理注册进程。 register(AnAtom,Pid) AnAtom为原子。注册进程Pid为原子AnAtom。如果该原子被另一个进程注册,那么注册失败。 unregister(AnAtom)移除对应的注册信息。如果一个注册进程死亡,那么也会自动取消注册。 whereis(AnAtom) -> Pid | undefined 判断原子是否被注册,如果被注册,返回进程id,如果没有被注册,返回原子undefined. registered() -> [AnAtom::atom()]返回系统中所有已经注册的名称列表。      

Continue

erlang被称作纯粹的消息传递语言。在erlang中,和进程打交道只需要3个原语:spawn、 send和receive。 1.关于erlang并发编程的一个例子。 先上代码: [caption id="attachment_618" align="alignnone" width="300" caption="erlang并发编程"]erlang并发编程[/caption] 20> c(area_server1.erl). {ok,area_server1} 21> Pid3 = area_server1:start(). <0.98.0> 22> area_server1:area(Pid3,{rectangle,10,10}). 100 <0.40.0> 代码分析: 第四行,来自并发原语Pid = spawn(Fun),创建一个新进程,对Fun求值。新进程与调用者所在的进程并发运行。spawn返回一个Pid。 第八行,并发原语Pid ! Message,想进程标识符为Pid的进程发送消息。消息发送是异步的,发送者无需等待返回结果就可继续处理自己的事务。 receive ... end 接受一个发给当前进程的消息,进行匹配,如果没有匹配到,消息会留给后续过程来处理,然后进程等待下一条消息。 当输入命令Pid3 = area_server1:start(),创建一个服务端进程。进程执行loop()函数,等待接受消息。 area_server1:area(Pid3,{rectangle,10,10}).输入命令后,将创建一个新进程(客户端进程),到代码第九行,给Pid3发送一条消息,{该客户进程id,{rectangle,10,10}}。然后等待消息返回,等待消息中,匹配模式{Pid,Response}绑定了Pid(这里是Pid3),因为receive...end里只有个匹配模式,所以只有当接受的消息为Pid时,后面的式子才会执行。 给Pid3发送消息后,17-20执行,From为客户端进程id,然后18行像客户端进程发送消息。self()现在为服务端的id,也就是Pid3。然后继续等待消息。 客户端接受消息,11行进行匹配,发现Pid正确,执行后面语句,打印self()(客户端id)。 晚上用零碎时间又修改了一下代码,方便调试。ps:这个代码开始没看懂,借助群里大牛点通了。 [caption id="attachment_620" align="alignnone" width="300" caption="erlang编程2"]erlang编程2[/caption]

Continue

1.BIF: BIF:(built-in function)内建函数,是erlang语言的组成部分。是erlang虚拟机中的基本操作。 tuple_to_list/1将元组转换为列表,time/0返回当前时间的时,分,秒。 1> tuple_to_list({12,cat,"ddd"}). [12,cat,"ddd"] 3> time(). {12,35,57} 2.二进制数据: 一种数据类型,用来实现原始数据的高速存储。节省内存,输入输出更加高效。书写打印时,二进制数据以一个整数或者字符序列的形式出现,两端分别用尖括号括起来。其中的整数,每一个都要在0-255之间,如果二进制数据是可以打印的字符串,shell将显示字符串形式,否则会显示一串整数。 @spec 描述函数的参数和返回类型。类型标注,不是erlang代码而是注释文档的一部分,shell中不能使用这些标注。erlang中的模块声明也是注释的一部分。 erlang通过BIF来构造二进制数据或者从中提取数据,或者通过比特语法来完成这一过程。 @spec list_tbo_inary(IoList) -> binary() @spec split_binary(Bin,Pos) -> {Bin1,Bin2} @spec term_to_binary(Term) -> Bin @spec binary_to_term(Bin) -> Term list_tbo_inary将IoList中所有东西转换为一个二进制数据。split_binary在pos位置将二进制数据分割成两个部分。下面两个是互逆。 4> Bin1 = <<1,2,3>>. <<1,2,3>> 5> Bin2 = <<4,5>>. <<4,5>> 6> Bin3 = <<6>>. <<6>> 7> list_to_binary([Bin1,1,[2,3,Bin2],4|Bin3]). <<1,2,3,1,2,3,4,5,4,6>> 12> split_binary(<<1,2,3,1,2,3,4,5,4,6>>,4). {<<1,2,3,1>>,<<2,3,4,5,4,6>> 14> term_to_binary({11,'333a',use}). <<131,104,3,97,11,100,0,4,51,51,51,97,100,0,3,117,115,101>> 15> binary_to_term(<<131,104,3,97,11,100,0,4,51,51,51,97,100,0,3,117,115,101>>). {11,'333a',use} 返回二进制数据字节长度 16> size(<<1,2,3,4>>). 4 3.比特语法 比特语法:一种模式匹配语法,用于二进制数据中的比特进行封包和解包工作。 比特语法是模式匹配的一种扩展。编写底层代码时,常会需要对比特级别的二进制数据进行封包解包,会体现比特语法的便捷,比特语法针对协议编程而设计(erlang的看家本领 哇塞)。 16bit色彩的封包解包 19> Red = 2. 2 20> Green = 54. 54 21> Blue = 20. 20 22> Men = <<Red:5,Green:6,Blue:5>>. <<22,212>> 23> Mem = <<Red:5,Green:5,Blue:5>>. <<21,84:7>> 24> <<R1:5,G1:6,B1:5>> = Men. <<22,212>> 25> R1. 2 27> G1. 54 28> B1. 20 可以看到是用:进行匹配,冒号前是数据,后是所占的比特数。 比特语法表达式 嗯,这里讲比特语法格式: 比特语法的形式:<<>>或者<<E1,E2,E3,E4,...,En>>。Ei有四种形式: Ei = Value | Value:Size | Value/TypeSpecifierList | Value:Size/TypeSpecifierList 二进制数据中总比特数恰好被8整除(二进制数据中每个字节都是8bit)。Value必须是一个绑定变量、文本串或者一个返回值的整数。浮点数、二进制数据的表达式。Size必须为一个整型或者整型绑定变量,不能是自由变量。整型默认Size为8,浮点型为64,二进制则为本身长度。SpecifierList决定字节序,取值为: @type End = big| little |native 书上给出一个例子来了解这三种排序和默认排序,不同机器可能不同。 37> {<<16#12345678:32/big>>,<<16#12345678:32/little>>,<<16#12345678:32/native>>,<<16#12345678:32>>}. {<<18,52,86,120>>, <<120,86,52,18>>, <<120,86,52,18>>, <<18,52,86,120>>} 4.使用总结 块表达式: begin Expr1, .... Exprn end 块得值就是快中最后一个表达式的值,用于当代码某处只允许使用单个表达式而你要用一串表达式时。 注释: 只有行注释%,没有块注释。 列表操作符++ ——:对列表进行添加和删除的中缀操作符。 比较表达式: 所有类型都定义了大小比较顺序: number<atom<reference<fun<port<pid<tuple<list<binary 作用:可以对存储了任何类型的列表进行排序,并根据比较顺序,编写高效的数据访问代码。 出了=:=,=/=外,其他都遵循下面规则: 如果一个比较参数为整数,另一个浮点数, 整数在比较前需要转换成浮点数。 如果两个比较参数都是整数或者浮点数,直接比较。。。 ==只适用于浮点数和整数的比较。最好都用=:=。   下划线变量: 如果一个变量在一个字句中只被使用一次,编译器会提出警告。但以下划线开始,那么编译器不会产生警告信息。 命名不准备使用的变量,增加可读性。方便调试。

Continue

  1.列表解析。 1> L=[1,2,3,4] . [1,2,3,4] 4> lists:map(fun(X) -> 2*X end,L). [2,4,6,8] 5> [2*X || X <- L]. [2,4,6,8] [F(X) || X <- L] 代表由F(X)组成的列表,X取值于列表L。||右边用于匹配列表L中元素的模式,左边是一个构造器。 右边模式匹配可以像过滤器一样操作,注意大小写的区别。 6> [X || {a,X} <- [{a,1},{b,2},{c,3},{a,4},hello,"wow"]]. [1,4] 7> [X || {A,X} <- [{a,1},{b,2},{c,3},{a,4},hello,"wow"]]. [1,2,3,4] 2.列表解析快速排序算法。  

-module(lib_misc).
-export([qsort/1]).

qsort([]) -> [];
qsort([Pivot|T]) ->
qsort([X || X <- T, X < Pivot])
++ [Pivot] ++
qsort([X || X <- T,X >= Pivot]).
++中缀添加操作符 19> [1]++[2]++[3,4]. [1,2,3,4] 3.毕达哥拉斯三元组。  
pythag(N) ->
    [{A,B,C} ||
        A <- lists:seq(1,N),
        B <- lists:seq(1,N),
        C <- lists:seq(1,N),
        A+B+C =< N,
        A*A+B*B =:= C*C
    ].
lists:seq(1,N)返回一个1到N组成的列表。 4.断言。 功能:断言是一种用于强化模式匹配功能的结构。 断言序列: 用分号(;)分开的集合,只要有一个True,整个序列就为True。 用逗号(,)分开的集合,必须都为true,整个断言序列才为true。 断言序列的合法语法形式:(表格,p43) 原子true,其他常量(条件,或者绑定变量)求值为false, 断言谓词或BIF(内建函数),比较表达式,算术表达式,布尔表达式,短路布尔表达式 布尔表达式中,orelse,andalso与 or/and 区别:and/or 需要对参数都进行求值。

Continue

妙不可言的函数编程。为什么这么说呢,当我看完第三章时,被erlang的编程方式深深的吸引住了,但是作者总是在我觉得很精妙的地方说:“该方法在实际编程中很少用到”T_T。python也有函数编程的思想,不过一直没有用到过,可能还停留在初级阶段。下面的笔记也只是自己学习中的所悟,可能有着大量的错误,如果你发现我理解的不对,感谢留言指正。请带着怀疑的态度看此笔记。 1、模块。 模块文件存放在扩展名为.erl的文件中,编译完成后的扩展名为.beam。 文件名geometry.erl(博客不支持erlang语法高亮,蛋疼了。。) -module(geometry). -export([area/1]). area({rectangle,Width,Ht}) -> Width * Ht; area({circle,R}) -> 3.14 * R * R 执行过程:先需要将erlang shell目录移动到代码目录下,查看当前目录命令pwd(),cd(Dir)。 2> c(geometry). {ok,geometry} 6> geometry:area({rectangle,10,5}). 50 7> geometry:area({circle,1.4}). 6.1544 字句顺序不重要,但调用参数的匹配过程是从上向下的。函数不能处理模式匹配失败的情况,将会抛出一个运行时错误。 2、fun匿名函数 1> Z = fun(X) -> 2*X end. #Fun<erl_eval.6.82930912> 2> Z(3). 6 这个简单的例子介绍fun函数的用法,用一个"end"做结尾。第三行语句的值为一个函数Z(X)=2*X 以fun为参数的函数 3> Even = fun(X) -> (X rem 2) =:= 0 end. #Fun<erl_eval.6.82930912> 4> Even(8). true 5> Even(3). false 6> lists:map(Even,[1,2,3,4,5,6,7,8]). [false,true,false,true,false,true,false,true] 7> lists:filter(Even,[1,2,3,4,5,6,7,8]). [2,4,6,8] 第三个语句,我们定义了一个Even函数,用来求变量是否是偶数,是返回true。 第四第五语句是函数的使用。 第六行将函数Even作为参数,将列表中的每一个元素代入Even,得到的值生成一个新的列表。lists:filter是将满足使Even值为true的列表元素生成一个新列表。 返回fun的函数 11> Fruit = [apple,pear,orange]. [apple,pear,orange] 12> MakeTest = fun(L) -> (fun(X) -> lists:member(X,L)end) end. #Fun<erl_eval.6.82930912> 13> IsFruit = MakeTest(Fruit). #Fun<erl_eval.6.82930912> 14> IsFruit(pear). true 15> IsFruit(dog). false 17> BoxList = [dog,orange,cat,apple,bear]. [dog,orange,cat,apple,bear] 18> lists:filter(IsFruit,BoxList). [orange,apple] 11行定义了一个Fruit列表,里面都是水果(⊙﹏⊙b汗)。十二行定义了一个函数MakeTest(L),他的值为一个fun函数,这个fun函数的功能是看变量X是否是L中的元素,是返回True。(感觉就是二元一次方程,你必须知道两个变量才能求得一个结果值) 第13行定义函数IsFruit(X)=lists:member(X,Fruit),当然我这样表示是不正确的,只是为了写得像数学上的函数。正确是IsFruit = fun(X) -> lists:member(X,Fruit) end,这样这个函数的功能变成:看X是否是水果,是返回true。 第14,15行进行了函数测试。十七行定义了一个篮子,里面各种东西,我们要挑出水果,用我们的lists标准库,如果IsFruit为true,那么是水果,则挑拣到另一个篮子里。哇哈哈~ 定义自己的抽象流程控制: 这里更是可以举一反三,做你想做的^_^。 书上给了一个for循环控制结构的实现:(语法不支持高亮,就用截图了,不高亮我自己看着别扭) [caption id="attachment_578" align="alignnone" width="300" caption="erlang编程"]erlang编程[/caption] 这是实现了for(i=0,i<10,i++),for i in range(0,10),简单的你可以实现一个for i in range(0,10,2)。 解释一下程序:如果第一个参数值与第二个相同,返回执行 F(Max),否则匹配第二个,F(I)作为列表的头,for(I+1,Max,F)作为列表尾。 如果看到这里,你也会有疑问,为什么不写第二条语句在第一条上面呢?因为交换以后,for(I,Max,F)将满足任何语句,就不会执行第二条语句。编译器也会提示给你:lib_misc.erl:6: Warning: this clause cannot match because a previous clause at line 5 always matches。你的执行将会死循环。当然如果你输入的匹配I>Max,也会进入死循环。 我感觉这将比其他语言的for循环更加灵活。 25> c(lib_misc). {ok,lib_misc} 26> lib_misc:for(1,10,fun(I) -> I end). [1,2,3,4,5,6,7,8,9,10] 27> lib_misc:for(1,10,fun(I) -> I*I end). [1,4,9,16,25,36,49,64,81,100] 28> 编译,26行打印1-10整数列表,27行打印平方列表。   先整理这么多,第二章的笔记是边学边记,第三章是一口气给看完了。⊙﹏⊙b汗

Continue

The Erlang plugin for Vim,地址:https://github.com/onlychoice/vimerl/ windows下将文件复制到“安装目录/Vim/vimfiles”里面就行了。 安装完直接带有自动补全功能, Ctrl+c,Ctrl+o,应该是正确的。 这里找到一个管理vim插件的工具:pathogen。 地址:http://www.vim.org/scripts/script.php?script_id=2332 这个工具可以很方便的管理插件的安装和卸载,windows下在\vimfiles里建立一个 bundle 目录, 以后插件可以以整个文件夹的形式存放在里面。 方便安装删除。 下载的pathogen.vim文件放到Vim\vimfiles\autoload里,linux下安装到/.vim/。 配置pathogen: 修改~/.vimrc配置文件,加入: List代码 call pathogen#infect() syntax on filetype plugin indent on vim字体的修改方法参考文章:http://www.vimer.cn/2009/11/ vim%EF%BC%88gvim%EF%BC%89%E7%BC%96%E7%A8%8B%E5%AD%97%E4%BD%93%E6%8E%A8%E8%8D%90.html

Continue

  在shell下学习erlang的语法 1.输入十六进制: 1> 16#f. 15 输入命令需要在末尾加上一个点号,否则会认为没有输入完整。(第一次安装完成,就迫不及待的在命令行下试了,没有成功,慌了。仔细阅读文档才发现输入不完整。) 2.erlang变量名大写: 13> i=9. ** exception error: no match of right hand side value 9 14> I=9. 9 3.erlang变量的单一赋值: 当执行I=9时,值9就被绑定到I上。没绑定之前是一个自由变量,绑定后变量的值将不会再发生改变。 “=”模式匹配操作符。 17> I=5. ** exception error: no match of right hand side value 5 “Erlang 是一个函数式语言,不存在可变状态。 当多核编程来临的时候,采用不可 变状态所带来的好处是难以估量的。 如果你用 C、Java 这样的传统编程语言为多核 CPU 编写程序,就不得不应付共享内存带来的问题。要想不破坏共享内存,就必须在访问时对其加锁。程序还要保证在操纵共享内存时不会崩溃。  而Erlang没有可变状态,也就没有共享内存,更没有锁,这一切都有利于并行化程序的编写。” “定义一个变量的词法单元就是这个变量的作用域。因此,如果在一个函数语句范围 内使用 X,那么 X 的值就不能―跳出‖语句之外。在同一个函数的不同子句中,彼此之间也不存在全 局或者共享的私有变量。如果 X 出现在许多个不同的函数当中,那么这些 X 的值也都是各自独立 的。”这个在看完函数后再进行验证。 4.浮点数运算: 18> 5/3. 1.6666666666666667 19> 4/2. 2.0 20> 5 div 3. 1 21> 10 rem 3. 1 22> 4 div 2. 2 23> 4 div 2.0. ** exception error: bad argument in an arithmetic expression      in operator  div/2         called as 4 div 2.0 24> 4.0 div 2 24> . ** exception error: bad argument in an arithmetic expression      in operator  div/2         called as 4.0 div 2 25> 5/3.0. 1.6666666666666667 经过测试,div运算两边必须是整数。rem取余。 5.erlang原子: 原子是一串以小写字母开头,后跟数字字母或下划线(_)或邮件符号(@)的字符,使用单引号引起来的也是原子。 原子的值就是他自身。 做了几个测试: 37> aaa. aaa 38> 'aaa'. aaa 39> "aaa". "aaa" 40> 111. 111 41> '1111'. '1111' 42> 'a111'. a111 43> ''. '' 44> '. 44> ' 44> . '.\n' 45> a'111'. * 1: syntax error before: '111' 45> +. * 2: syntax error before: '.' 45> '+'. '+' 46> AAA. * 1: variable 'AAA' is unbound 47> 'AAA' . 'AAA' '单引号是个特殊字符,用来区分原子,"双引号是定义字符串,在后面讲。符合定义的,单引号加与没加一样,不符合的(如:大写字母开头或者数字,或者特殊字符),需要加上单引号作为区分。  6.元组: 将若干个以逗号分割的值用一对花括号括起来,就形成了一个元组。值可以是数值和原子。 从元组获取值: 48> D = {a,5,b} . {a,5,b} 49> {a,C,B} = D. {a,5,b} 50> C. 5 51> B. b 书上的一个例子: 55> Person={person,{name,{first,joe},{last,armstrong}},{footsize,42}} 55> . {person,{name,{first,joe},{last,armstrong}},{footsize,42}} 56> {_,{_,{_,Who},_},_}=Person. {person,{name,{first,joe},{last,armstrong}},{footsize,42}} 57> Who. joe 58> 注意    在前面的样例中,将_作为占位符,表示那些我们不关心的变量。符号_称为匿名变量,与常规变量不同,在同一个模式中的不同地方,各个_所绑定的值不必相同。 7.erlang列表: 用列表存储可变的东西,将若干个以逗号分割的值用一对方括号括起来,就形成了一个列表。列表的第一个元素称为列表的头(head)。除去头,剩下的东西就称为列表的尾(tail)。 例如,如果有列表[1,2,3,4,5],那么列表的头就是整数 1,它的尾为[2,3,4,5]。注意,列表的头可以是任何东西,但是列表的尾通常还是一个列表。 访问列表的头是一个非常高效的操作,因此,实际上所有的列表处理函数都是从提取列表头开始的,先对头进行处理,然后继续处理列表的尾。 erlang列表定义:如果T 是一个列表,那么[H|T]也是一个列表 ,这个列表以H 为头,以T为尾。竖线符号(|)可以将列表的头和尾分隔开来,而[]则是空列表。 58> H = [1,a,{a,b,c}]. [1,a,{a,b,c}] 59> T = [1,2,3]. [1,2,3] 60> [H|T]. [[1,a,{a,b,c}],1,2,3] 提取列表中的值: 62> [Aa|Bb]=H. [1,a,{a,b,c}] 63> Aa. 1 64> Bb. [a,{a,b,c}] 8.erlang字符串: 严格来讲,erlang没有字符串,字符串就是一个整数列表,列表中的每一个值都是字符所对应的整数值。 shell打印一串列表值时,只有列表中的所有整数都是可打印字符,它才把这个列表当作字符串来打印: 66> [3,4,5]. [3,4,5] 67> [83,84,85]. "STU" 68> [3,83,84]. [3,83,84] 69> [$a,$b,$c].    "abc" 我们无须死记硬背哪一个整数表示哪一个特定的字符(ASCII码表),可以使用$符号来表示字符的整数值。例如,$a实际上是一个整数,表示字符 a。  命令f()会让shell释放它所绑定过的所有变量。执行这个命令后,所有的变量都变成自由变量。 72> X=333. 333 73> X=111. ** exception error: no match of right hand side value 111 74> f(). ok 75> X=111. 111 终于将第二章看完了,有种眼前一亮的感觉。

Continue

Erlang程序设计。pdf 这几天突然又迷茫了,再看c和c++的一些注意、要点。看了本《python网络编程》,与自己想学的东西差太多,看到后面就开始蹦跶着看了。 在网上看了一篇文章,决定小小的研究一下erlang,说不定以后用得着。 找了几篇文章 http://zhys9.com/blog/?tag=erlang http://jbpm.group.iteye.com/group/topic/7938 几本erlang相关的pdf书,从sina的共享文档找的,论坛不上,qq群加了。 开始搞erlang

Continue