庄周梦蝶

生活、程序、未来
   :: 首页 ::  ::  :: 聚合  :: 管理

    今天费了一个下午安装了redhat9,并且安装了需要使用的jdk5、netbean、ruby和Erlang。做个备忘。

一。安装jdk5
1.到sun的网站上下载jdk5与netbean5.5的捆绑版本,注意要linux平台的

2.比如下载到/root/目录下,执行
chmod 755 jdk-1_5_0_12-nb-5_5_1-linux-ml.bin
然后执行:
./jdk-1_5_0_12-nb-5_5_1-linux-ml.bin
就会自动启动安装向导,一路选择确定下去就OK了。

3.设置环境变量,这时其实没有设置就可以启动netbean了,不过为了在终端执行,还是要设置下环境变量,使用vi编辑/etc/profile配置文件,在最后面加上:
JAVA_HOME=/opt/jdk1.5.0_12
PATH
=/opt/jdk1.5.0_12/bin:$PATH
CLASSPATH
=/opt/jdk1.5.0_12/lib/dt.jar:/opt/jdk1.5.0_12/lib/tools.jar
export JAVA_HOME PATH CLASSPATH
保存退出,reboot下就OK

二。安装ruby
1.到ruby-lang.org下载ruby-1.8.6.tar.gz
2.解压缩并进入解压后的目录:
tar xzvf ruby-1.8.6.tar.gz
cd ruby-1.8.6

3.默认是安装/usr/local目录下,可以通过下面的命令设置安装到/usr/local/ruby目录下:
/.configure -prefix=/usr/local/ruby

4.执行命令:make && make install

5.再次编辑vi /etc/profile,修改我们在上面提到的PATH,把ruby的bin加进去:
PATH=/usr/local/ruby/bin:/opt/jdk1.5.0_12/bin:$PATH

6.测试下是否正确安装,
ruby -version
ruby -e "puts 'hello'"

三、Erlang的安装

1.到Erlang.org下载最新版本的linux平台下的Erlang(源代码版本,需要自己编译),比如otp_src_R11B-5.tar.gz

2.解压缩,并进入解压后的目录:
tar zxvf otp_src_R11B-5.tar.gz
cd otp_src_R11B-5

3.build Erlang需要下列工具,确认你的linux版本有安装:
 GNU make

 GNU C compiler

 Perl 5

 OpenSSL

 Sun Java jdk-1.2.2

 X Windows

 sed  solaris平台需要

 Flex 可选

4.安装过程,顺序执行下列命令,包括OTP在内都将被安装
1)export LANG=#如果是C Shell,执行setenv LANG C,linux一般是Bourne shell



2)./configure

3)make

4)make install

5.确认安装正确,新开一个终端,执行erl进入Erlang shell

最后启动下ssh,允许防火墙通过ssh执行下面的命令,在windows上搞个putty
iptables -A INPUT -p tcp --sport 22 -j ACCEPT
iptables -A OUTPUT -p tcp --dport 22 -j ACCEPT




posted @ 2007-06-28 18:08 dennis 阅读(997) | 评论 (0)编辑 收藏

这一节的内容非常有趣,通过将序列作为interface,在此基础上进而提取出各种高阶操作(map,filter,accumulate,enumerate等),由此引出模块化设计的讨论。模块化设计带来复杂性的降低,同时可能引入性能上的损失,比如书中对sum-odd-squares过程的两种写法,原来的写法枚举列表元素的过程散落在累积、过滤、映射的过程中,主要一次循环就够了,而通过三个高阶过程来操作反而需要3次的遍历。

习题2.33,将map,append,length基本过程用累积操作重新定义,联系以往的实现通过观察和测试可以得出:
(define (map p sequence)
  (accumulate  (
lambda(x y) 
                (cons (p x) y))       
                       () sequence))
(define (append seq1 seq2)
  (accumulate cons seq2 seq1))
(define (length sequence)
  (accumulate (
lambda(x y)
                (
+ 1 y))
                0 sequence))
难点就在于累积的操作。

习题2.34,Horner规则求多项式,难点也是累积操作的定义:
(define (horner-eval x coefficient-sequence)
  (accumulate (
lambda(this-coeff higher-terms)
                (
+ this-coeff (* x higher-terms)))
              0 coefficient
-sequence))
只要记住lambda中的y其实是另一个递归的accumulate就比较容易完成了。
测试下:
> (horner-eval 2 (list 1 3 0 5 0 1))
 
79

习题2.35,利用map和accumulate重新定义count-leaves统计树的节点数目:
(define (count-leaves t)
  (accumulate 
+ 0 (map (lambda (x) (if (pair? x) (count-leaves x) 1)) t)))
map过程的参数op是过程
(lambda (x) (if (pair? x) (count-leaves x) 1))
当x是列表,递归调用count-leaves,否则返回个数1

习题2.36,列表的列表,因此map过程的第一个参数是一个过程作用于列表中的每个列表,当然是采用car将它们首项取出然后进行op操作,因此:
(define (accumulate-n op init seqs)
  (
if (null? (car seqs))
      ()
      (cons (accumulate op init (map car seqs))
            (accumulate
-n op init (map cdr seqs)))))

