2009年3月12日星期四

横向->纵向

时间过得真是很快,没想到上次发日志都是两个月之前的事情了,还是当时凑出来的那个打印自己的程序

两个月之间发生了不少事,总之比较乱,不过还好现在逐渐走上正轨了,渐渐明白人的精力是有限的,要承认自己的局限性,或者说,有些事情,确实做不到~

感慨一下,也许自己的IQ高于平均值吧,但是EQ几乎为零,哈哈,看来还是专注于技术吧,感情这种烂糟东西,实在是不会玩啊,呵呵

这周的学习主题是Regex,谈到了学习主题,Topic或是Theme,这是之前没有的一个概念,也就是说,开始从以前的横向学习转为纵深学习了,hoho~

简单的说就是以前研究的东西太广了,语言从C到C#的实现,基础学科也不落下,外加经管类的图书,形成了一个尴尬的局面,就是什么都知道,但什么都知道的不多,有必要解决一下这个局面

就从Regex开始吧,这玩意还是挺有意思的,一个不点的式子,有时可以顶的上几百行代码,而且巨Beautiful,及其Graceful

资料是Jefferey E.F Friedl 的 Mastering Regex 3rd Edition,是我一年前买的书,汗一下,一年前仅仅是看过第一章而已~~

看到第五章后,MS作者对Perl也颇有研究,但这本书并没有对Regex背后的灵魂:NFA和DFA作过多的阐释,只是用了两个Metaphor,电车和机车,虽然个人感觉不是多好的比喻,不过也很不错了,毕竟要深究这两项东西就得看Compiler Principle了,那玩意就太扯了,一个SLR就有够折腾了

总之这本书写的还是很浅显的,虽然现在只看到第五章,不过估计看完前六章,再把.Net的Regex熟练,基本上就可以成为一个中级Regexer了,哈哈,毕竟在看这本书之前,我可是构造了一个自动检查Log语法的Regex Engine了呢

具体的书评等看完书之后再写

2009年1月7日星期三

14行 SelfPrinting

using System;
public class WriteMyself{static void Main(){string[] stat1=new string[8];string[] stat2=new string[8];char c=(char)34;string s=c.ToString();
stat1[0] = "using System;";
stat1[1] = "public class WriteMyself{static void Main(){string[] stat1=new string[8];string[] stat2=new string[8];char c=(char)34;string s=c.ToString();";
stat1[2] = "for (int i = 0; i < 2; i++) { Console.WriteLine(stat1[i]); }";
stat1[3] = "for (int i = 0; i < 7; i++) { stat2[i] = c + stat1[i] + c; }";
stat1[4] = "for (int i = 0; i < 7; i++) { ";
stat1[5] = "Console.WriteLine(stat1[{0}] = {1};, i, i==5?stat2[i].Insert(19, s).Insert(37, s): stat2[i]); ";
stat1[6] = "}for (int i = 2; i < 7; i++) {Console.WriteLine(i!=5?stat1[i]:stat1[i].Insert(18,s).Insert(36,s));}}}";
for (int i = 0; i < 2; i++) { Console.WriteLine(stat1[i]); }
for (int i = 0; i < 7; i++) { stat2[i] = c + stat1[i] + c;}
for (int i = 0; i < 7; i++) {
Console.WriteLine("stat1[{0}] = {1};", i, i==5?stat2[i].Insert(19, s).Insert(37, s): stat2[i]);
}for (int i = 2; i < 7; i++) {Console.WriteLine(i!=5?stat1[i]:stat1[i].Insert(18,s).Insert(36,s));}}}

2009年1月3日星期六

2009 Looking Forward

这些东西先放在这里了,自己是很不喜欢做计划的,不过还是写个目标之类的东西放在这里,等到09年末的时候再回头看看究竟完成度如何吧,哈哈。

我对书是非常看重的,所以这摊目标里的书的比重很大

需要反复看的书:
1, CLR via C# 2nd
2, The Practice of Programming
3, Data Structure and Algorithm Analysis in C/C++
4, Programming Pearls 2nd
5, Refactoring
6, Code Complete 2nd
7, The C++ Programming Language
8, How to solve it
9, Design Pattern Explained
10, Understanding .Net 2.0
11, Framework Design Guidelines

需要彻底看完的书:
1, Modern Operating System 2nd
2, Principle of Microeconomics
3, The Art Of Computer Programming Vol.1 (传说中的TAOCP,囧)
4, TOEFL Vocabulary
5, Computer Networks 4th
6, Agile Software Development C#
7, What is mathematics
8, C++ Primer Plus 5th
9, C Primer Plus 5th
10, Mastering Regex 3rd
11, C Programming Language 2nd

