为什么开发出了这么多的编程语言?

Python08

为什么开发出了这么多的编程语言?,第1张

C#与JAVA的相同之处:由于C#与JAVA都是基于C++发展起来的,因此二者之间具有很多相似之处,具体如下:

1、C#和JAVA语言的编译结果是独立于计算机和编程语言的,可执行文件可以在受管理的执行

环境中执行;

2、C#和JAVA语言都是采用了自动的垃圾回收机制;

3、C#和JAVA语言都取消了指针操作;

4、C#和JAVA语言都没有头文件;

5、C#和JAVA语言都只支持单重继承,要实现与多重继承类似的功能,必须通过接口来实现;

6、类都是从Object类派生而来,类的对象通过关键字new生成;

7、C#和JAVA语言都支持线程;

8、C#和JAVA语言都没有全局变量和全局函数,所有的变量和函数都属于某个类所有;

9、C#和JAVA语言都支持对数组和字符串边界的严格检查,不会出现边界溢出的情况;

10、C#和JAVA语言都使用“.”操作符,不再使用“->”和“::”操作符;

11、C#和JAVA语言都将null和bool作为关键字;

12、C#和JAVA语言中所有的值都必须先初始化后才能使用;

13、C#和JAVA语言中的if语句都不允许采用整数作为判断条件;

14、C#和JAVA语言中的try语句块都可以后接finally语句块。

C#与JAVA的不同之处:

尽管C#和JAVA有很多相同之处,但是由于二者是两家不同公司开发的高级程序设计语言,它们又相互独立,

自成体系,各自具有一些自己特有的特点,下面将C#与JAVA之间的不同之处如下:

1、属性

对于那些经常使用快速开发工具,如Delphi或者Visual Basic的开发人员来说,属性是一个非常熟悉的概念。

一般来说,通过getXXX可以读取属性的值,而通过setXXX可以设置属性的值。

JAVA中比较常见的属性操作语句: foo.setSize(foo.getSize()+1)label.getFont().setBold(true)

c#中比较常见的属性操作语句: foo.size++label.font.bold=true

很明显,上述的属性设置方式较JAVA来说更为简洁,可主读性也更强。这充分体现了C#简单的特点。

JAVA对于属性的定义:public int getSize(){ return size} public void setSize(int value){ size=value}

c#对于属性的定义进行了简化:public int Size{ get{ return size} set{size=value}}

2、index

C#提供index来给对象加上索引的功能,从而用与处理数组类似的方式来处理对象,JAVA语言则不支持index

C#中定义index的典型方式如下:

public Story this[int index]