习题2.37,list作为Lisp的基本结构可以演化出各式各样的复杂结构,比如此题就将列表作为矢量,矢量通过组合成为矩阵,3个解答就是矩阵的运算:
(define (dot-product v w)
  (accumulate 
+ 0 (map * v w)))
(define (matrix
-*-vector m v)
  (map (
lambda (x) (dot-product x v)) m))
(define (transpose mat)
  (accumulate
-n cons () mat))
(define (matrix
-*-matrix m n)
  (let ((cols (transpose n)))
    (map (
lambda (x) (matrix-*-vector cols x)) m)))
知道矩阵运算的定义得出结果并不困难。

习题2.38,计算下结果:
> (fold-right / 1 (list 1 2 3))
1 1/2
;也就是3
/2

> (fold-left / 1 (list 1 2 3))
1/6
> (fold-right list () (list 1 2 3))
(
1 (2 (3 ())))
> (fold-left list () (list 1 2 3))
(((() 
123)

如果想使这两个过程的结果相同,op需要满足交换率和结合率的条件。

习题2.39:
;2.39
(define (reverse
-list sequence)
  (fold
-right (lambda(x y)(append y (list x))) () sequence))
(define (reverse
-list2 sequence)
  (fold
-left (lambda(x y) (cons y x)) () sequence))




posted @ 2007-06-27 15:14 dennis 阅读(466) | 评论 (3)编辑 收藏

    这个类在spring2.01前没有被改写,spring2.06似乎已经改写了,还未看源码。不过这不是我所在意的问题。我在《org.springframework.beans简单解读》中的对这个类的理解是不正确的。我们先看看Guillaume Poirier对这个类中为什么使用WeakHashMap的解释:

WeakHashMap is implemented with WeakReference for keys, and strong reference for values. That means if the value has a strong reference on the key, then the key cannot be garbage collected until the WeakHashMap is ready for collection. However, if the value has no strong reference on its key, then being in the WeakHashMap won't prevent the key and value from being garbage collected if it is otherwise ready. The WeakHashMap knows when to remove the key (and the value with it) by using the notification provided by the java.lang.ref package. For more information on this, see: http://java.sun.com/j2se/1.4.2/docs/api/java/lang/ref/package-summary.html

So the problem here with the CachedIntrospectionResults is that it uses BeanInfo and PropertyDescriptor that both have strong reference to the class (indirectly by a reference on Methods of the class). That will be solved with JDK 1.5 that uses a combinaison of Weak and Soft Reference to the Class and Method objects, but for 1.4.2, there's not really any better solution than to flush the Introspector's cache and/or use WeakReference on CachedIntrospectionResults. Using WeakReference on the CachedIntrospectionResults is safer, but decrease performance, and in such case a manual Instrospector.flushFromCaches(Class) must be used, so that the Instrospector does not keep a strong reference on the BeanInfo.

When a webapp is hot-redeployed, a new ClassLoader is created to load the webapp, and the old one is thrown away, expected to be garbage collected. For the collection to happen, the server must clear any strong reference to the ClassLoader or its classes, and also the webapp must make sure that any code in parent ClassLoaders (or siblings) clear any reference it might have to any of the webapp's class.

    照他的说法和参考《深入JVM》一书,对Class有强引用的有:ClassLoader,java.beans.BeanInfo,java.beans.PropertyDescriptor,java.lang.reflect.Method。因为在这个缓存中使用Class作为key,而Value是CachedIntrospectionResults,CachedIntrospectionResults中持有BeanInfo和Method的引用,这两个都对Class对象有强引用(这一点据说在jdk5中已经修改,被改成软引用和弱引用的组合,而在jdk1.4.2需要这样的处理),导致在web应用关闭或者热部署的时候,旧的ClassLoader和它引用的类不能被回收,因此使用弱引用包装CachedIntrospectionResults对象作为Value。web应用关闭或者热部署的时候,会new新的ClassLoader用于装载类,这就是CachedIntrospectionResults判断缓存是否safe的根据所在,判断要缓存的Class引用的ClassLoader是否相同。
    当使用JavaBean的内省时,使用Introspector,jdk会自动缓存内省的信息(BeanInfo),这一点可以理解,因为内省通过反射的代价是高昂的。当ClassLoader关闭的时候,Introspector的缓存持有BeanInfo的信息,而BeanInfo持有Class的强引用,这将导致ClassLoader和它引用的Class等对象不能被垃圾收集器回收,因此在关闭前,需要手工清除Introspector中的缓存,调用Introspector.flushFromCaches,这就是CachedIntrospectionResults中当得到BeanInfo后为什么要执行下面这段代码的原因:
            this.beanInfo = Introspector.getBeanInfo(clazz);

            
// Immediately remove class from Introspector cache, to allow for proper
            
// garbage collection on class loader shutdown - we cache it here anyway,
            
// in a GC-friendly manner. In contrast to CachedIntrospectionResults,
            
// Introspector does not use WeakReferences as values of its WeakHashMap!
            Class classToFlush = clazz;
            
do {
                Introspector.flushFromCaches(classToFlush);
                classToFlush 
= classToFlush.getSuperclass();
            }
            
while (classToFlush != null);

说到这里,spring中有一个比较少人注意的Listener——org.springframework.web.util.IntrospectorCleanupListener,这个类的说明如下:

它是一个在web应用关闭的时候,清除JavaBeans Introspector缓存的监听器.在web.xml中注册这个listener.可以保证在web 应用关闭的时候释放与掉这个web 应用相关的class loader 和由它加载的类
 
如果你使用了JavaBeans Introspector来分析应用中的类,系统级Introspector 缓冲中会保留这些类的hard引用。结果在你的web应用关闭的时候,这些类以及web 应用相关的class loader没有被垃圾收集器回收.
 
不幸的是,清除Introspector的唯一方式是刷新整个缓存。这是因为我们没法判断哪些是属于你的应用的引用.所以删除被缓冲的introspection会导致把这台server上的所有应用的introspection(内省)结果都删掉.
 
需要注意的是,spring容器托管的bean不需要使用这个监听器.因为spring它自己的introspection所使用的缓冲在分析完一个类之后会被马上从javaBeans Introspector缓冲中清除掉(上面提到的代码说明了这一点)

一般的应用基本不会直接用到JavaBean的内省方法,所以一般不用考虑遇到此类内省资源泄露,但是,很多的类库或者框架(比如struts,Quartz)没有清除Introspector。这个Listener就是为它们“擦屁股”的。请注意,这个监听器需要注册在web.xml中的所有应用监听器之前(比如ContentLoaderListener之前)
<listener>
   
<listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
</listener>

    参考资料:
 《深入Java虚拟机》
 《Class对象什么时候被回收?
 《Spring API Doc
  《ss目前的设计有引起内存泄露而导致down机的隐患
 以及一篇非常好的解释java引用类的文章《Java对象的强、软、弱和虚引用




posted @ 2007-06-26 10:39 dennis 阅读(2967) | 评论 (1)编辑 收藏

     摘要:     去了趟福州,事情没搞定,托给同学帮忙处理了,回家休息了两天就来上班了。回家这几天最大的收获是第四次重读《深入Java虚拟机》,以前不大明了的章节豁然开朗,有种开窍的感觉,水到渠成,看来技术的学习还是急不来。    闲话不提,继续Erlang的学习,上次学习到分布式编程的章节,剩下三章分别是错误处理、构造健壮的系统和杂项,错误处理和...  阅读全文

posted @ 2007-06-25 17:10 dennis 阅读(6098) | 评论 (0)编辑 收藏

    明天要回家一个星期了,好好休息下。今天找到别人翻译的Erlang编程手册,值的好好读一遍。
    所谓分布式的Erlang应用是运行在一系列Erlang节点组成的网络之上。这样的系统的性质与单一节点上的Erlang系统并没有什么不同。分布式这是个“大词”,Erlang从语言原生角度支持分布式编程,相比于java简单不少。
一、分布式机制
下列的BIFs是用于分布式编程:
spawn(Node, Mod, Func, Args)
启动远程节点的一个进程

spawn_link(Node, Mod, Func, Args)
启动远程节点的一个进程并创建连接到该进程

monitor_node(Node, Flag)
如果Flag是true,这个函数将使调用(该函数)的进程可以监控节点Node。如果节点已经舍弃或者并不存在,调用的进程将收到一个{nodedown,Node}的消息。如果Flag是false,监控将被关闭

node()
返回我们自己的进程name

nodes()
返回其他已知的节点name列表

node(Item)
返回原来Item的节点名称,Item可以是Pid,引用(reference)或者端口(port)

disconnect_node(Nodename)
从节点Nodename断开。

    节点是分布式Erlang的核心概念。在一个分布式Erlang应用中,术语(term)节点(node)意味着一个可以加入分布式transactions的运行系统。通过一个称为net kernal的特殊进程,一个独立的Erlang系统可以成为一个分布式Erlang系统的一部分。当net kernal进程启动的时候,我们称系统是alive的。

    与远程节点上的进程进行通信,与同一节点内的进程通信只有一点不同:
  
   {Name, Node} ! Mess.
显然,需要接收方增加一个参数Node用于指定接受进程所在的节点。节点的name一般是用@隔开的atom类型,比如pong@dennis,表示计算机名为dennis上的pong节点。通过执行:
erl -sname pong
将在执行的计算机中创建一个节点pong。为了运行下面的例子,你可能需要两台计算机,如果只有一台,只要同时开两个Erlang系统并以不同的节点名称运行也可以。

二、一些例子。
    这个例子完全来自上面提到的翻译的连接,关于分布式编程的章节。我增加了截图和说明。
首先是代码:
-module(tut17).

-export([start_ping/1, start_pong/0,  ping/2, pong/0]).

ping(
0, Pong_Node) ->
    {pong
, Pong_Node} ! finished,
    io
:format("ping finished~n", []);

ping(N
, Pong_Node) ->
    {pong
, Pong_Node} ! {ping, self()},
    receive
        pong 
->
            io
:format("Ping received pong~n", [])
    end
,
    ping(N 
- 1, Pong_Node).

pong() 
->
    receive
        finished 
->
            io
:format("Pong finished~n", []);
        {ping
, Ping_PID} ->
            io
:format("Pong received ping~n", []),
            Ping_PID 
! pong,
            pong()
    end
.

start_pong() 
->
    register(pong
, spawn(tut17, pong, [])).

start_ping(Pong_Node) 
->
    spawn(tut17
, ping, [3, Pong_Node]).

    代码是创建两个相互通信的进程,相互发送消息并通过io显示在屏幕上,本来是一个单一系统的例子,现在我们让两个进程运行在不同的两个节点上。注意start_ping方法,创建的进程调用ping方法,ping方法有两个参数,一个是发送消息的次数,一个就是远程节点的name了,也就是我们将要创建的进程pong的所在节点。start_pong创建一个调用函数pong的进程,并注册为名字pong(因此在ping方法中可以直接发送消息给pong)。
    我是在windows机器上测试,首先打开两个cmd窗口,并cd到Erlang的安装目录下的bin目录,比如C:\Program Files\erl5.5.3\bin,将上面的程序存为tut17.erl,并拷贝到同一个目录下。我们将创建两个节点,一个叫ping@dennis,一个叫pong@dennis,其中dennis是我的机器名。见下图:

采用同样的命令
erl -sname ping
创建ping节点。然后在pong节点下执行start_pong():


OK,这样就在节点pong上启动了pong进程,然后在ping节点调用start_ping,传入参数就是pong@dennis
tut17:start_ping(pong@dennis).
执行结果如下图:

同样在pong节点上也可以看到:


    结果如我们预期的那样,不同节点上的两个进程相互通信如此简单。我们给模块tut17增加一个方法,用于启动远程进程,也就是调用spawn(Node,Module,Func,Args)方法:
start(Ping_Node) ->
    register(pong
, spawn(tut17, pong, [])),
    spawn(Ping_Node
, tut17, ping, [3, node()]).
pong进程启动Ping_Node节点上的进程ping。具体结果不再给出。



    

posted @ 2007-06-15 17:33 dennis 阅读(5490) | 评论 (1)编辑 收藏

    Erlang中的process——进程是轻量级的,并且进程间无共享。查了很多资料,似乎没人说清楚轻量级进程算是什么概念,继续查找中。。。闲话不提,进入并发编程的世界。本文算是学习笔记,也可以说是《Concurrent Programming in ERLANG》第五张的简略翻译。
1.进程的创建
    进程是一种自包含的、分隔的计算单元,并与其他进程并发运行在系统中,在进程间并没有一个继承体系,当然,应用开发者可以设计这样一个继承体系。
    进程的创建使用如下语法:
Pid = spawn(Module, FunctionName, ArgumentList)

spawn接受三个参数:模块名,函数名以及参数列表,并返回一个代表创建的进程的标识符(Pid)。
如果在一个已知进程Pid1中执行:
Pid2 = spawn(Mod, Func, Args)
那么,Pid2仅仅能被Pid1可见,Erlang系统的安全性就构建在限制进程扩展的基础上。

2.进程间通信
    Erlang进程间的通信只能通过发送消息来实现,消息的发送使用!符号:
Pid ! Message
    其中Pid是接受消息的进程标记符,Message就是消息。接受方和消息可以是任何的有效的Erlang结构,只要他们的结果返回的是进程标记符和消息。
    消息的接受是使用receive关键字,语法如下:
receive
      Message1 [when Guard1] 
->
          Actions1 ;
      Message2 [when Guard2] 
->
          Actions2 ;

end

    每一个Erlang进程都有一个“邮箱”,所有发送到进程的消息都按照到达的顺序存储在“邮箱”里,上面所示的消息Message1,Message2,当它们与“邮箱”里的消息匹配,并且约束(Guard)通过,那么相应的ActionN将执行,并且receive返回的是ActionN的最后一条执行语句的结果。Erlang对“邮箱”里的消息匹配是有选择性的,只有匹配的消息将被触发相应的Action,而没有匹配的消息将仍然保留在“邮箱”里。这一机制保证了没有消息会阻塞其他消息的到达。
    消息到达的顺序并不决定消息的优先级,进程将轮流检查“邮箱”里的消息进行尝试匹配。消息的优先级别下文再讲。

    如何接受特定进程的消息呢?答案很简单,将发送方(sender)也附送在消息当中,接收方通过模式匹配决定是否接受,比如:
Pid ! {self(),abc}
给进程Pid发送消息{self(),abc},利用self过程得到发送方作为消息发送。然后接收方:
receive
  {Pid
1,Msg} ->

end
通过模式匹配决定只有Pid1进程发送的消息才接受。

3.一些例子
    仅说明下书中计数的进程例子,我添加了简单注释:
-module(counter).
-compile(export_all).
% start(),返回一个新进程,进程执行函数loop
start()
->spawn(counter, loop,[0]).
% 调用此操作递增计数
increment(Counter)
->
    Counter
!increament.
% 返回当前计数值
value(Counter)
->
    Counter
!{self(),value},
    receive
        {Counter
,Value}->
            
%返回给调用方
            Value
        end
.
  
%停止计数      
 stop(Counter)
->
     Counter
!{self(),stop}.
 loop(Val)
->
     receive
         
%接受不同的消息,决定返回结果
         increament
->
             loop(Val
+1);
         {From
,value}->
             From
!{self(),Val},
             loop(Val);
         stop
->
             true;
         
%不是以上3种消息,就继续等待
         Other
->
             loop(Val)
      end
.   
             
                        
        


调用方式:
1> Counter1=counter:start().
<0.30.0>
2> counter:value(Counter1).
0
3> counter:increment(Counter1).
increament
4> counter:value(Counter1).
1

基于进程的消息传递机制可以很容易地实现有限状态机(FSM),状态使用函数表示,而事件就是消息。具体不再展开

4.超时设置
    Erlang中的receive语法可以添加一个额外选项:timeout,类似:
receive
   Message1 [when Guard1] 
->
     Actions1 ;
   Message2 [when Guard2] 
->
     Actions2 ;
   

   after
      TimeOutExpr 
->
         ActionsT
end

after之后的TimeOutExpr表达式返回一个整数time(毫秒级别),时间的精确程度依赖于Erlang在操作系统或者硬件的实现。如果在time毫秒内,没有一个消息被选中,超时设置将生效,也就是ActionT将执行。time有两个特殊值:
1)infinity(无穷大),infinity是一个atom,指定了超时设置将永远不会被执行。
2) 0,超时如果设定为0意味着超时设置将立刻执行,但是系统将首先尝试当前“邮箱”里的消息。

    超时的常见几个应用,比如挂起当前进程多少毫秒:
