如何看待Erlangmatlab之父 编程实践Joe Armstrong觉得OO编程很烂

&&2960 阅读
本次图灵访谈,邀请到了Erlang最初的设计者和实现者Joe Armstrong,他也是Erlang OTP系统项目的首席架构师。他拥有瑞典皇家理工学院博士学位,是容错系统开发领域的世界级专家。此外,他还在开发旨在替代XML的标记语言ML9。现任职于爱立信公司。
Joe Armstrong最广为人知的是他发明了Erlang编程语言,并且创建了用于构建Erlang应用程序的框架——开放电信平台(Open Telecom Platform,OTP)。在现代语言的版图中,Erlang有点另类。同很多流行的语言相比,它既老又新。早在1986年,也就是Perl出现的前一年,Armstrong就已经开始了Erlang的工作,当时它只作为商用产品出售并且主要在爱立信公司内部使用。这种情况直到1998年Erlang作为开源项目发布后才发生变化,那时Java和Ruby已经问世3年了。Erlang并非起源于Algol系列中的某个成员,而是源于逻辑编程语言Prolog。当时Erlang设计初衷也相当明确,它针对的软件是类似于电话交换机那样的高可用性、高可靠性系统。
但是几乎在不经意间,Erlang适合于构建电话交换机的那些特征也让它非常适合于编写并发软件,当程序员们开始努力应对多核系统未来的发展趋势时,并发性引起了他们的注意。
Armstrong本人也有点另类。他起初是一名物理工作者,在攻读物理学博士学位时因为用完积蓄而转向了计算机科学,找到一份研究员的工作,为英国人工智能领域奠基人之一的Donald Michie工作。在Michie的实验室,Armstrong接触了人工智能领域各个方面的杰作,成为英国机器人学会的创始成员并撰写了一些有关机器人视觉的论文。
由于Lighthill所做的那份非常有名的调查报告 ,人工智能的资金来源枯竭,Armstrong又回到了物理学领域,从事了5年多与物理学编程相关的工作。开始时他在欧洲非相干散射科学协会(EISCAT)工作,后来又到了瑞典空间研究中心,最后加入了爱立信计算机科学实验室,Erlang就是在那里发明的。
最终入选问题的提问者,将获得图灵社区送出的图灵电子书一本。
往期活动回顾:
,问题入选的获奖者有:cage
,问题入选的获奖者有:zzNucker,大胖,颜海镜,2gua,旁观者,大熊兔
,问题入选的获奖者有:lt,putin123456,邓国平,2gua
,问题入选的获奖者有:Ada_liu_1987,2gua
更多精彩,加入图灵访谈微信!
《码农》电子刊执行主编,图灵访谈记者
在多核、并发、分布为王的时代,谁将成为下一个主流编程语言?来自全世界的众多专家都认为,Erlang最有可能在竞...分析Erlang虚拟机启动过程
分析Erlang虚拟机启动过程
  Erlang虚拟机的启动
  erl实际上是一个shell脚本,设置几个环境变量之后,调用执行erlexec。erlexec的入口点在 otp_src_R15B01/erts/etc/common/erlexec.c 文件。erlexec的main函数首先分析erl传入的参数和环境变量,选择正确版本的beam可执行文件,然后将传入的参数整理好,加入一些默认参数,最后通过系统调用execv运行beam虚拟机。例如在smp环境中,运行的就是 beam.smp 版本的虚拟机。因此,erl和erlexec都是加载器,最终执行的Erlang虚拟机进程是名字为beam系列的进程。
  beam进程的入口点在&otp_src_R15B01/erts/emulator/sys/unix/erl_main.c 。在这个文件中,main函数只有一行:
  erl_start(argc,&argv);&
  通过erl_start这个函数真正进入Erlang虚拟机的世界了。erl_start函数位于&otp_src_R15B01/erts/emulator/beam/erl_init.c 文件,Erlang虚拟机初始化相关的代码基本上都在这个文件中。这个函数大约600行,但是结构简单,大部分代码都是在处理参数。把erl_start的主干理出来,就是这样的:
  void&erl_start(int&argc,&char&**argv)&  &{&  &&&&&early_init(&argc,&argv);&  &&&&&处理各种参数&  &&&&&设置信号处理函数&  &&&&&erl_init(ncpu);&  &&&&&init_shared_memory(boot_argc,&boot_argv);&  &&&&&load_preloaded();&  &&&&&erts_initialized&=&1;&  &&&&&erl_first_process_otp(&otp_ring0&,&NULL,&0,&boot_argc,&boot_argv);&  &&&&&erts_start_schedulers();&  &&&&&erts_sys_main_thread();&&  &}&
  early_init()函数进行一些非常底层的初始化工作。erl_init()处理一些和Erlang虚拟机本身的初始化操作,例如各种数据结构的初始化。init_shared_memory()进行一些和内存回收相关的初始化。
  load_preloaded()函数将需要预加载的Erlang模块加载至虚拟机。需要预加载的模块都在&otp_src_R15B01/erts/preloaded/ebin 目录下。由于在build Erlang/OTP的时候,本地应该还没有Erlang编译器,所以这个目录下提供的都是编译好的.beam文件。这些模块的源码位于otp_src_R15B01/erts/preloaded/src 目录。预加载模块在build的时候由工具程序 make_preload 生成C语言文件硬编码在虚拟机中了。如果想要修改预加载的文件,例如在里面加上 erlang:display() 表达式打印调试信息,可以修改src中的文件,然后通过编译器erlc生成.beam文件保存在&otp_src_R15B01/erts/preloaded/ebin目录下覆盖原来的文件,再build即可。
  在预加载的文件夹中可以看到,预加载的有以下模块:
  erl_prim_loader:主要加载器,负责所有模块的加载
  erlang:对虚拟机提供的一些BIF的接口
  init:init进程的代码
  otp_ring0:Erlang虚拟机中第一个进程的代码,启动init
  prim_file:文件操作接口
  prim_inet:网络操作接口
  prim_zip:压缩文件操作接口
  zlib:zlib库
  把这些必要模块都加载至虚拟机之后,通过erl_first_process_otp()函数创建了Erlang虚拟机上的第一个进程,调用&otp_ring0 模块中的start/2函数。start/2 函数运行init模块的 boot/1 函数,之后开始Erlang/OTP系统的引导过程。这里先把虚拟机的启动过程分析完再讲述Erlang/OTP的引导过程。
  创建了第一个进程之后,进程还不能运行,因为还没有创建调度器。erts_start_schedulers()根据CPU的核心数和用户通过参数设置的数值启动某个数目的调度器线程。每一个调度器都在一个线程中运行。调度器挑选要执行的进程,然后执行进程,当进程的reds用完或进程等待IO挂起的时候再挑选另一个进程执行。以后再撰文详细分析Erlang调度器的工作原理。运行了erts_start_schedulers()函数之后Erlang虚拟机才真正运转起来。
  启动调度器之后,调用erts_sys_main_thread()函数,也就是说beam进程的主线程进入了erts_sys_main_thread()函数。下面简单分析一下erts_sys_main_thread()函数。
  void&erts_sys_main_thread(void)&  &{&  &&&&&erts_thread_disable_fpe();&  &&&&&smp_sig_notify(0);&/*&Notify&initialized&*/  &&&&&while&(1)&{&  &&&&&&&&&/*&Wait&for&a&signal&to&arrive...&*/  &&&&&&&&&select(0,&NULL,&NULL,&NULL,&NULL);&  &&&&&}&  &}&
  这个函数很简单,屏蔽浮点数异常、通知信号处理线程已经完成了初始化,然后进入一个死循环等待信号。这个select调用表示永远等待文件IO操作,但是什么文件也不等,只是把线程挂起。但是这个函数在收到信号的时候会返回。这里顺便提一下Erlang虚拟机中的信号处理。在之前初始化的时候,设置了信号处理函数,也就是通过函数&init_break_handler() 设置了一些信号的处理函数。这些信号处理函数收到了信号之后实际上将信号通过管道转发给了一个专门处理信号的线程,之前在调用 early_init() 的时候创建了这个线程,这个信号处理线程运行的函数是&signal_dispatcher_thread_func(),这个函数是一个死循环,等待从管道中读取值。虚拟机的主线程通过&smp_sig_notify() 函数将通知消息写入管道发给信号处理线程。
  从Erlang虚拟机处理信号的方式可以看出,这种处理方式也是Erlang提倡的进程间通信方式。
  下面分析otp_ring0的start/2调用init的boot/1引导Erlang/OTP系统的过程。
  init进程的引导过程
  init:boot/1的代码如下:
  boot(BootArgs)&-&&  &&&&&register(init,&self()),&  &&&&&process_flag(trap_exit,&true),&  &&&&&start_on_load_handler_process(),&  &&&&&{Start0,Flags,Args}&=&parse_boot_args(BootArgs),&  &&&&&Start&=&map(fun&prepare_run_args/1,&Start0),&  &&&&&Flags0&=&flags_to_atoms_again(Flags),&  &&&&&boot(Start,Flags0,Args).&
  第2行将当前进程注册为init,于是我们就有了init进程。第4行启动了一个新的进程ON_LOAD_HANDLER,这个进程处理一些和加载相关的事件。然后对传入的参数做一些处理,Start是erl -s参数传入的要运行的MFA列表,Flags0是调用erl传入的一些标志,Args是erl -extra 传入的一些额外参数。接下来这些参数传入boot/3。下面是boot/3的代码:
  boot(Start,Flags,Args)&-&&  &&&&&BootPid&=&do_boot(Flags,Start),&  &&&&&State&=&#state{flags&=&Flags,&  &&&&&&&&&&&&args&=&Args,&  &&&&&&&&&&&&start&=&Start,&  &&&&&&&&&&&&bootpid&=&BootPid},&  &&&&&boot_loop(BootPid,State).&
  boot/3调用do_boot/2,设置State,然后就进入boot_loop/2循环。下面是do_boot/2的代码:
  do_boot(Flags,Start)&-&&  &&&&&Self&=&self(),&  &&&&&spawn_link(fun()&-&&do_boot(Self,Flags,Start)&end).&  &do_boot(Init,Flags,Start)&-&&  &&&&&process_flag(trap_exit,true),&  &&&&&{Pgm0,Nodes,Id,Path}&=&prim_load_flags(Flags),&  &&&&&Root&=&b2s(get_flag('-root',Flags)),&  &&&&&PathFls&=&path_flags(Flags),&  &&&&&Pgm&=&b2s(Pgm0),&  &&&&&_Pid&=&start_prim_loader(Init,b2a(Id),Pgm,bs2as(Nodes),&  &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&bs2ss(Path),PathFls),&  &&&&&BootFile&=&bootfile(Flags,Root),&  &&&&&BootList&=&get_boot(BootFile,Root),&  &&&&&LoadMode&=&b2a(get_flag('-mode',Flags,false)),&  &&&&&Deb&=&b2a(get_flag('-init_debug',Flags,false)),&  &&&&&catch&?ON_LOAD_HANDLER&!&{init_debug_flag,Deb},&  &&&&&BootVars&=&get_flag_args('-boot_var',Flags),&  &&&&&ParallelLoad&=&&  &&&&&&&&&(Pgm&=:=&&efile&)&and&(erlang:system_info(thread_pool_size)&&&0),&  &&&&&PathChoice&=&code_path_choice(),&  &&&&&eval_script(BootList,Init,PathFls,{Root,BootVars},Path,&  &&&&&&&&&&&&&&&&&{true,LoadMode,ParallelLoad},Deb,PathChoice),&  &&&&&%%&To&help&identifying&Purify&windows&that&pop&up,&  &&&&&%%&print&the&node&name&into&the&Purify&log.&  &&&&&(catch&erlang:system_info({purify,&&Node:&&&++&atom_to_list(node())})),&  &&&&&start_em(Start).&
  do_boot/2创建了一个负责引导过程的进程(do_boot/3,没有register,让我们称为do_boot),boot/2最后进入了一个boot_loop循环,接受来自do_boot进程的消息。现在系统上有3个进程,如下图所示:
  此时的&0.0.0&在boot_loop循环等待接受&0.2.0&发出的和boot相关的消息,&0.1.0&在等待接收和加载相关的消息。下面看do_boot/3的引导过程。第7-10行从传入的参数中获得加载器相关的参数,然后在第11行调用函数start_prim_loader通过erl_prim_loader模块的start/3函数创建了加载器进程。第13-14行从启动脚本中获得启动指令列表。有关启动脚本的格式参见文档&erl -man script ,启动脚本描述了Erlang运行时系统启动的过程,包含了启动过程要执行的一系列指令。如果启动erl的时候没有带-boot Name参数,那么默认使用start.boot启动脚本。start.boot是由start.script生成的。start.script内容摘要如下所示:
  {script,&  &&&&&{&OTP&&APN&181&01&,&R15B01&},&  &&&&&[{preLoaded,&  &&&&&&&&&&[erl_prim_loader,erlang,init,otp_ring0,prim_file,prim_inet,prim_zip,zlib]},&  &&&&&&{progress,preloaded},&  &&&&&&{path,[&$ROOT/lib/kernel/ebin&,&$ROOT/lib/stdlib/ebin&]},&  &&&&&&{primLoad,[error_handler]},&  &&&&&&{kernel_load_completed},&  &&&&&&{progress,kernel_load_completed},&  &&&&&&{path,[&$ROOT/lib/kernel/ebin&]},&  &&&&&&{primLoad,&  &&&&&&&&&&[application,application_controller,application_master,&  &&&&&&&&&&&...&  &&&&&&&&&&&standard_error,user,user_drv,user_sup,wrap_log_reader]},&  &&&&&&{path,[&$ROOT/lib/stdlib/ebin&]},&  &&&&&&{primLoad,&  &&&&&&&&&&[array,base64,beam_lib,binary,c,calendar,dets,dets_server,dets_sup,&  &&&&&&&&&&&...&  &&&&&&&&&&&supervisor_bridge,sys,timer,unicode,win32reg,zip]},&  &&&&&&{progress,modules_loaded},&  &&&&&&{path,[&$ROOT/lib/kernel/ebin&,&$ROOT/lib/stdlib/ebin&]},&  &&&&&&{kernelProcess,heart,{heart,start,[]}},&  &&&&&&{kernelProcess,error_logger,{error_logger,start_link,[]}},&  &&&&&&{kernelProcess,application_controller,&  &&&&&&&&&&{application_controller,start,&  &&&&&&&&&&&&&&[{application,kernel,&  &&&&&&&&&&&&&&&&&&&...}]}},&  &&&&&&{progress,init_kernel_started},&  &&&&&&{apply,&  &&&&&&&&&&{application,load,&  &&&&&&&&&&&&&&[{application,stdlib,&  &&&&&&&&&&&&&&&&&&&...}]}},&  &&&&&&{progress,applications_loaded},&  &&&&&&{apply,{application,start_boot,[kernel,permanent]}},&  &&&&&&{apply,{application,start_boot,[stdlib,permanent]}},&  &&&&&&{apply,{c,erlangrc,[]}},&  &&&&&&{progress,started}]}.&
  do_boot/3中的BootFile就是这个文件,BootList就是从第3行开始的这个列表。列表中的每一项表示一个动作,这些动作包括preLoaded、progress、path、primLoad、kernelProcess和apply,这些动作在erl -man script文档中有详细的解释。do_boot/3的第23行调用eval_script/8函数负责执行这个列表中的每一个动作。下面是eval_script/8的代码节选:
  eval_script([{progress,Info}|CfgL],Init,PathFs,Vars,P,Ph,Deb,PathChoice)&-&&  &&&&&debug(Deb,{progress,Info}),&  &&&&&init&!&{self(),progress,Info},&  &&&&&eval_script(CfgL,Init,PathFs,Vars,P,Ph,Deb,PathChoice);&  &eval_script([{preLoaded,_}|CfgL],Init,PathFs,Vars,P,Ph,Deb,PathChoice)&-&&  &&&&&eval_script(CfgL,Init,PathFs,Vars,P,Ph,Deb,PathChoice);&  &eval_script([{path,Path}|CfgL],Init,{Pa,Pz},Vars,false,Ph,Deb,PathChoice)&-&&  &&&&&...&  &&&&&eval_script(CfgL,Init,{Pa,Pz},Vars,false,Ph,Deb,PathChoice);&  &eval_script([{path,_}|CfgL],Init,PathFs,Vars,P,Ph,Deb,PathChoice)&-&&  &&&&&eval_script(CfgL,Init,PathFs,Vars,P,Ph,Deb,PathChoice);&  &eval_script([{kernel_load_completed}|CfgL],Init,PathFs,Vars,P,{_,embedded,Par},Deb,PathChoice)&-&&  &&&&&eval_script(CfgL,Init,PathFs,Vars,P,{true,embedded,Par},Deb,PathChoice);&  &eval_script([{kernel_load_completed}|CfgL],Init,PathFs,Vars,P,{_,E,Par},Deb,PathChoice)&-&&  &&&&&eval_script(CfgL,Init,PathFs,Vars,P,{false,E,Par},Deb,PathChoice);&  &eval_script([{primLoad,Mods}|CfgL],Init,PathFs,Vars,P,{true,E,Par},Deb,PathChoice)&  &&&&&...&  &&&&&eval_script(CfgL,Init,PathFs,Vars,P,{true,E,Par},Deb,PathChoice);&  &eval_script([{primLoad,_Mods}|CfgL],Init,PathFs,Vars,P,{false,E,Par},Deb,PathChoice)&-&&  &&&&&eval_script(CfgL,Init,PathFs,Vars,P,{false,E,Par},Deb,PathChoice);&  &eval_script([{kernelProcess,Server,{Mod,Fun,Args}}|CfgL],Init,&  &&&&&&&&&&&&&PathFs,Vars,P,Ph,Deb,PathChoice)&-&&  &&&&&start_in_kernel(Server,Mod,Fun,Args,Init),&  &&&&&eval_script(CfgL,Init,PathFs,Vars,P,Ph,Deb,PathChoice);&  &eval_script([{apply,{Mod,Fun,Args}}|CfgL],Init,PathFs,Vars,P,Ph,Deb,PathChoice)&-&&  &&&&&...&  &&&&&eval_script(CfgL,Init,PathFs,Vars,P,Ph,Deb,PathChoice);&  &eval_script([],_,_,_,_,_,_,_)&-&&  &&&&&&  &eval_script(What,_,_,_,_,_,_,_)&-&&  &&&&&exit({'unexpected&command&in&bootfile',What}).&
  eval_script/8对BootList中的每一个动作进行处理。有一些动作要给init进程发送消息,init进程的boot_loop/2循环接收这些消息。boot_loop/2接收的消息中有以下两个:
  boot_loop(BootPid,&State)&-&&  &&&&&receive&  &&&&&&&&&{BootPid,progress,started}&-&&  &&&&&&&&&&&&&{InS,_}&=&State#state.status,&  &&&&&&&&&&&&&notify(State#state.subscribed),&  &&&&&&&&&&&&&boot_loop(BootPid,State#state{status&=&{InS,started},&  &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&subscribed&=&[]});&  &&&&&&&&&......&  &&&&&&&&&{'EXIT',BootPid,normal}&-&&  &&&&&&&&&&&&&{_,PS}&=&State#state.status,&  &&&&&&&&&&&&&notify(State#state.subscribed),&  &&&&&&&&&&&&&loop(State#state{status&=&{started,PS},&  &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&subscribed&=&[]});&  &&&&&&&&&......&  &&&&&end.&
  BootList最后一条指令是{progress,started},对应了boot_loop/2第3行的消息,在执行完这一条指令之后,eval_script/8结束了执行,因此do_boot/3在结束eval_script/8之后调用start_em/1之后就正常退出了,进程&0.2.0&正常退出,boot_loop/2收到'EXIT'消息,init进程进入loop/1循环。此时,init作为初始化的任务已经完成。这一个默认的启动脚本启动了两个应用程序,kernel和STDLIB,前者是一个普通应用程序,后者只是一个库应用程序。如果erl没有传入-noshell参数,kernel还会启动shell和用户交互。这两个应用程序是Erlang最简系统的基础,前者提供了必要的系统服务,例如文件服务、网络服务和错误日志记录服务等,后者提供了编写程序需要使用的各种工具、数据结构以及OTP相关的重要模块。
  通过本文的分析可以看出,Erlang虚拟机很像一个运行了操作系统的计算机。erl对应的是BIOS,加载对应bootloader的erlexec。erlexec加载BEAM虚拟机,BEAM虚拟机对应了操作系统。接下来BEAM进行初步的初始化,初始化执行环境,对应了操作系统的初始化。初始化完成之后,BEAM像Linux一样加载系统中的第一个进程init。init进程读取启动列表,执行启动系统的步骤。执行完这些步骤之后,Erlang成为了一个完全完成了初始化过程可以运行的系统。Erlang像操作系统一样,有自己的调度系统,内存管理系统,还有和外界交互的I/O系统。只不过内存管理系统更加的智能,可以主动帮助进程进行垃圾回收。I/O系统以系统服务的方式存在,通过Erlang消息通信的方式向其他进程提供服务,因此Erlang的进程只需要通过消息这一种语义就能和外界交换数据。Erlang中的模块就好像操作系统中的动态共享库,只要加载到系统中,就可以供所有的进程访问。多个模块可以组织为应用程序。Erlang的模块命名是平坦的,因此不同应用程序中的模块不能重名。Erlang的应用程序是对模块和进程的一种组织方式,从一个应用程序可以包含一组进程的角度看,Erlang的应用程序有点类似于Linux系统中的进程。
  原文:
  【编辑推荐】
    Erlang之父Joe Armstrong访谈:程序调试与啤酒
  Scala和Erlang,以及多核主导的未来
  Erlang面向分布与并发的编程语言
  看Erlang中Actor模型的执行方式和优劣
  Erlang视点:并行计算和云计算