{

get{return stories[index]}

set{

if(value!=null){

stories[index]=value

}

}

3、delegate :可以认为是一种类型安全、面向对象的函数指针。

C#使有delegate可以通过一个名字访问不同的函数,它实现和JAVA中的interface类似的功能,但是它比interface更为好用。

4、event

C#提供对event的直接支持,它通过delegate和event关键字实现对事件的处理。event关键字隐藏所有delegate方法,运算符“+=”和“-+”允许程序员自由加入或者删除时间处理程序。

5、enum:枚举用于指定一系列的对象。

C#通过如下语句来定义和使用枚举:

定义:public enum Direction{North,East,West,South}

使用:Direction wall=Direction.North

JAVA不直接支持枚举,如果要实现和C#相类似的功能,必须先定义一个类

public class Direction{

public final static int NORTH=1

public final static int EAST=2

public final static int WEST=3

public final static int SOUTH=4}

在定义了Direction类后,JAVA可以通过引用类中的值来使用枚举:

int wall= Direction.NOTRH

6、foreach语句

C#提供了标准的for循环,同时还提供了foreach语句(从VB中引入)来循环处理集合中的元素。

JAVA遍历集合中的所有元素的典型处理方式如下:

while(!collection.isEmpty())

{

Object o=collection.get()

connection.next()

}

C#遍历集合中的所有元素:foreach(object o in collection){…}

7、统一数据类型:

大多数的高级程序设计语言都有基本数据类型,如整型、浮点类型等。同时,为了更好地满足实际的需要,对不同的数据类型有不同的处理方式,显然,如果能够对简单数据类型的处理和对复杂数据类型的处理结合在一起,并用一致的方式加以处理的话,无疑会大大提升应用程序设计的效率,增强程序设计的灵活性。

JAVA语言在处理基本数据类型的时候也采取分别处理的策略,但是在基本数据类型的基础上提供了一系列封装这些基本数据类型的类,例如:整型(int)被类Integer所封装,双精度浮点(double)被类Double封装。

C#提供了一种和JAVA不同的方式来实现数据类型的统一。事实上,在c#中,即使是int这样的简单数据类型在C#内部也是通过一个结构体Int32来实现的,在C#中,可以这样认为,int只是结构体Int32的一个别名。由于C#中的结构体也继承自类Object,这样,Object类中定义的方法,各个结构体也拥有,于是,在C#中可以通过如下的方式来操作整数:int I=5System.Console.WriteLine(i.ToString())

8、操作符重载

通过操作符重载可以用一种比较自然的方式来操纵各种数据类型,从而大大提升程序的可读性和灵活性。C#中的“==”操作符在Object类中进行了定义,在Object中定义的==操作符通过比较两个值的引用来获得最后的结果。如果使有和集合相关的类,则必须在这样的类中实现ICompar接口,这个接口中定义了一个方法CompareTo,该方法返回两个对象的比较结果,在此基础上,可以进一步定义各个实现比较的操作符,如

“>”、“<”、“>=”、“<=”等。事实上,数字类型(int、long等)可以直接使用这些比较操作符,它们的内部都实现了ICompare接口。

9、多态性

虚似方法提供了多态性的技持。多态意味着派生类可以定义一个和基类中同名的方法。尽管JAVA和C#都支持多态性,但是它们的具体实现方式还是有一定的差别。

在JAVA语言中,默认情况下,基类的对象可以直接调用派生类中的虚似方法,在C#语言中,基类要调用派生类中的虚似方法必须通过virtual关键字来实现。同时,在C#语言中,一个方法要重载基类中的同名方法,还必须通过关键字override来实现。在C#中实现多态的典型程序如下:

Class B{ public virtual void foo{}}

Class D:B{ public overried void foo(){}}

以上只是简单地比较了C#和JAVA之间的异同,事实上,这二者之间的比较远不止上面所介绍的内容,要学好这两种语言,需要经过大量的实践工作,在实践中区分开两种语言

 简言之,不论是面向对象编程还是函数式编程,如果你走了极端,那都是错误的。面向对象编程的极端是一切都是对象(纯面向对象)。函数式编程的极端是纯函数式编程语言。

面向对象编程的问题

面向对象的问题在于它对“对象”的定义,它试图将所有事情就纳入到这个概念里。这种做法极端化后,你就得出来一个一切皆为对象思想。但这种思想是错误的,因为

有些东西不是对象。函数就不是对象。

也许你会反驳,在Python和Scala语言里,函数也是对象。在Python中,所有的含有一个叫做__call__的方法的对象其实都是函数。类似的,在Scala语言里,函数是拥有一个叫做apply方法的对象。但是,经过认真的思考后,你会发现,它混淆了源祖和衍生物的概念。函数是源祖,包含函数的对象实际是衍生物。__call__和apply它们自身首先就是要定义的所谓“函数对象”。Python和Scala实际上是绑架了函数,把它们监禁在“对象”里,然后打上“__call__” 和 “apply” 标签,把它们称作“方法”。当然,如果你把一个函数封装到对象里,你可以像使用一个函数那样使用对象,但这并不意味着你可以说”函数也是对象“。

大多数的面向对象语言里都缺乏正确的实现一等(first-class)函数的机制。Java语言是一个极致,它完全不允许将函数当作数据来传递。你可以将全部的函数都封装进对象,然后称它们为“方法”,但就像我说的,这是绑架。缺乏一等函数是为什么Java里需要这么多“设计模式”的主要原因。一旦有了一等函数,你将不再需要大部分的这些设计模式。

函数式编程的问题

相似的,函数式编程走向极端、成为一种纯函数式编程语言后,也是有问题的。为了讨论这个问题,我们最好先理解一下什么是纯函数式编程语言。出于这个目的,你可能需要阅读一下Amr Sabry先生(他是我的博士导师)的What is a Purely Functional Language。概述一下就是,纯函数式编程语言是错误的,因为

有些东西不是纯的。副作用是真实存在的。

所谓纯函数,基本上就是忽略了物质基础(硅片、晶体等)表现的特性。纯函数式的编程语言试图通过函数——在函数中传入传出整个宇宙——来重新实现整个宇宙。但物理的和模拟的是有区别的。“副作用”是物理的。它们真实的存在于自然界中,对计算机的效用的实现起着不可或缺的作用。利用纯函数来模拟它们是注定低效的、复杂的、甚至是丑陋的。你是否发现,在C语言里实现一个环形数据结构或随机数发生器是多么的简单?但使用Haskell语言就不是这样了。

还有,纯函数编程语言会带来巨大的认知成本。如果你深入观察它们,你会看到monads使程序变得复杂,难于编写,而且monad的变体都是拙劣的修改。monads跟Java的“设计模式”具有相同的精神本质。使用monad来表现副作用就像是visitor模式来写解释器。你是否发现,在很多其它语言里很简单的事情,放到Haskell语言就变成了一个课题来研究如何实现?你是否经常会看到一些有着诸如“用Monadic的方式解决一个已经解决的问题”这样标题的论文?有趣的是,Amr Sabry先生一起合著了这样一篇论文。他试图用Haskell语言重新实现Dan Friedman的miniKanren,但他不知道如何构造这些monads。他向Oleg Kiselyov——公认的世界上对Haskell类型系统知识最渊博的人——求教。而且你可能不知道,Amr Sabry先生应该是世界上对纯函数编程语言知识最渊博的人了。他们在 Oleg 的帮助下解决了疑难后一起合著了这篇论文。讽刺的是,Dan Friedman——这个程序的原作者——在使用Scheme语言开发时却没有遇到任何问题。我在Dan的代码基础上重新实现了miniKanren,增加了一个复杂的负操作。为了实现这个,我需要使用约束式逻辑编程和其它一些高级的技巧。鉴于用Haskell语言重写基本的miniKanren将两位世界级程序员都难倒了的事实,我不敢想象如果用Haskell的monads如何能实现这些。

有些人认为monads的价值在于,它们“圈定”了副作用的范围。但如果monads不能真正的使程序变得易于分析或更安全,这种“圈定”有什么用呢?事实上就是没用处。本身就跟副作用一样难于分析理解。没有一种东西可以说monads能使其简单而静态分析办不到的。所有的静态分析研究者都知道这点。静态分析利用了monads的本质,但却去除了程序员编写monads代码的负担——而不是增加负担。当然,过度的副作用会使程序很难分析,但你也可以使用C语言写出纯函数,例如:

int f(int x) {

int y = 0

int z = 0

y = 2 * x

z = y + 1

return z / 3

}

你用汇编语言也能做到这些。纯函数并不专属于纯函数式编程语言。你可以用任何语言写出纯函数,但重要的是,你必须也应该允许副作用的存在。

回首历史,你会发现,数学上的理想主义是纯函数编程语言的背后推动力。数学函数简单漂亮,但不幸的是,它们只是在你构建原始纯粹的模型时才好用。否者它们会变得很丑陋。不要被“范畴论”等标语吓倒。我对范畴论了解很多。即使是范畴理论学家自己也称其为“抽象无意义”,因为它们基本上就是用一种怪诞的方式告诉你一些你已经知道的事情!如果你读过Gottlob Frege的文章Function and concept,你会吃惊的发现,在他的这篇论文前的大多数数学家都错误的理解了函数,而这仅仅是刚刚100多年前的事。事实上,数学语言上的很多事情都是有问题的。特别是微积分方面。编程语言的设计者们没有理由要盲目的学习数学界。

不要盲目的爱上你的模型

无论任何事情,当走向极端时都是有害的。极端化时,面向对象编程和函数式编程都试图把整个世界装入它们的特有模型中,但这个世界是在完全不依赖我们的大脑思考的情况下运转的。如果以为你有一个锤子,就把所有东西都当成钉子,这明显是不对的。只有通过认清我们的真实世界,才能摆脱信仰对我们的束缚。

不要让世界适应你的模型。让你的模型适应世界。