sleep(Time->
  receive
    after 
Time ->
    true
end
.
    比如清空进程的“邮箱”,丢弃“邮箱”里的所有消息:
   
flush_buffer() ->
  receive
    AnyMessage 
->
      flush_buffer()
  after 
0 ->
    true
end
.
    将当前进程永远挂起:
  suspend() ->
    receive
    after
        infinity 
->
            true
    end
.
    超时也可以应用于实现定时器,比如下面这个例子,创建一个进程,这个进程将在设定时间后向自己发送消息:
-module(timer).
-export([timeout/2,cancel/1,timer/3]).
timeout(
Time, Alarm->
   spawn(timer
, timer, [self(),Time,Alarm]).
cancel(Timer) 
->
   Timer 
! {self(),cancel}.
timer(Pid
, Time, Alarm->
   receive
    {Pid
,cancel} ->
       true
   after 
Time ->
       Pid 
! Alarm
end
.

   
5、注册进程
    为了给进程发送消息,我们需要知道进程的Pid,但是在某些情况下:在一个很大系统里面有很多的全局servers,或者为了安全考虑需要隐藏进程Pid。为了达到可以发送消息给一个不知道Pid的进程的目的,我们提供了注册进程的办法,给进程们注册名字,这些名字必须是atom。
    基本的调用形式:
register(Name, Pid)
将Name与进程Pid联系起来

unregister(Name)
取消Name与相应进程的对应关系。

whereis(Name)
返回Name所关联的进程的Pid,如果没有进程与之关联,就返回atom
:undefined

registered()
返回当前注册的进程的名字列表

6.进程的优先级
设定进程的优先级可以使用BIFs:
process_flag(priority, Pri)

Pri可以是normal、low,默认都是normal
优先级高的进程将相对低的执行多一点。

7.进程组(process group)
    所有的ERLANG进程都有一个Pid与一个他们共有的称为Group Leader相关联,当一个新的进程被创建的时候将被加入同一个进程组。最初的系统进程的Group Leader就是它自身,因此它也是所有被创建进程及子进程的Group Leader。这就意味着Erlang的进程被组织为一棵Tree,其中的根节点就是第一个被创建的进程。下面的BIFs被用于操纵进程组:
group_leader()
返回执行进程的Group Leader的Pid
group_leader(Leader, Pid)
设置进程Pid的Group Leader为进程的Leader

8.Erlang的进程模型很容易去构建Client-Server的模型,书中有一节专门讨论了这一点,着重强调了接口的设计以及抽象层次的隔离问题,不翻译了。

posted @ 2007-06-14 17:12 dennis 阅读(7150) | 评论 (0)编辑 收藏

Erlang logo

Erlang前世今生


1982 - 1985

我们使用了 > 20种语言进行了电信行业的编程实验,结论是:这样的语言需要是一门高度的抽象的语言才能达到生产力目标。(给我们留下印象的有:List,Prolog,Parlog ...)

1985 - 86

我们使用Lisp,Prolog,Parlog等语言进行了实验,结论是:这样的语言需要原生支持的并发控制和容错处理,并且执行模型必须没有使用回溯。(排除了List和Prolog.) 而且它必须拥有并发粒度比如一个异步的电话进程可以用语言的一个进程表示(排除了Parlog)。最后我们不得不开发一门我们自己的语言,它拥有 Lisp,Prolog和Parlog的特性,但内置了并发和容错处理。

1987

第一次使用erlang进行实验。

1988

ACS/Dunder(项目)第一阶段:外来用户使用erlang进行PABX(专用自动交换分机)功能的原型构建, Erlang走出了实验室!

1989

 ACS/Dunder(项目)第二阶段:重新改造了完整的MD-110系统的1/10,结果:相比于使用PLEX的构建有>>10倍的效率提高!

1990

 Erlang正式以ISS'90标准发布,这带来不少的新用户,比如Bellcore。

1991

Erlang发布了更快的版本实现给用户,Erlang应用于电信'91(项目?),更多功能比如编译器、图形接口等。

1992

 更多的新用户,许多高速发展的项目。Erlang可以运行于VxWorks,PC,Macintosh等系统。有三个应用使用了ISS'92标准的Erlang。

1993

 分布式支持加进了Erlang,这使得erlang可以运行一个自发系统在不同的硬件上。决定向外部发布Erlang的实现,从爱立信分离出独立的部门开始维护和支持Erlang的实现和Erlang工具的开发工作。

posted @ 2007-06-14 09:28 dennis 阅读(2119) | 评论 (2)编辑 收藏

    读erlang.org上面的Erlang Course四天教程
1.数字类型,需要注意两点
1)B#Val表示以B进制存储的数字Val,比如
7> 2#101.
5
进制存储的101就是10进制的5了
2)$Char表示字符Char的ascii编码,比如$A表示65

2.比较难以翻译的概念——atom,可以理解成常量,它可以包含任何字符,以小写字母开头,如果不是以小写字母开头或者是字母之外的符号,需要用单引号包括起来,比如abc,'AB'

3.另一个概念——Tuple,有人翻译成元组,可以理解成定长数组,是Erlang的基础数据结构之一:
8> {1,2,3,4,5}.
{
1,2,3,4,5}
9> {a,b,c,1,2}.
{a
,b,c,1,2}
10> size({1,2,3,a,b,c}).
6
内置函数size求长度,元组可以嵌套元组或者其他结构。下面所讲的列表也一样。

4.另外一个基础数据结构就是各个语言都有的list(列表),在[]内以,隔开,可以动态改变大小,
    [123, xyz]
    [
123, def, abc]
    [{person
, 'Joe', 'Armstrong'},
        {person
, 'Robert', 'Virding'},
        {person
, 'Mike', 'Williams'}
    ]
可以使用内置函数length求列表大小。以""包含的ascii字母代表一个列表,里面的元素就是这些字母的ascii值,比如"abc"表示列表[97,98,99]。

5.通过这两个数据结构可以组合成各种复杂结构,与Lisp的cons、list演化出各种结构一样的奇妙,Erlang也可以当作是操作列表的语言。

6.Erlang中变量有两个特点:
1)变量必须以大写字母或者下划线开头,可以包含字母、下划线和@
2)变量只能绑定一次,也就是所谓的Single Assignment。或者以一般的说法就是只能赋值一次,其实Erlang并没有赋值这样的概念,=号也是用于验证匹配。

