为什么说选择正确的编程语言很重要,以及如何正确的选择

编程语言书籍


几个月前,一个同事问我,应该如何选择编程语言,或者有没有什么固定的选择模式,当时我便打算写点什么。上周在硅谷开会,这我是第一次跟“hack3rs”的创业狂以及技术狂们打交道。我学会了很多前所未闻的脏话,也有所得–即便是追求精简的初创企业也倾向于把问题过份复杂化。

将真正领悟精简精神的人甄别出来并不困难。谷歌,Facebook以及Akamai的程师们的讲座魅力十足。他们从一个更宏观的角度思考和解决问题。这跟公司的财力,规模没有关系,他们特意剪除细枝末节,以便将注意力集中在问题的根本。

我自己也曾一味要求手下考虑使用高级编程语言甚至全面向对象语言,我发现许多的新时代初创企业也还没领悟其精髓。他们用Javascript、Python和Ruby编程,却不明白为什么要用这些语言。

不可否认,把循环写得紧凑或者避免使用模板固有其道理。但如果这是你选择一门编程语言的唯一理由,那么你就大错特错了。日常工作中,与其用基于深度优化的向量化C++语言构建的多核并行异步map-reduce架构去做一个卷积离散傅立叶变换(correlation-DFT),我宁愿用BASIC来做一个快速傅立叶变换(FFT)。

那么到底应该根据什么来选择编程语言呢?唯一检验标准:是否言而达意。

抛开语言的执行效率和功能等等不谈,一门语言必须能够让你描述自己的意图,不光是对编译器而言,更是对未来的读者而言。我相信软件维护中99%的问题都是由于最初写代码的人没能准备表述他们的意图造成的。如果言不达意,文档就不叫文档。如果言不达意,UML图就不是UML图。如果无法描述某种数据型适用于哪些操作符的话,面向对象编程就不是面向对象编程。言而达意不是指C风格的ModifyWindowEx(HWND wnd)不易读而Window.modify()告诉了你和编译器这个window可以和不可以做什么。关键是要表明你的意图。

Fortran如今已大大落后,因为它用下面这种方式描述一个算式:

MOV AX, $5D
ADD AX, $6F
MOV $7F, AX

其实完全可以写成这样:

c = a + b

如此你就知道是a加上b,结果存到c,即便你不懂计算机也能看懂。

一个常见的误解是:函数式编程语言表达你要什么(what you want)而命令式编程语言表达你想怎样(how you want)。

这是一种糟糕的理解。因为有时候“你想怎样”恰恰是你想表达的意思。

按照我一贯的博文风格,请你问自己一个基本问题,当面临语言的选择时:

“我是否把意思说清楚了?”

如果你无法回答这个问题,那么你没有用最佳语言。如果你不得不写文档或者做注释,这说明你的代码没能描述你的意图。看看这个函数原型:

char* reverseString(const char *foo);

在缺少关于空指针,空字符串以及其他异常处理文档的帮助下,根本没法理解作者到底想干什么。这不太好。当然,函数内部可能对输入做了无数的验证,但你必须写一堆针对各种特定输入的单元测试以确保你的假设是正确的。

我所指的“把意思说清楚”是什么意思呢?假设C++在原型中支持以下虚拟语法:

char* @Nullable reverseString(@NonNullable const char *foo);

函数原型中加上这些注解有两个好处:

1. 你不需要事先测试foo是不是null。编译器保证会给你一个非null。

2. 明确地告诉调用者你不容忍null。这种表述方式编译器能够明白,优秀的静态分析工具可以检测到这类bug,这是C语言做不到的。

虽然这看起来只不过是增强了一下语法,实际不仅如此,它还增强了语义。如此不论是人或是机器就明白foo这个变量不可为null,否则函数很生气,后果很严重。而且,你给这个函数划定了界限,再不用担心foo可否为null了。

函数式编程并不是万金油:

大家对我的另外一个常见误解是我推崇纯函数式语言。我的确有理由喜欢它们。看到上面那个式子了吗?

c = a + b

如果我想把expr1和expr2的值相加该如何表达呢?

c = (expr1) + (expr2)

如果expr1有附加操作而且会影响expr2的值又该如何表达呢?这并不罕见:

c = (a++) + (a + b);

这里的问题不是你想的那样。我知道你在想什么:“天知道这门语言会如何解释这个式子。万一计算的顺序反了怎么办?”