H3C认证Java认证Oracle认证
基础英语软考英语项目管理英语职场英语
.NETPowerBuilderWeb开发游戏开发Perl
二级模拟试题一级模拟试题一级考试经验四级考试资料
软件测试软件外包系统分析与建模敏捷开发
法律法规历年试题软考英语网络管理员系统架构设计师信息系统监理师
高级通信工程师考试大纲设备环境综合能力
路由技术网络存储无线网络网络设备
CPMP考试prince2认证项目范围管理项目配置管理项目管理案例项目经理项目干系人管理
职称考试题目
招生信息考研政治
网络安全安全设置工具使用手机安全
生物识别传感器物联网传输层物联网前沿技术物联网案例分析
Java核心技术J2ME教程
Linux系统管理Linux编程Linux安全AIX教程
Windows系统管理Windows教程Windows网络管理Windows故障
数据库开发Sybase数据库Informix数据库
&&&&&&&&&&&&&&&
希赛网 版权所有 & &&扫描或点击关注中金在线客服
下次自动登录
其它账号登录:
数据加载中...
数据加载中...
浏览过该文章的人还浏览过
数据加载中...
数据加载中...
数据加载中...
  文 / Peter Seibel
译 / 米全喜   以啤酒收取程序调试报酬   Seibel:你是如何开始学习编程的?是从甚么时间开始的?
  Armstrong:是从中学时开始的。我出生于1950年,上中学那会儿还没有几台计算机。到了中学末了一年,那年我应当是17岁,我们当地的议会得到一台大型计算机,似乎是IBM的。我们可以在上面写Fortran程序。平常,我们在编码纸上写好程序,然后发出往。一个星期后,等编码纸和穿孔卡拿回来的时间还必须确认一下。但是建造穿孔卡的人总会出点错,所以可能要反复1~2次才能弄好。末了这些穿孔卡就可以送到计算机中心了。   卡片进进计算机中心后会再拿回来,由于Fortran编译器会在程序中出现第一个句法错误的地方停下来,后面的程序就都不处理了。你的第一个程序似乎必要3个月才能跑通。我熟悉到,不能每次只送一个程序,应当并行地开发多个单一子例程并且一次都送往。我记得写过一个表现国际象棋棋盘的小程序,用打印机绘制出来。但是由于中间等待的时间太烦人了,我不得不把全部的子例程都当做并行的任务一次写完。   Seibel:你学的是物理学,是从甚么时间开始转向编程的?   Armstrong:嗯,有一些本科生的课程必要编写程序,而我又分外喜好编程。我还非常善于调试程序。假如别人程序出了题目,我就会往调试别人的程序。标准调试的开价是一杯啤酒。也可能提价,还有两杯啤酒、三杯啤酒的题目。   Seibel:在给他们调试程序时,是以他们必须给你买多少杯啤酒而论的,对吗?   Armstrong:对,等我修复了程序时他们要给我买啤酒。我在读程序的时间总是在想:"他们为甚么要如许写程序呢,太复杂了。"我会重新编写并简化程序。看到人们编写复杂的代码我感到很吃惊。有些题目用几行代码就能解决,但是他们要写上几十行。我有点好奇,他们为甚么看不到简单的方法呢。而我就很是善于采取简单的方法。   我真正开始编程是拿到第一个学位并打算读博士学位的时间。我开始读高能物理博士学位并加进了那边的气泡室小组,他们有一台计算机。那是一台DDP-516,是Honeywell公司的。我可以独自一人使用它。它是穿孔卡式的,但是可以在上面直接运行程序,只要把穿孔卡放进往,按一下按钮,答案刷地一下就出来了。我分外喜好那台计算机。我在上面编写了一个小象棋程序。   当时的实际磁心存储器是由妇女编织而成的,可以或许看到磁心和一块块的小磁铁和穿进穿出的线路。它的价格高得惊人,有一个大约10MB的磁盘驱动器,上面有20个小底板,大约15公斤重。它还配了一个电传文本的界面,可以在上面输进程序。   后来又出现了"玻璃电传打字终端",那是最早的视频表现器设备,可以在上面输进并编辑程序。我觉得这太神奇了,不再必要穿孔卡了。我记得当时和计算机治理员说:"要我说,将来有一天大家都会有如许一套机器。"他说道:"我看你疯了,Joe,你真是疯了!""为甚么不可能呢?""这些东西贵得离谱。"   正是从当时我真正开始学习编程了。当时我的导师对我说:"你不应当再读物理学博士了,转业吧。你热爱计算机,应当搞计算机。"我说道:"不,不,不。我不能中途而废。"但实际上他的话是对的。   不同平常的工作经历   Seibel:那你拿到博士学位了吗?   Armstrong:没有。我没钱了,所以没有读完。我后来往来往了爱丁堡大学。此前在读物理的时间我们经常到物理系图书馆往学习。在图书馆的角落里有一些计算机科学书本。有一些棕色封底的杂志叫做《机器智能》,一共有4期,是爱丁堡大学的机器智能系编辑出版的。我学的是物理学,但我却渴看浏览这些杂志,并且在想:"真是太有趣了。"DonaldMichie当时担任爱丁堡大学机器智能系的主任,我给他写了封信,说我对这类东西非常感爱好,问他那边有没有工作可做。他给我回了信,说而今还没有,不过无论如何,他很想和我见一面,看看我是甚么样的人。   几个月后我接到一个电话,也可能是一封信,是Michie的。他说:"我周二往伦敦,我们见一面如何?我要乘火车回爱丁堡,你能来车站吗?"我往了车站,见到Michie,他说:"嗯,不能在这儿口试-我们往找个酒吧。"因而我们到一家酒吧聊了聊。过了没多久又收到他的一封信,说:"在爱丁堡大学有一份研究工作,你申请一下吧。"因而我成了DonaldMichie的研究助理并往了爱丁堡大学。我就如许从物理学转到了计算机。   Michie在二战期间曾和图灵在布莱切利公园(BletchleyPark)(编者注:二战爆发前,英国在间隔伦敦不远的布莱切利公园设置了国家暗码破译机构,很多破译员在那边工作,破解德国电报暗码)一起工作过,拿到了图灵全部的论文。我在图灵图书馆有一张书桌,四周也全都是图灵的论文。我在爱丁堡大学待了一年。此后由于数学家JamesLighthill的原因起因,爱丁堡大学都有点维持不下往了。Lighthill受雇于当局,前往爱丁堡大学调查人工智能。他回往后说道:"阿谁地方甚么有贸易价值的东西也弄不出来。"   说得就像一个巨大的儿童游戏区。我是英国机器人学会的创始成员,我们都以为这个工作意义庞大。但是拨款机构却说:"机器人!这是甚么东西!我们不打算在这上面投进资金了。"我记得那是1972年前后,全部的资金来历都枯竭了,大家都说:"嗯,在这里度过的韶光非常精美,但而今最好还是找点别的事变做吧。"   因而我又回往从事物理学工作了。我到了瑞典,在EISCAT科学协会得到一份物理学程序员的工作。我的上司来自IBM,年龄比我大,他想要上头给出一份规格说明书,如许他就可以拿往实行。我们曾接头过这个题目。他说:"假如没有任务说明,也没有规格说明,如许的工作太糟了。"我说:"嗯,假如没有任务说明,那才是一个好任务。由于你可以按照自己喜好的方式来完成。"一年后我的上司往职了,我接替了他的工作,担任首席计划师。   我为他们计划了一个体系,可以称为利用操纵体系,那是一个在平凡操纵体系上运行的体系。阿谁时间计算机的价格已比较公道了。我们有一些NORD-10计算机,是挪威制造的-我觉得他们这类型号的计算机想要进进PDP-11的市场。   我在那边工作了将近4年。接着在瑞典空间研究中心得到一份工作,构建了另外一个利用操纵体系,用于控制瑞典发射的名为"海盗"的第一颗卫星。那是一个有趣的项目,不过我忘了那台计算机的名字了,只记得它克隆的是Amdahl公司的计算机。那上面还只有行编辑器,没有全屏幕编辑器。全部的程序都只能放到一个目录下面。文件名是10个字母,扩大名是3个字母。还有一个Fortran编译器或汇编语言编译器,扫数东西就是这些了。   有趣的是,而今回头想想,我不以为当今这些小玩意会让你的生产率更高。比如说分层文件体系,它怎么可能让生产率更高呢?很多程序开发方式是在脑海中形成的。我以为在那些简单的体系上工作可以逼迫你规范地进行思考。假如没有目录体系,就只能把全部的文件都放到一个目录下面,你只能变得相当规范。假如没有修订控终瑰系,你也只能变得相当规范。假如自己做的事变可以或许规范起来,那我觉得分层文件体系和修订控终瑰系也就没甚么可取的地方了。它们解决的题目并不能从本质上解决你的题目。假如多人一起工作,它们可能会让事变轻易一些。但对个人来讲,我看不出来有甚么差别。   另外,我觉得我们而今由于选择过多而不堪重负。我的意思是,当时间我只能使用Fortran。乃至连Shell脚本也没有。只有可以运行程序的批处理文件,编译器,还有就是Fortran。假如确切必要的话,还可能有汇编语言编译器。不必要痛楚地做出选择。今天年轻的程序员肯定会感到很不舒服,面对20种编程语言和几十种框架,该如何选择,真是无所适从。我们当时没有这些难以选择的地方。只要开始做就行了,由于使用甚么语言、甚么工具都已经是定下来的。不必要考虑该做些甚么,只管做就行了。   打开黑盒的紧张性   Seibel:另外一个差别是而今也没法彻底相识全部体系了。也就是说不仅仅是要做出很多选择,而且在选摘要使用哪些黑盒的时间,还不肯定完全明黑黑盒的工作方式。   Armstrong:是啊,假如这些大黑盒不能正常工作,必须做出点窜,我觉得自己把全部的内容都从头开始编写一次会更轻易些。做不到软件复用,真是太糟了。   Seibel:但是假如把全部这些黑盒都打开,看看里面有甚么,看看它们的工作方式,再确定如何对它们做一点改造来满足自己的必要。你觉得如许做确切是可行的吗?   Armstrong:这些年我犯了一些人们常犯的错误,那就是没有打开黑盒。偶然间想想,觉得这个黑盒没法明黑,难度太大,所以不想打开它。我曾打开过1~2个黑盒。有一次我必要做一个窗口体系,为Erlang做一个图形体系,我在想:"嗯,就在XWindows上运行吧。"XWindows是甚么呢?它是一个套接字,上面跑着协议。只要打开套接字,往里面注进这些消息就可以了。为甚么要用库呢?Erlang是基于消息的。团体引导脑筋是向其他东西发出消息,让它们实行操纵。嗯,XWindows中的引导脑筋则是,有一个窗口,向窗口发送消息,再由窗口实行操纵。假如在窗口中实行操纵,它会把消息回送给你。这非常像Erlang。但是XWindows的编程方式是应用回调库-假如出现了这个环境就调用这个函数。这不是Erlang的思考方式。Erlang的思考方式是,给某个东西发送消息,让它做一些事变。所以,等一下,把个中的库往掉吧,直接和套接字对话。   猜猜效果会如何?非常简单。X协议收到了一些消息,我不知道具体是多少条,大概是100条、80条,大致就是这么多。但实际上只必要个中的20条就能完成有用的工作了。把这20条消息映射到Erlang术语上,变个小把戏,然后可以向窗口直接发送消息,它们就开始执举措作了。如许做的服从也很高。但界面不是很好,由于我没有把太多的精力用到图形和艺术标准上。假如为了让界面再雅观一些,要做的工作还很多。但是不管怎么说,实际上并不难。   另外一个例子是我做的排版体系,我打开的抽象边界是PostScript。到了边界的地方你会想:"我不想超出这个边界。"由于你会以为边界里面的东西极其复杂。但是我再次发现,它实际上是很简单的。那是一种编程语言,一种不错的编程语言。抽象边界很轻易穿越,而一旦穿越,会有很多收益。   在出****夷潜綞rlang编程书时,出版社说:"我们有画图工具。"但是画图工具真的很难精确地对准箭头,所以我不喜好那些工具。而且画图的时间手也很难熬。我想:"编写一个天生PostScript的程序,然后在'这里画个圆圈、那边画个箭头',让程序正常运转起来,如许一比,编程花的时间并不长。"编写程序必要几个小时。以所见即所得的方式画图也必要这么长时间。只是自己编写程序还有两个好处。你的手不会难熬,而且即使把图形放大一万倍,看到的箭头也是对得整整洁齐的。   我并不是说刚进行的程序员应悼貉全部这些抽象的东西都打开。我的意思是,肯定要考虑是不是可以打开它们。不要完全放弃这个想法。看看直接到达的路子是不是是比包装后的路子要快一些,这是值得一看的。一样平常来讲,假如购买软件或是使用其他人的软件,肯定要充分考虑还必要花很长时间来加工这套软件,由于它和你想要的不完全一样。软件的实行方式有奥妙的差别,而这个差别可能必要很长时间来解决。   编程这些年的变化   Seibel:同刚开始编程时相比,你在看待该如何编程的题目上最大的变化是甚么?   Armstrong:我以为编程方式中的最大变化与硬件无关。明显,而今的计算机速率要快得多,功能要强大得多,但是人的大脑比最好的软件工具还要强大一百万倍。我在编写程序的时间,几天以后会俄然说:"程序中有一个错误-假如如许、那样、那样、如许的话,程序就要崩溃了。"然后我往看了代码,确切云云。此前一点征象也没有。你能告诉我哪一个开发体系可以或许做到这一点吗?作为一个程序员,我所发生的变化是心田脑筋的变化。   我以为在经过多年的编程以后会有两个变化。一个变化是,在年轻的时间,我会不停地写程序,直到完成。当程序完成后,我就不再管它了。程序写好了,完工了。然后我会俄然意会:"啊!搞错了!真是笨蛋!"我会重新编写程序,后来再次发现:"噢,程序是错的。"因而又重新编写。   我记得当时有如许一个想法:"先不要动手写代码,把这些东西都想好,如许做不是很好吗?"假如我不写代码就能获得那番意会,不是很好吗?我以为而今可以做到这一点了。那20年可以算作是学习如何编程的时期。而今知道该如何编程了。我从前通过实验来学习编程。而今我知道该如何编程了,不必要再做实验了。   偶然,我也得做一些很小的实验,比如编写一些非常小的程序往返答某个题目。我会把事变想清楚,等到开始编程的时间,这些程序就可以或多或少地像我预计的那样运行起来了,由于之前我已想清楚了。这也意味着要花很长时间。编写程序、有所觉醒、重新编写。如许可能必要花上一年的时间来写程序。所以我而今可能不如许做,而是先思考上一年。我不会再做那种简单的输进工作。   这是第一个变化。出现的第二个变化是直觉。在年轻的时间,我会通宵地写程序,干到破晓4点钟,精疲力尽,那是夫君汉风格的编程,一个小时接着一个小时,不停地编写代码。即使环境不好我也坚持不懈,总要让代码可以或许跑起来。即使没有直觉,我也要继续编程。   我得到的教训是,在疲顿的时间编写的程序都是垃圾,第二天就要把它们都抛弃了。20年前,就算猛烈地感到事变不对劲、代码中有错误时,我也会继续编程。这些年来我注重到,真恰好的代码是我完全进进状态的时间编写的,时间不知不觉地过了,而我乃至没有在考虑程序,只是很放松地坐在那边,输进这些东西,看着自己输进的东西出而今屏幕上。如许的代码会很不错。假如你不能集中注重力,弄出来的东西会说:"不行,不行,这儿错了,那儿也错了。"可我在多年前并没有注重到这一点。写出来的代码都被抛弃了。而今,假如觉得不行,我就不再编程了。"不能再写了。"这是我根据经验得到的,停下来,不要再写代码了。不要再处理这个题目了。干点别的。   我在上学的时间很善于数学之类的课程,所以在想:"噢,我是一个按照逻辑思考的人。"但是我参加生理测试时,在直觉上得了高分,而逻辑思考方面的分数却有点低。不是很低,我还是可以做数学这类的标题,我相当善于。但正是由于我善于数学,所以我过往以为科学是关于逻辑和数学的。我而今就不会如许说了。我要说科学也有很多直觉,根据直觉可以或许知道甚么是精确的。   Seibel:你而今在编码之前会花更长的时间思考,那么在思考阶段会做些甚么呢?   Armstrong:噢,我会记些笔记,我不仅仅是在思考。在纸上任意写点甚么。我可能不会写很多代码。假如你密切注重我的活动,会发现我大部分时间都在思考,偶然写点甚么。另外一件对解决题目非常紧张的事变是问问我的同事:"你将如何解决这个题目?"你找到他们,说:"我不知道应当采取这类方式还是那种方式。必须在A和B之间做出选择。"然后你向他们形貌A和B,等讲到一半的时间,你会说:"啊,是B。感谢你们。非常感谢。"如许的事变发生过很多次。   你必要如许一块智能黑板,假如你只是独自一人在一块黑板上写写画画,是得不到反馈的。但是假如面对的是人,你会在黑板上向他们解释更换方案,他们也会加进接头,提出一点建议。然后俄然间你就知道答案是甚么了。对我来讲没有触及到代码编写。但是和处理一样题目的同事进行交谈黑白常有价值的。   (本文来历于《程序员》杂志11年03期,内容节选自人民邮电出版社北京图灵文化发展有限公司出版的《编程人生》一书。特此感谢图灵公司授权。)   (本文来自《程序员》杂志11年03期,更多出色内容敬请关注03期杂志)   《程序员》11年03期出色内容:2011开放平台之征   《程序员》杂志订阅
有思想的财经媒体人都在这里!
是否确认删除这篇文章?
是否确认将这个粉丝放进黑名单?
是否确认这条留言删除?
您确定要删除这条信息?
用手机或者平板电脑扫描应用拍下上方二维码,可以在手机博客继续浏览文本,也可以分享给你的联系人。
以下资料仅供联系使用,请放心填写。
*您的姓名:
*联系电话:
我要给文敢逊诽送鲜花
您将赠送(朵)鲜花给:文敢逊诽
(1鲜花 = 0.1人民币)
附言:(不超过100字)
可用金币:
可用人民币元
您还需要支付0元人民币}

我要回帖

更多关于 父母和孩子看待事物 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信