7.模式匹配——Pattern Matching,Erlang的模式匹配非常强大,看了buaawhl的《Erlang语法提要》的介绍,模式匹配的功能不仅仅在课程中介绍的数据结构的拆解,在程序的分派也扮演重要角色,或者说Erlang的控制的流转是通过模式匹配来实现的。具体功能参见链接,给出书中拆解列表的例子:
    [A,B|C] = [1,2,3,4,5,6,7]
        Succeeds 
- binds A = 1, B = 2,
        C 
= [3,4,5,6,7]
    
    [H
|T] = [1,2,3,4]
        Succeeds 
- binds H = 1, T = [2,3,4]
    
    [H
|T] = [abc]
        Succeeds 
- binds H = abc, T = []
    
    [H
|T] = []
        Fails

下面会给出更多模式匹配的例子,给出一个模块用来计算列表等

8.Erlang中函数的定义必须在一个模块内(Module),并且模块和函数的名称都必须是atom,函数的参数可以是任何的Erlang类型或者数据结构,函数要被调用需要从模块中导出,函数调用的形式类似:
moduleName:funcName(Arg1,Arg2,...).
写我们的第一个Erlang程序,人见人爱的Hello World:
-module(helloWorld).
-export([run/1]).
run(Name)
->
    io
:format("Hello World ~w~n",[Name]).
存为helloWorld.erl,在Erlang Shell中执行:
2> c(helloWorld).
{ok
,helloWorld}
3> helloWorld:run(dennis).
Hello World dennis
ok
打印出来了,现在解释下程序构造,
-module(helloWorld).
这一行声明了模块helloWorld,函数必须定义在模块内,并且模块名称必须与源文件名相同。
-export([run/1]).
而这一行声明导出的函数,run/1指的是有一个参数的run函数,因为Erlang允许定义同名的有不同参数的多个函数,通过指定/1来说明要导出的是哪个函数。
接下来就是函数定义了:
run(Name)->
    io