你想错了。正是由于人们会产生那样的想法,编程语言才会有这样的特点。要解答你的疑问很简单,看看编译手册就知道了。

上面式子的根本问题是我无法知道那样的计算顺序是偶然的还是有意的。我确切地知道上面式子的会做什么,但我无法确定的是,它的计算顺序是不是有意的?我能不能优化那个式子,放到一个循环里去?我能不能在多核多线程的情况下调用它?假设有人问我,如果给z赋值10而不是20,会不会影响c的值,我无法回答。

理论上是无法回答上面那个问题的。当然了我们可以根据经验做加一些断言(assertion)。在断言出了一堆或者一个警告后,理性地说,我们仍然不知道z会不会影响a或者b,最终影响到c。

为什么这很重要

代码的可维护性是建立在代码的可阅读性的基础上的。你知道为什么CSS不好吗?如果仅仅是程序员写错了或者设计者把字体和布局规则混淆了,地球人都知道那还不算太坏。CSS坏就坏在如果不加上大量的注释,人们就无法通过字面上的意思来理解代码的意图。

别忘了基于规则的声明式语言并不是新概念,更不是革命。50年前Prolog就提供了类似CSS的声明方式。今天的Erlang也提供了这类方式,并在业界得到广泛应用。

请看下面这行代码:

div .title #subtitle {color: blue}

如果不加载试一下的话,我敢打赌你完全想不到这会对页面产生怎样的效果。字面上完全看不出跟其它规则的关系,也看不出它如何处理匹配冲突。

因此对于汝等Ruby/Python/Node.js程序员而言,我的建议是,如果你真想超凡脱俗的话,学学谷歌和Facebook。他们使用一些实验性技术,并不是为了取代for-loops,而是用来表明for-loops的意图。快速原型的话选择简单的语言就可以了,当需要准确描述意图的时候才考虑更换编程语言。

命令式语言的必要性:

最后,我想解释一下为什么命令式语言是必要的。看看下面这个驱动程序例子:

setlpt1(00000000b);setlpt1(00010000b);setlpt1(00000000b);

这是我假想的串口命令协议。这几行代码是按照先后顺序排列的。哪怕200年以后,它们的意图也不会发生什么变化。必要的时候使用命令型语言,明确地告诉读者不要打乱这些代码。你不应该改变它们的顺序。你也不会把他们用在某些抽象的端口上,它们只适用于串口或者所谓打印机口。

用函数式语言来实现上面的功能,并且加上同步原语来保证它们按照顺序运行,是愚蠢的。

结论:

如果说这篇文章有一点点值得总结的东西的话,那便是:下次你写任何代码/规范/程序的时候,问问自己,意图是否清楚表达?未来的维护者看到你写的东西,是否能明白它


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/447351.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

细数开源历史上的十个重大事件

