《代码之髓--编程语言核心概念》读书笔记

《代码之髓--编程语言核心概念》

[日] 西尾泰和(Nishio Hirokazu) 著

曾一鸣 译

人民邮电出版社  2014年8月第1版

书中的示例源代码可以从作者的网站上下载: http://nhiro.org/langbook/

p3: ruby语言中false, nil为假,其余都为真(包括数字0)。c语言中0为假,非0为真。python语言中0为假,空的容器(如空字符串,空的列表等)为假。java语言中0不是布尔值,所以既不是真也不是假。所以,0为真,0为假,0即非真也非假的语言都存在。

p5: 在招聘网站dice.com上检索一下,就可知道APL语言的没落了。

p8: 1946年,第一台电子计算机---ENIAC(埃尼阿克,Electronic Numerical Integrator and Computer)问世。它是第一台可编程计算机。编程方式就是用电缆把不同的端口连接起来。修改程序很费劲。

1949年,EDSAC(爱达赛克,Electronic Delay Storage Automatic Calculator, 电子延迟存储自动计算机)问世。它是通过纸带打孔的方式来记录和读取数据。程序通过纸带输入。修改程序简单了很多。

paper tape : https://en.wikipedia.org/wiki/File:PaperTapes-5and8Hole.jpg

作者设计的,基于浏览器的EDSAC仿真器: http://nhiro.org/learn_language/repos/EDSAC-on-browser/index.html

p10: FORTRAN的全称是Formula Translating System(公式翻译系统)。设计者是John Backus。

P11: Perl语言设计者Larry Wall在其著作Programming Perl(中文版为《Perl语言编程》中国电力出版社,何伟平 译)中提出,程序员的3大美德:懒惰、急躁和傲慢(laziness, impatience and hubris)。

p17: Forth语言开发与1958年左右,是一种几乎没有语法的语言。设计者是Charles H. Moore。

作者的网页上Forth模拟器:http://nhiro.org/learn_language/FORTH-on-browser.html

p24: 前缀表达式和后缀表达式也被称为波兰表示法和逆波兰表示法,得名于最先研究它们的波兰人Jan Lukasiewicz。前缀表达式总括号是不必要的。

p28,p42: if...else..., while, for, foreach语句都是为了更可读而发明的,只用条件跳转就可以实现这些功能。

p40: 函数在不同时期,不同语言中,有事务、程序(procedure)、子程序(subroutine)、方法(method)等不同叫法,都是一回事。

p43: 函数的诞生。把反复使用的命令封装在一起再利用,这种需求在很早以前就有了。1949年的EDSAC就使用了带有这一功能的技术。参见 https://www.cl.cam.ac.uk/~mr10/edsacposter.pdf。那时函数的返回正确的地址是靠每次调用前修改在函数结尾中的跳转指令的目的地址。调用处要知道返回命令的地址,以便修改其中的返回地址。假设函数增加了几条语句,所有调用的地方都要修改。

后来设计专用的寄存器以保存返回地址,并同时设计有跳转到该寄存器所记录的地址的命令,这样就调用处就不用知道函数的返回命令的地址了,只要填写返回地址寄存器就可以了。但是,这种方法也有一个问题,如果调用函数X其间又调用了函数Y,则返回地址就会被覆盖,函数X执行之后应该返回的目的地址就找不到了。

最终的解决办法是使用栈。

p45:函数名使一组操作更可读。

p47: 过去有些语言无法实现递归调用,现在几乎所有的语言都支持这一编程技术。

p54: 出错时如何处理,大体上可以分为2中方法:使用返回值和使用异常处理。除了c语言外,大多数语言都支持异常处理。

使用返回值处理错误有2个问题:1)遗漏错误。忘了或者懒得检查返回值,并做处理。比如,c语言中写文件的函数fprintf(),它在执行错误时返回负值。你对这个检查过吗?还是想当然地以为执行成功从而遗漏了错误呢?一个很少会出错的函数可能通过很多测试,真正发现问题,可能是产品发布很久以后了。由于时间久远、又是在远程的生产环境下,追查问题往往会很辛苦。此外,由于连锁反应,问题看起来是在和出错的函数没有任何关系的地方发现的,这样一来就更难发现真正的问题所在了。理性情况下,严格检查返回的错误值,并处理,就不会造成遗漏错误的情况。然而,现实中忘记或忽略检查返回值的情况不胜枚举。2)处理返回错误代码,会使得源代码很难读懂。常常是少量正常处理的代码夹在大量的错误处理代码的缝隙中间,使得程序流程难以读懂。把错误处理代码集中起来,用goto语句跳转到错误处理代码,是个好办法。参见Linus Torvalds的文章 http://www.linuxfromscratch.org/alfs/view/hacker/part2/hacker/coding-style.html

p60: PL/I语言中异常和失败叫做条件(condition),抛出异常的命令不是现在一般的raise或throw,而是signal。