:format("Hello World ~w~n",[Name]).
大写开头的是变量Name,调用io模块的format方法输出,~w可以理解成占位符,将被实际Name取代,~n就是换行了。注意,函数定义完了要以句号.结束。然后执行c(helloWorld).编译源代码,执行:
helloWorld:run(dennis);

9.内置的常用函数:
    date()
    
time()
    
length([1,2,3,4,5])
    size({a
,b,c})
    atom_to_list(an_atom)
    list_to_tuple([
1,2,3,4])
    integer_to_list(
2234)
    tuple_to_list({})
    hd([1,2,3,4])  %输出1,也就是列表的head,类似Lisp的car
    tl([1,2,3,4])  %输出[2,3,4],也就是列表的tail,类似List的cdr

10.常见Shell命令:
1)h(). 用来打印最近的20条历史命令
2)b(). 查看所有绑定的变量
3) f(). 取消(遗忘)所有绑定的变量。
4) f(Val).  取消指定的绑定变量
5) e(n).   执行第n条历史命令
6) e(-1).  执行上一条shell命令

11.又一个不知道怎么翻译的概念——Guard。翻译成约束?呵呵。用于限制变量的类型和范围,比如:
     number(X)    - X 是数字
    integer(X)    
- X 是整数
    float(X)    