需要看一部分的书:
1, Introduction to Algorithms
2, Principle of Macroeconomics
3, Introduction to Psychology
4, C# Cookbook 2.0
5, Assembly Language
6, Programming Language Pragmatics
7, The Art Of Computer Programming Vol.3
8, Pro .Net 2.0 XML
9, Global History 3rd
10, Structured Computer Organization 5th

需要学的一些新技术:
1, ASP.Net
2, Javascript
3, Ajax

一些目标:
1, Exhaust Visual Studio 2005 Compiler
2, Mastering .Net via C#
3, Mastering Data Structure and Algorithms
4, Understanding Computer Organization
5, Understanding Operating System
6, Over 18000 English words
7, Fake 720

2008年12月7日星期日

自己对程序员的分级

自己闲的无聊为计算机专业的学生定的级别,从D一直到X,从低到高,偶是C系程序员,故编码都是C系的技术,J系的类比就是了,呵呵

D-:
知道一些计算机的基本操作,能够说明白内存和硬盘的区别,知道CPU既有Intel的,也有AMD的,上网很多,但没有信息抓取和搜索能力,只是被动的接收信息。

D:
可以比较熟练用某种语言编写一个"Hello World",了解程序控制结构,可以通过一堆if、while、for、switch,在一个方法(函数)里实现一个基本的功能,如果代码通不过编译的话会认为是编译器出错,如果代码通过编译而且正巧结果还对,就认为已经完成程序了。知道栈和队列这些基本数据结构的概念,但是无法完美的去用某种语言实现。

D+:
知道程序不仅仅可以在一个方法体里写,有了初步的函数的思想,可以完成一些行数较多的程序(大于200行),不过这个程序一般只有编译器和他本人能看懂(比如string s1,s2,s3;这样的语句)。终于开始相信自己有时也会犯错误的(对编译器多了点信任),写完了程序会做一点基本的测试,不过输入的数据都是比较"完美"的数据,认为没有必要用Dirty Data(脏数据)进行测试,这样只会给自己增加烦恼。

C-:
对数据结构,操作系统这样的基础课程有了一个初步的认识,可以在20分钟内写一个带有Bug的二分搜索或是含糊的描述出来进程和线程的区别。具备了"分治"(Divide and conquer)和递归(Recursive)的初步思想。知道什么是Pascal风格和camel风格,听说过Hungarian命名法,不过只是偶尔的在自己的代码去运用这种命名法。知道了程序是会抛出异常的,所以就用try/catch all这样的风格去写程序,认为没有异常的程序就是好程序

C:
具备了一定的代码量(超过5k行),至少对某一门主流的编程语言有了一定的认识,开始从结构化编程向对象化编程转变,可以从容的说出OO三特性是封装(Encapsulation),继承(Inheritance)和多态(Polymorphism),但是却很难为多态给出一个精准的定义。在写中型程序的时候会有意的建立一个class,然后"疯狂"的去丰富这个class的功能并最终完成一个典型的GodClass。认为数据结构操作系统这类的课程是在浪费时间,反正我们有的是类库,现成的List/Hashtable/Queue满天飞,需要用的时候引一下就是了。开始用搜索引擎协助自己编写代码,并会比较熟练的使用断点。

C+:
具备了一定的代码量(超过15k行),对某一门主流的编程语言有了比较深入的认识,拿C#来说,可以描述出中的属性(Property)的实现方式,指出值类型和引用类型的五个区别或是给出十个不同类型的Exception,知道有些数据存储在线程栈(Thread Stack)上而有些数据存储在托管堆(Managed Heap)上而存取它们的速度是很不一样的,拿C++来说,可以精确的给出常量指针和指针常量的区别及各自的用途,或是描述C++中实现多态的方式(VTable)。开始有意的去使用多态的思想编写程序但并没有意识到这样做的实际好处,开始使用一些接口(interface)并试图将具有同样行为的类拉拢在一起。

B-:
具备了一定的代码量(超过30k行),对某一门主流的编程语言有了深入的认识,拿C#来说,可以精确的描述出new操作符的具体的行为(计算类型到基类型需要的空间并分配这部分内存,初始化类型对象指针和同步块索引,调用类型的实例构造器创建实例,返回实例的一个引用),清楚的知道该什么时候使用struct而什么时候该使用class。开始在程序中应用设计模式的思想,编写代码的过程中将思考的问题更多的停留在代码的易读性和高效性的抉择上。能够比较有效的利用网络资源去获得自己需要的信息(尤其是熟练的使用英文的搜索引擎)。