p61: John Goodenough 1975年提出了现代异常的处理方式: 一是明确声明命令可能抛出的异常,二是需要有将可能出错的操作括起来的语句结构。参见 https://www.sei.cmu.edu/about/leadership/display.cfm?customel_datapageid_2623=3002

p62: CLU语言使用了except一词,但是CLU语言中抛出异常的命令和PL/I语言一样,都是signal。C++语言选用throw作为触发异常的命令,是因为raise和signal两个词已经在标准库中作为函数名字占用了。于是触发异常的表述就变成了抛出异常。

p65: 1990年左右,微软公司开始引入结构化异常处理(SEH, __try, __except, __finally),1995年java也引入finally。现今,python和ruby语言也支持finally。

p66: c++中没有finally,使用的是RAII(资源获取即初始化),Bjarne Stroustrup认为,RAII比finally更优雅。

p67: 2001年出现的D语言,引入了作用域守护(scope guard)的概念,定义从某作用域退出时要执行的操作。

p70: 同一种错误,是返回错误码,还是抛出异常,不同语言有不同的做法。在生产环境下,不同的软件目的不同,简单地停止操作有时可能不太妥当。但是,作者认为,至少是在学习和开发阶段,一出错就立刻抛出异常,终止操作立刻报告是较好的,这种思想被成为错误优先(fail first)。

p73: 检查型异常没有得到普及,参见C#语言的设计者Anders Hejlsberg所说“The Trouble with Checked Exceptions” https://www.artima.com/intv/handcuffs.html

p88: Perl语言中,local的变量是动态作用域(从perl4开始),my的变量是静态作用域(从perl5开始)

p89: Python语言的内置作用域是其他语言的所谓全局作用域,python的全局作用域是文件作用域。

p97: 位值制计数法发明于印度,途径阿拉伯国家传到欧洲,因此被称为阿拉伯数字。有一本年代可考的著作是花剌子模(al-khwarizmi)于公元825年写的《印度数的计算法》,这是关于数位最古老的记载。值得一提的是,al-khwarizmi这个词在拉丁语里被记为Algoritmi,这就是现代英语中算法(algorithm)的词源。

p99: calculate的词源是拉丁语中表达算盘中的算珠的词calculus。Calcium(钙)一词的词源同样来于此。

p132: java、python、ruby将数组作为一种最基本的容器,lisp、scheme、haskell将链表作为最基本的容器。

p147:  表明源码编码方式的魔术注释符。

emacs:

# -*- coding: shift_jis -*-

vim:

# vim: set fileencoding=shift_jis :

p150: c语言只规定了char类型最低为8bit,IBM7.01(1952年)和UNIVAC 1103(1953年)采用了9bit。但是时至21世纪的今天,8bit的机器占了绝大多数。char是character(字符)的前4个字母。

p161: 《Java Concurrency in Practice》 Brain Goetz, Joshua Bloch, Doug Lea 著,中文版《Java并发编程实战》 机械工业出版社 童云兰 译。竞争发生的3个条件:a)共享变量 b)修改变量  c)同时访问。只要任何一个条件不具备,就可以避免竞争。

避免条件a。采用进程(不共享内存,当然访问文件、数据库等还可能竞争)。或者采用actor模型,即传递消息来通信,而不共享内存。Erlan、Scala就采用了actor模型。

避免条件b。c/c++中const声明的变量,Scala中val声明的变量,Java中Immutable模式(就是类的私有成员,不提供setter)下的变量都是不能改写的。从而对它们的访问不存在竞争。

避免条件c。采用协作式多任务的模式。即一个任务主动在适当的时候,让出资源,让别人使用。这并不总能够容易做到。另一种方式是用锁(lock,mutex,semaphore都是锁一类的东西)

p176:模块和包是一个意思,就是把相关的变量和函数放在一起,便于管理。Perl、Java叫包。Python、Ruby叫模块。

p188: JavaScript中函数f前面使用new运算符后,会执行下面4个操作:

创建新的对象x;新创建的对象x的原型变为函数f的原型;把新创建的对象x传给this,执行函数f的内容;返回对象x。

p213:发表于2002年的一篇关于Trait的论文很好地整理的类的两种截然相反的作用。一种是用于创建实例的作用,它要求类是全年的、包含所有必需的内容的、大的类。另一种作用是作为再利用单元的作用,它要求类是按功能分的、没有多余内容的、小的类。参见:Nathanael Schaerli, Stephane Ducasse, Oscar Nierstrasz, Andrew Black. "Traits: Composable Units of Behaviour", 2002.

p216: Perl 6也引入了和Trait类似的概念,叫做Roll。PHP也从5.4版开始引入了这种功能。Ruby在2.0版引入了mix method, 可以像Trait一样操作模块。

[2019-01-14]

猜你喜欢

转载自blog.csdn.net/thinktalk/article/details/86473347
今日推荐