开放源码(开源)的精神在于使用者可以使用、复制、散布、研究和改进软件。这可以追溯到20世纪60年代,至今已有半个世纪了。虽然下面所列举的不都是专门的开源产品,但还是在开源发展的进程中有着巨大的影响。开放源码(开…

深度卷积神经网络CNNs的多GPU并行框架及其应用

摘要:本文是腾讯深度学习系列文章之一,主要聚焦于腾讯深度学习平台(Tencent Deep Learning Platform)中深度卷积神经网络Deep CNNs的多GPU模型并行和数据并行框架。 【编者按】深度卷积神经网络有着广泛的应用场景,本…

线程安全和对应的核心概念

线程安全 线程安全的概念:当多个线程访问某一个类(对象和方法)时,这个类始终都能表现出正确的行为,那么这个类(对象或者方法)就是线程安全的synchronized:可以在任意对象及方法上加…

JDK Unsafe类的使用与CAS原子特性

JDK Unsafe类的使用与CAS原子特性 Java.util.concurrent.atomic包,其中包含了大量使用到Unsafe这个类Java不能直接访问操作系统的底层,而是通过本地方法来访问。 Unsafe类提供了硬件级别的原子操作,主要提供了以下功能 内存操作字段的定位和…

写软件不是造汽车

写软件和做其他事情是不一样的。当我们制造别的东西的时候——像汽车、玩具、椅子、画作、甚至包括数字产品如平面图片和3D模型——我们做出来的成品就是最终的结果。而开发软件则不是,我们做出来的产品永远不可能有最终的结果——我们需要向计算机解释如何根据任意…

网站盈利的10种方式

如果你有自己的网站,而且已经有了不少的流量,你肯定会开始考虑如何通过这个网站来挣一些钱。 在这篇文章中,我会向大家介绍网站最常见的10种盈利方式。 1.按点击付费广告 在网站上展示一个按点击付费的广告横幅是最简单的盈利方式&#xff…

Redis数据的类型

Redis一共分为五种基本数据类型:String、Hash、List、Set、Zset. string 内部编码有三种,raw,embstr,int String 是二进制的。可以存储序列化对象,图片,字符串,数值等 set和get方法 &#x…

Redis高级命令与特性以及单点模式的介绍

高级命令 keys * 返回满足条件的所有key,可以模糊匹配exists 是否存在指定的keypersist 取消过期时间select 选择数据库 (0-15,总共16个数据库)move key index 将当前数据库的 key 移动到给定的数据库 db 当中randomkey 随机返回…

同为程序员 为什么我的工资最低

我看着工资单上每一个开发团队成员的薪水,慢慢地我不能保持淡定了。 而当我看到我的工资排名是倒数的时候——靠近最后一名——我不由得倒抽一口冷气。就像圣诞故事中的那个可爱的小男孩Ralphie ,想买气枪却被忽悠会有危险一样,我也不断忽悠…

redis.conf配置文件详解

基本配置 daemonize no #是否以后台进程启动databases 16 #创建database的数量(默认选中的是database 0)save 900 1 #刷新快照到硬盘中,必须满足两者要求才会触发,即900秒之后至少1个关键字发生变化save 300 10 #必须是300秒之后至少10个关键字发生变…

用Unix的设计思想来应对多变的需求

摘要:无论是Unix设计,还是面向对象设计,还是别的什么如SOA,ECB,消息,事件,MVC,网络七层模型,数据库设计,等等,他们都在干三件事——解耦&#xff…

有关编程的12个猜想

摘要:编程世界的将来如何目前仍难预料,但可以肯定的一点是技术一直在加速发展。本文搜罗出12个独特的编程视角猜想,一起来看看有哪些猜想在不久的将来就能变为现实。 编程世界的将来如何目前仍难预料,但可以肯定的一点是技术一直…

面试中如何剔除“鱼目混珠”程序员?

公司招聘面试事宜是一个耗时耗钱的项目,从挑选简历开始,还要花更多的时间面试候选人。有的时候这些人才机构会向你保证这些人都是Java天才、SQL专家、堆栈开发者等等,但实际上真实情况远不及你想想的。对于一个公司来说,执行招聘面…

java容器相关问题

同步类容器 1,这些复合操作在多线程并发地修改容器时,可能会表现出意外的行为,最经典的便是ConcurrentModificationException,原因是当容器迭代的过程中,被并发的修改了内容,这是由于早期迭代器设计的时候…

趣文:如果编程语言是车

C语言是全能手,小巧,强大,所向披靡,可靠,任何事情都能对付。 C是新的C,双倍的能力,双倍的尺寸,适应险恶的环境,但是你如果没练好就去驾驶,很可能会撞车。 C#是…

开发者应该了解的API技术清单!

摘要:有人说,有API的地方就有App,借助这些API开发者轻松构建出一款应用,极大地提高开发效率和开发质量。文中整理了一份API服务清单,内容涵盖:监控/调试、 CDN 、数据库、仪表盘、支付、通信等方面&#xf…

提高程序员职场价值的10大技巧

如果你已经是个很牛叉的程序员,但是依然觉得觉得还不够的话,欢迎阅读此文。本文旨在帮助各位更上一层楼。 你是不是觉得自己已经掌握了所有的编程技巧?别太自以为是了! 会写代码的确很重要,但是要拿到更好薪水&#…

google python的风格规范

点击链接,查看内容

IT人应当知道的10个行业小内幕

如果你打算从事IT行业或刚进入这个行业,也许本文下面的小内幕会吓到你,因为这些事平常都不会公开讨论的。如果你是IT资深人士,或许你已经遇到其中的大部分了。如果你愿意,请一起来参与讨论吧。 这些内幕大多数是针对网络管理员、…

Volatile原子性一致性JVM指令重排

概念 Volatile概念:Volatile关键字的主要作用是使变量在多个线程间可见。作用: 在多线程间可以进行变量的变更,使得线程间进行数据的共享可见 阻止指令重排序,happens-before package com.example.core.cas;import com.example.c…