B:
具备了一定的代码量(超过50k行),在对一门主流的编程语言有了深入的认识的基础上,在数据结构和操作系统等计算机基础课程的也有了一定的造诣。例如可以轻松的给出6种不同的排序算法并可以在段时间之内将它们以某一种语言实现,也可以在1分钟之内写出一个准确的二分搜索算法。可以在短时间内理解某个API,然后在这个API的基础之上根据自己的需求写一个更加实用的Wrapper。很自然的应用Factory Method去替代Constructor。熟悉异常的使用,拿.Net来说,既知道FCL多数常用的方法会在什么情况下抛出什么样的异常而在什么情况下不会抛异常,也知道在自己编写的类库中什么时候应该抛出什么样的异常。

2008年11月16日星期日

CLR via C#学习笔记(6)--强制类型转换

CLR中很重要的特性之一就是类型安全性,也就是说,在运行的时候,CLR总是知道一个对象的类型。如果想知道一个对象的确切类型,可以去调用GetType方法,由于这个方法是非虚方法,不可被重写,所以一个类型不能伪装成另一种类型。

开发人员经常需要将一个对象从一种类型转换成其它类型。CLR允许将一个对象强制转换成它的类型或者是它的任何基类型。CLR中的每种编程语言都规定了具体如何将转型操作揭示给开发人员。比如说,C#不需要任何特殊的语法就可以将一个对象转换成任何它的基类型,而C#要求开发人员将对象转换成它的任何派生类型这个操作必须是显式的,因为这种类型有可能在运行时失败。

internal class Employee
{...}

public sealed class Program
{
public static void Main()
{
//不需要转型,因为new返回一个Employee对象,而Employee是Object的派生类型
Object o=new Employee();
//需要转型,因为Employee从Object派生
Employee e=(Employee)o;
//下面的操作可以通过编译,但是在运行时会抛出InvalidCastException异常
//因为dt既不是Employee类型,也不是Employee的派生类型
DateTime dt=DateTime.Now;
Employee e1=(Employee)dt;
}
}

在C#语言中进行强制类型转换的另外一种比较"温和"的方式是使用is操作符,is操作符检查一个对象是否兼容于指定的类型,并返回一个Boolean值。之所以说它比较"温和",是因为is操作符不会抛出异常。is操作符经常这么使用:

if(o is Employee)
{
Employee e=(Employee)o;
}

不过上面的代码CLR实际上检查了两次对象的类型,is操作符首先检测o是否兼容于Employee类型,如果是肯定的,那么在if语句内部执行强制转型时,CLR会再次核实o是否引用一个Employee,CLR的类型检查增强了安全性,但无疑也会对性能造成一定的影响。也因为CLR首先需要判断o引用的对象的实际类型,然后,CLR会遍历继承层次结构,用每个基类型去核对指定的类型(Employee)。由于这种编程模式很常见,所以C#提供了as操作符,目的就是简化这种模式的代码写法,同时提升性能。

Employee e=o as Employee;
if(e!=null)
{
//Using e
}

as与is一样,它不会抛出一个异常——只要对象不能成功转型,它的结果就是null。当然如果不加判断直接使用最终生成的引用的话,可能会造成一个System.NullReferenceException异常。

2008年11月12日星期三

设计模式解析-学习笔记(2)--功能分解

编程中有一种很重要的思想,就是分而治之(Divide and Conquer),N多的算法都采取了这种思想,比如Merge Sort和Quick sort,以及对Binary tree的递归前中后的Traverse。套用到数学公式里,比如说工作F的内容包含F1和F2,那么F1+F2小于F,也就是说将F1和F2分开处理的难度要比直接处理F的难度要小。

在软件开发中也常用到这种思想,不过这里叫做功能分解(Functional decomposition),比如说,如果给你一个任务,需要编写一段代码,访问数据库中存储的形状然后将其显示出来。按照正常所需要的步骤来思考是一种很自然的选择,比如说,你可能会这样分步骤解决这个问题:

1)在数据库中找到形状列表
2)打开形状列表
3)按某种规则将列表排序
4)在显示器上显示各个形状

然后就是逐步细化的过程,比如说步骤4可以被分解为下面的步骤:

4a)识别形状的类型
4b)识别形状的位置
4c)调用相应的方法,以位置作为参数,来显示形状

这就是一种很典型的"功能分解"的思想,因为分析人员把一个大的问题分解成了多个相对小的功能步骤(这些步骤就构成了这个问题的解决方案)。一般人都会这么做,因为直接解决整个问题的难度往往要比逐个解决子问题的难度要很多。