- X 是浮点数
    atom(X)        
- X 是一个atom
    tuple(X)    
- X 是一个元组
    list(X)        
- X 是一个列表
    
    
length(X) == 3    - X 是一个长度为3的列表
    size(X) 
== 2    - X 是一个长度为2的元组
    
    X 
> Y + Z    - X >Y+Z
    X 
== Y        - X 与Y相等
    X 
=:= Y        - X 全等于Y
    (比如: 
1 == 1.0 成功
               
1 =:= 1.0 失败)
为了方便比较,Erlang规定如下的比较顺序:
number < atom < reference < port < pid < tuple < list
其中pid就是process的id。

12.忘了介绍apply函数,这个函数对于熟悉javascript的人来说很亲切,javascript实现mixin就得靠它,它的调用方式如下:
apply(Mod, Func, Args),三个参数分别是模块、函数以及参数列表,比如调用我们的第一个Erlang程序:
apply(helloWorld,run,[dennis]).
13.if和case语句,if语句的结构如下:
if
Guard1 ->
Sequence1 ;
Guard2 ->
Sequence2 ;
...
end
而case语句的结构如下:
case Expr of
Pattern1 [when Guard1] 
-> Seq1;
Pattern2 [when Guard2] 
-> Seq2;

PatternN [when GuardN] 
-> SeqN
end


if和case语句都有一个问题,就是当没有模式匹配或者Grard都是false的时候会导致error,这个问题case可以增加一个类似java中default的:
case Fn of

   _ 
->
   true
end
通过_指代任意的Expr,返回true,而if可以这样:
if
  

  true 
->
   true
end
一样的道理。case语句另一个需要注意的问题就是变量范围,每个case分支中定义的变量都将默认导出case语句,也就是在case语句结束后可以被引用,因此一个规则就是每个case分支定义的变量应该一致,不然算是非法的,编译器会给出警告,比如:
f(X) ->
case g(X) of
true 
-> A = h(X), B = A + 7;
false 
-> B = 6
end
,
h(A)
.

如果执行true分支,变量A和变量B都被定义,而如果执行的false分支,只有变量B被定义,可在case语句执行后,h(A)调用了变量A,这是不安全的,因为变量A完全可能没有被定义,编译器将给出警告
variable 'A' unsafe in 'case' (line 10)



14.给出一些稍微复杂的模型匹配例子,比如用于计算数字列表的和、平均值、长度、查找某元素是否在列表中,我们把这个模块定义为list:
-module(list).
-export([average/1,sum/1,len/1,double/1,member/2]).
average(X)
->sum(X)/len(X).
sum([H
|T]) when number(H)->H+sum(T);
sum([])
->0.
len([_
|T])->1+len(T);
len([])
->0.
double([H
|T]) -> [2*H|double(T)];
double([]) 
-> [].
member(H
, [H|_]) -> true;
member(H
, [_|T]) -> member(H, T);
member(_
, []) -> false.
                

细细体会,利用递归来实现,比较有趣,这其实与Lisp中利用尾递归来实现迭代是一样的道理,[H|T]的形式类似Lisp中的car、cdr操作。_用于指代任意的变量,当我们只关注此处有变量,但并不关心变量的值的时候使用。用分号;来说明是同一个函数定义,只是不同的定义分支,通过模式匹配来决定调用哪个函数定义分支。
另一个例子,计算各种图形的面积,也是课程中给出的例子:

-module(mathStuff).
-export([factorial/1,area/1]).
factorial(
0)->1;
factorial(N) when N
>0->N*factorial(N-1).
%计算正方形面积,参数元组的第一个匹配square    
area({square
, Side}) ->
    Side 
* Side;
%计算圆的面积,匹配circle  
area({circle
, Radius}) ->
   
% almost :-)
   
3 * Radius * Radius;
%计算三角形的面积,利用海伦公式,匹配triangle 
area({triangle
, A, B, C}) ->
   S 
= (A + B + C)/2,
math
:sqrt(S*(S-A)*(S-B)*(S-C));
%其他
area(Other) 
->
   {invalid_object
, Other}.

执行一下看看:
1> c(mathStuff).
{ok
,mathStuff}
2> mathStuff:area({square,2}).
4
3> mathStuff:area({circle,2}).
12
4> mathStuff:area({triangle,2,3,4}).
2.90474
5> mathStuff:area({other,2,3,4}).
{invalid_object
,{other,2,3,4}}

Erlang使用%开始单行注释。

posted @ 2007-06-13 14:36 dennis 阅读(29159) | 评论 (4)编辑 收藏

看到天涯上的这个帖子:谁来救救我们的孩子?——400位父亲泣血呼救。我难以呼吸,我无法相信这残酷的现实。在21世纪的今天,竟然还有类似奴隶这样的现象出现,这就是我的祖国,这就是和谐社会。我帮不了那些孩子,我只好把这个帖子写在blog里,让更多人知道,让更多人思考。

posted @ 2007-06-12 11:40 dennis 阅读(688) | 评论 (1)编辑 收藏

习题2.17,直接利用list-ref和length过程
(define (last-pair items)
  (list (list
-ref items (- (length items) 1))))