然而功能分解方法存在一个很大的问题:它通常会导致一个"主"程序负责控制子程序,当然这是将功能分解为多个子功能的自然结果。但是,主程序所承受的责任太多了,它不仅要确保一切子功能的正确工作,而且还要协调各个函数并控制它们的先后顺序,因此经常会产生非常复杂的代码。如果让一些子函数自己去负责自己的行为,并且可以告知主函数它们要去执行哪些任务,这种方式要比功能分解的方式容易得多。这就是委托(Delegation)的思想。

功能分解的另外一个问题在于:它很难应对未来可能出现的变化以及对代码进行合理的改进。变化是无法避免的:新功能的增加和新模型的引进都会导致代码发生变化。如果把实现各个步骤的所有逻辑代码都放在一个大模块中的话,那么这些步骤中哪怕是有一处变化,都会导致对整个模块进行修改。其实最麻烦的是不在于修改代码,而是在于变化往往会产生各种各样的Bug,也正如本书作者Alan Shalloway所提到的,许多Bug都源自与对代码的修改。

而且,无论事先的分析做的怎么好,也是无法从用户那里获得所有需求的,因为未来的变数太多了。因此对于阻止变化,我们是无计可施的,但是幸运的是我们对变化本事并不是无能为力的。

2008年11月8日星期六

计算机组成学习笔记(5)--RISC 和CISC

人们很早就发现,虽然复杂的指令单条执行的时间要长一些,但是却能使整个程序运行的更快,原因在于:执行指令的步骤有时可以重叠,或者用不同的硬件并行执行。所以高性能计算机的指令一般要多一些。到了20世纪50年代后期,当时占统治地位的IBM意识到支持一个所有计算机都执行相同指令的计算机序列是十分重要的,无论是对IBM自己还是IBM的用户都具有很多的优势,于是IBM引入了体系结构这个属于来描述这一个层次的兼容性。他们希望同系列的计算机的体系结构相同,可以用不同的实现的方法来执行同样的指令,只是在价格和速度上有所不同。

但是如何是低成本的计算机可以执行高价高性能的计算机的复杂指令呢?答案就是Cambridge的Wilkes在1951年提出的关于解释的理念。这个理念成就了IBM System/360体系结构的一系列兼容计算机,从通过解释来执行复杂指令的低端机到不通过解释直接由硬件实现复杂指令的高端机,IBM形成了一条完备的产品线。

解释执行指令的简单计算机有很多的优势。首先,可以在解释过程中改正指令实现的错误,甚至是补偿基础硬件中的设计缺陷;其次,可以以最小的代价来增加新的指令,甚至是在计算机发货之后也可以做到这一点;再次就是结构化的设计使对复杂的指令有效的进行开发、测试和编制文档成为可能。

到了20世纪70年代的后期,人们发现几乎所有的复杂的指令都可以被解释,设计者于是开始试图弥补高级语言和机器语言之间的“语义代沟”,几乎没人想到设计一台简单点的计算机,就像现在很少人去研究设计功能简练些的操作系统一样。

到了80年代,Berkeley的一个研究小组开始研究不用解释器的超大规模集成电路CPU芯片,并首次创造了RISC这个概念,并将他们的CPU命名为RISC I,接着又推出了RISC II。这种处理器和当时的商业处理器有着很大的区别,新的CPU并不存在与过去的产品间的兼容性问题,而且它可以自由选择新的指令集从而最大限度的提高系统的整体性能。由于设计的初期就强调选用可以快速执行的简单指令,大家很快意识到设计出可以快速启动的指令才是提高性能的关键,每秒钟能启动的指令的条数要比单条指令执行时间更重要。

实际上RISC是Reduced Instruction Set Computer的缩写,而与之对应的CISC则指代Complex Instruction Set Computer。RISC的支持者认为 即使RISC需要4条或5条指令来完成CISC的一条指令的功能,但是如果RISC能比CISC快10倍的话(因为RISC并不需要解释执行),那么还是RISC的速度占优。另外,随着主存的速度越来越快,解释执行的代价已经高了很多。

也许有人会想,依靠RISC在速度上的优势,RISC机(Sun的UltraSPARC)应该已经在市场上超出CISC机了(Intel的Pentium),但事实上并非如此,这是为什么呢?

首先由于向后兼容的问题,要考虑到N多的公司在Intel系列的CPU上投资的数十亿美元的软件,同时要注意到,Intel在它的CISC架构中也采用了RISC的思想,从486开始,Intel的CPU就包含了RISC核心,能在单个数据通路周期中执行一些最简单也是最常用的指令,同时复杂的指令还是用CISC的方式执行的,这样一来,常用的指令执行速度很快,而不常见的指令执行就会慢一些。显然这种混合的方案不如纯RISC方案快,但是它却可以在不加修改旧程序的前提下给出极具竞争力的整体性能。