习题2.18,采用迭代法
(define (reverse-list items)
  (define (reverse
-iter i k)
    (
if (null? i) k (reverse-iter (cdr i) (cons (car i) k))))
  (reverse
-iter items ()))
习题2.20,如果两个数的奇偶相同,那么他们的差模2等于0,根据这一点可以写出:
(define (same-parity a . b)
  (define (same
-parity-temp x y)
  (cond ((
null? y) y)
        ((
= (remainder (- (car y) x) 20)
         (cons (car y) (same
-parity-temp x (cdr y))))
        (
else
           (same
-parity-temp x (cdr y)))))
  (cons a (same
-parity-temp a b)))
利用了基本过程remainder取模

习题2.21,递归方式:
(define (square-list items)
  (
if (null? items)
      items 
      (cons (square (car items)) (square
-list (cdr items)))))
利用map过程:
(define (square-list items)
  (map square items))

习题2.23,这与ruby中的each是一样的意思,将操作应用于集合的每个元素:
(define (for-each proc items)
  (define (
for-each-temp proc temp items)
  (
if (null? items)
      #t
      (
for-each-temp proc (proc (car items)) (cdr items))))
  (
for-each-temp proc 0 items))
最后返回true

习题2.24,盒子图就不画了,麻烦,解释器输出:
Welcome to DrScheme, version 360.
Language: Standard (R5RS).
> (list 1 (list 2 (list 3 4)))
(
1 (2 (3 4)))
树形状应当是这样
               . 
/\
/ \
1 .
/\
/ \
2 .
/\
/ \
3 4
习题2.25,
第一个list可以表示为(list 1 3 (list 5 7) 9)
因此取7的操作应当是:
(car (cdr (car (cdr (cdr (list 1 3 (list 5 79))))))
第二个list表示为:(list (list 7))
因此取7操作为:
(car (car (list (list 7))))

第三个list可以表示为:
(list 1 (list 2 (list 3 (list 4 (list 5 (list 6 7))))))
因此取7的操作为:
(define x (list 1 (list 2 (list 3 (list 4 (list 5 (list 6 7)))))))
(car (cdr (car (cdr (car (cdr (car (cdr (car (cdr (car (cdr x))))))))))))
够恐怖!-_-

习题2.26,纯粹的动手题,就不说了
习题2.27,在reverse的基础上进行修改,同样采用迭代,比较难理解:

(define (deep-reverse x)
  (define (reverse
-iter rest result)
    (cond ((null? rest) result)
          ((
not (pair? (car rest)))
           (reverse
-iter (cdr rest)
                 (cons (car rest) result)))
          (
else
           (reverse
-iter (cdr rest)
                 (cons (deep
-reverse (car rest)) result)))
           ))
  (reverse
-iter x ()))

习题2.28,递归,利用append过程就容易了:
(define (finge x)
  (cond ((pair? x) (append (finge (car x)) (finge (cdr x))))
        ((null? x) ())
        (
else (list x))))

习题2.29,这一题很明显出来的二叉活动体也是个层次性的树状结构
1)很简单,利用car,cdr
(define (left-branch x)
  (car x))
(define (right
-branch x)
  (car (cdr x)))
(define (branch
-length b)
  (car b))
(define (branch
-structure b)
  (car (cdr b)))

2)首先需要一个过程用于求解分支的总重量:
(define (branch-weight branch)
  (let ((structure (branch
-structure branch)))
    (
if (not (pair? structure))
        structure
        (total
-weight structure))))
(define (total
-weight mobile)
  (
+ (branch-weight (left-branch mobile))
     (branch
-weight (right-branch mobile))))

利用这个过程写出balanced?过程:
(define (torque branch)
  (
* (branch-length branch) (branch-weight branch)))
(define (balanced? mobile)
  (
= (torque (left-branch mobile))
     (torque (right
-branch mobile))))

3)选择函数和定义函数提供了一层抽象屏蔽,其他函数都是建立在这两个基础上,因此需要改变的仅仅是selector函数:
(define (right-branch mobile) (cdr mobile))
(define (branch
-structure branch) (cdr branch))

习题2.30:
(define (square-tree tree)
  (cond ((null? tree) tree)
        ((
not (pair? tree)) (square tree))
        (
else
           (cons (square
-tree (car tree)) (square-tree (cdr tree))))))
(define (square
-tree2 tree)
  (map (
lambda(x)
         (
if (pair? x)
             (square
-tree x)
             (square x))) tree))

习题2.31,进一步抽象出map-tree,与map过程类似,将proc过程作用于树的每个节点:
(define (tree-map proc tree)
  (cond ((null? tree) tree)
        ((
not (pair? tree)) (proc tree))
        (
else
           (cons (tree
-map proc (car tree)) (tree-map proc (cdr tree))))))
(define (square
-tree3 tree)
  (tree
-map square tree))

习题2.32,通过观察,rest总是cdr后的子集,比如对于(list 1 2 3),连续cdr出来的是:
(2 3)
(3)
()
其他的5个子集应该是car结果与这些子集组合的结果,因此:
(define (subsets s)
  (
if (null? s)
      (list s)
      (let ((rest (subsets (cdr s))))
        (append rest (map (
lambda(x) (cons (car s) x)) rest)))))


posted @ 2007-06-12 09:55 dennis 阅读(1070) | 评论 (2)编辑 收藏

仅列出标题
共56页: First 上一页 38 39 40 41 42 43 44 45 46 下一页 Last