精华内容
下载资源
问答
  • 首先一个总体的理解是:声明式编程强调做什么,命令式编程语言强调怎么做。 声明式编程语言 典型的语言 SQL, HTML 声明式编程是计算机科学中的一种编程范例,在不描述控制流程的情况下表示...

    在模型转换语言的学习过程中,在文献中总能看到声明式编程语言( declarative language)与命令式( imperative language)编程语言。查阅了资料之后,在这里总结一下自己的理解。
    首先一个总体的理解是:声明式编程强调做什么,命令式编程语言强调怎么做。

    声明式编程语言

    典型的语言有
    SQL, HTML

    声明式编程是计算机科学中的一种编程范例,在不描述控制流程的情况下表示计算的逻辑,让编程人员专注于业务上来,而不是具体的实现步骤,有些接口的味道。
    声明式编程是在命令式编程之上的抽象,因此更具有可读性
    Regex正则表达式是声明式编程语言的典型例子,因为其执行模式时是完全不能追踪的,只能最终的结果。
    函数式编程是生命式编程的子集

    命令式编程语言

    典型的语言有

    C, C++, Java

    此外还有混合式编程语言
    典型的语言有Python, JavaScripe, C#

    函数式编程

    展开全文
  • bl:为娱乐而创建的简单命令式编程语言
  • 一文解决现代编程语言选择困难:命令式编程

    万次阅读 多人点赞 2021-04-07 16:22:41
    如何了解某种编程语言的优缺点?某种编程语言是否适用于我的项目?面对此类问题,如果求助于搜索引擎,输入“最佳编程语言”,结果会罗列一堆文章,涵盖 Python、Java、JavaScript、C#、C++、PHP 等,并且大多对各...

    本文转载自 InfoQ,作者:Ilya Suzdalnitski 翻译:盖磊

    如何了解某种编程语言的优缺点?某种编程语言是否适用于我的项目?面对此类问题,如果求助于搜索引擎,输入“最佳编程语言”,结果会罗列一堆文章,涵盖 Python、Java、JavaScript、C#、C++、PHP 等,并且大多对各语言的优缺点表述得模棱两可。我本人很不喜欢去读此类文章,因为其中许多部分内容的表述并不到位,缺少实战借鉴意义,而且行文生硬。因此我试撰本文来做一次深入总结,去伪存真。

    本文概述了当前广为使用乃至可能小众的现代编程语言,力图做到尽量客观、值得一读,并不带个人偏见。各语言按推荐程度从低到高依次列出。

    谨记,并不存在所谓完美的编程语言。有的语言非常适用于后端 /API 开发,而有的语言则非常适用于系统编程。

    文中按当今两大通用语言家族分类,即 C 衍生语言家族和元语言(Meta Language,ML)衍生语言家族。

    对于开发人员而言,编程语言只是工具箱中的工具,更重要的是如何选择合适的工具去完成工作。我衷心希望本文有助于读者选取适合自身项目的编程语言。做出正确的选择,可降低数月甚至数年的开发工作量。

    哪些编程语言特性值得关注?

    很多编程语言排行榜文章,主要对比的是语言使用广泛度、开发人员收入期望值等因素。在软件领域,虽然大规模的社区和生态系统的确有所裨益,但语言的使用情况并非好的排行指标。本文另辟蹊径,采用的评判依据主要考虑语言的强大之处和不足之处。

    为表示所列语言的推荐程度,文中使用“赞”(👍)、“否”(👎)和“尚可”(👌,即不赞也不否)三种 emoji。

    那么应该比较哪些特性?换句话说,除了语言使用广泛性,还有哪些特性更能代表语言的受欢迎程度?

    类型系统(Type System)

    类型系统倍受大量开发人员的青睐,这也是为什么 TypeScript 之类的语言日渐大行其道。在我看来,类型系统去除了大量的程序错误,更容易实现重构。但是否具有类型系统,只是本文考虑的部分评判因素。

    支持类型系统的编程语言,最好同时具备类型推断(type inference)。一个好的类型系统,不用明确地标出函数签名(function signature),也能支持对大部分类型的推断。不幸的是,大多数编程语言提供的仅是基本的类型推断功能。

    更进一步,支持代数数据类型(Algebraic Data Types,ADT)的类型系统评分更高。

    强大的类型系统,还应支持高级类类型(higher-kinded types)。高级类类型是对泛型(generics)的更高阶抽象,支持编程人员在更高的抽象层上编程。

    尽管大家对类型系统寄予厚望,但还有一些比静态类型(static typing)更重要的特性。因此在选择一门编程语言时,不能只看是否支持类型系统,

    学习难度

    即便编程语言是完美无瑕的,如果一位新手上船需要前期投入数月甚至是数年的精力,那么又会有多少人使用呢?另一方面,很多编程范式需要数年的时间才能逐渐完善。

    好的编程语言需对新手友好,掌握它们不应花费大量学习时间。

    空值

    我将 1965 年创建的空值引用(null reference)称为“亿万美元错误”。当时,我正设计首个完全类型系统,用于面向对象语言中的引用。目标是确保所有对引用的使用是绝对安全的,并由编译器自动执行检查。我无法克制添加空值引用的诱惑,完全因为空值引用非常易于实现。近四十年来,这一设计导致了不计其数的错误、漏洞和系统崩溃,可能造成了数十亿美元的痛心损失。

    — 空值引用的创立者 Tony Hoare

    为什么说空值引用是不好的?因为空值引用破坏了类型系统。一旦默认为空值,那么就不能依靠编译器检查代码的有效性。任何空值都是一枚随时可能引爆的炸弹。如果没能想到所使用的值的确为空值,那么会产生什么后果?会出现运行时错误。

    function capitalize(string) { return string.charAt(0).toUpperCase() + string.slice(1);}capitalize("john"); // -> "John"capitalize(null); // 未捕获类型错误:不能读取为空值的属性“charAt”。

    为确保所处理的值并非空值,开发人员必须对运行时做手工检查。即使是静态类型语言,空值引用也破坏了类型系统的很多优点。

    function capitalize(string) { if (string == null) throw "string is required";return string.charAt(0).toUpperCase() + string.slice(1);}

    运行时检查也称为“空值防护”(null guards),在现实中可归为一种不良的编程语言设计。一方面,引入样板代码破坏了编程风格。更糟的是,它并不能确保我们是否检查了空值。

    好的编程语言,应在编译时做类型检查,判断值的存在与否。

    因此,支持空值检查机制的编程语言应加分。

    错误处理

    捕获异常并不是一种好的错误处理方式。抛出异常本身没有问题,但仅适用于程序没有办法恢复而必须崩溃这类异常情况。异常和空值一样,会破坏类型系统。

    如果将异常作为错误处理的首选方式,那么就无法获知函数是返回了期望值,还是发生了故障。抛出异常的函数也无法实现复合(Compose)。

    function fetchAllComments(userId) { const user = fetchUser(userId); // 可能抛出异常。const posts = fetchPosts(user); // 可能抛出异常return posts // posts 可能为空值,这会再次导致异常。.map(post => post.comments) .flat();}

    无法获取部分数据而导致整个程序崩溃,这显然并非一种好的做法。尽管我们不希望发生这种情况,但它的确会发生。

    一种做法是手工检查是否生成异常,但是在编程过程中可能会忘记对异常做检查,因此这种做法是非常不可靠的,而且会在代码中添加大量额外处理。

    function fetchAllComments(userId) {  try {    const user = fetchUser(userId);
        const posts = fetchPosts(user);    
        return posts            .map(post => post.comments)            .flat();  } catch {    return [];  }
    

    目前已有更好的错误处理机制,支持在编译时对潜在错误做类型检查。因此,默认无需采用异常处理的编程语言也应加分。

    并发

    当前业界正处于摩尔定律的末端,即处理器不会再大规模提速。我们身处多核 CPU 时代,所有的现代应用必须能很好地利用多核技术。

    不幸的是,大多数当前在用的编程语言都是设计用于单核计算时代的,本质上并不能有效地支持多核处理。

    一种亡羊补牢的设计,是在后期提供支持并发的软件库。但这只是给语言打了补丁,并非从根本上就针对并发设计,不能称为良好的开发体验。一些现代语言内建了对并发的支持,例如 Go、Erlang 和 Elixir 等。

    不可变性

    我认为大型的面向对象程序,需要解决由于大规模可变对象间关联所导致的复杂图结构。否则在调用方法时,必须得把握并牢记该方法的功能和副作用。

    —— Rich Hickey,Clojure 创建者。

    当前的编程工作中,使用不可变值越来越常见。即便是 React 这样的现代 UI 软件库,也考虑使用不可变值。对支持不可变数值提供一等支持的编程语言,我们会给出更高的评判。这完全是因为不可变性避免了编程中出现许多软件缺陷。

    什么是不可变状态?简而言之,就是数据不会发生改变。例如,大多数编程语言中的字符串。字符串转为大写,并不会去改变原始的字符串,而是返回一个新的字符串。

    为确保任何事情都不发生改变,不可变性对上述理念做了进一步扩展。更改不可变数组,总是会返回一个新的数组,而非原始数组。更新用户名,将返回一个包含更新后用户名的新用户对象,并不改变原始对象。

    不可变状态不做任何共享,因此无需操心线程安全所导致的复杂性。不可变性使得代码更易于并行化。

    不对状态做任何更改的函数,称为“纯函数”(Pure)。纯函数更易于测试和推断。使用纯函数,无需操心函数体之外事情,可聚焦于函数本身。不用像面向对象编程中那样必须牢记整个对象图,这样极大地简化了编程开发。

    生态系统和工具链

    一种编程语言可能本身并没有多少亮点,但如果其具有大型的生态系统,这会令语言更具吸引力。具备良好的软件库,可以节省数月乃至数年的开发工作。

    显著的例子就是 JavaScript 和 Python。

    速度

    语言的编译速度如何?程序的启动速度如何?运行时的性能如何?所有这些都是影响评判中的考虑因素。

    诞生年代

    尽管并非绝对,通常新推出的语言要比原先的语言更好。只是因为新语言会吸取了前辈的经验教训。

     C++    

    图片

    下面从最糟糕、也可能是计算机科学中最大错误的 C++ 语言开始。当然,我并不认为 C++ 是一种很好的现代编程语言。但 C++ 当前依然得到广泛应用,在此必须提及。

    语言家族:C

    👎 语言特性

    图片

    C++ 可称为糟糕透顶的语言……如果项目局限于 C,意味着不会有任何机会被 C++ 愚蠢的“对象模型”搞砸。

    —— Linux 创立者 Linus Torvalds

    C++ 中填充了各种特性,力图无所不能,但在在其中任何一项上都不能说出色。C++ 支持 goto、指针、引用、面向对象编程、操作符重载,以及各种非生产特性。

    为什么说 C++ 不好?在我看来,最大问题在于 C++ 颇具年头了。C++ 是在 1979 年设计的。在当时设计者缺少经验,关注点发散,虽然所添加的特性在当时看来是似乎好的做法。C++ 得到了非常广泛的使用,这意味着为其中支持各种用例而添加了更多特性,导致特性成堆。

    👎 速度

    C++ 的编译时间出奇的慢,甚至比 Java 慢很多,尽管与 Scala 不相上下。

    但在运行时性能和启动时间上,C++ 程序表现非常优秀。

    👎 生态系统和工具

    图片

    上图的推文给出了很好的解释。C++ 编译器的错误信息对新手并不友好。通常并未指出导致错误的确切原因,需要开发人员花时间查找。

    👎👎 垃圾回收

    我曾希望在 C++0x 标准中至少考虑可选地支持垃圾回收,但这在技术上存在问题。

    —— C++ 的创建者 Bjarne Stroustrup

    垃圾回收从未添加到 C++ 中,而手工内存管理非常易于出错。开发人员必须操心如何手工释放和分配内存。我对使用非垃圾回收语言的经历记忆深刻,其中大量的缺陷在当前支持垃圾回收语言中可轻易避免。

    👎 面向对象编程的失败尝试

    我提出了“面向对象”一词,但并没有没有顾及 C++。

    —— 面向对象编程的创建者 Alan Kay

    面向对象编程是一项很好的技术,出现于上世纪六十年代后期,当时 C++ 刚出现。不幸的是,不同于 Smalltalk 等语言,C++ 在实现面向对象编程中出现了几个致命错误,导致好的理念变成噩梦。

    好的一方面是,不同于 Java,至少在 C++ 中面向对象是可选的。

    👎👎 学习难度

    图片

    C++ 是一种复杂的低层(low level)语言,不具备任何自动内存管理机制。由于特性纷杂,初学者必须花费大量时间学习。

    👎 并发

    C++ 设计用于单核计算时代,只支持简单的并发机制,这还是在近十年中添加的。

    👎 错误处理

    抛出并捕获错误是 C++ 的首选错误处理机制。

    👎 不可变性

    未内置对不可变数据结构的支持。

    👎 空值

    C++ 中所有引用均可为空值。

    评判

    图片

    C++ 的初衷是成为更好的 C 语言,但这一初衷并未实现。

    系统编程是 C++ 的最适合使用场景。但考虑到已具有 Rust 和 Go 等更好、更现代的替代语言,系统完全可以不用 C++ 实现。不管读者同意与否,我不认为 C++ 具有任何优点。

    是该终结 C++ 的时候了。

      Java    

    图片

    Java 是自 MS-DOS 以来计算机领域中最令人困扰的事情。

    —— 面向对象编程创始人 Alan Kay

    Java 出现在 1995 年,比 C++ 晚了 16 年。Java 是更简单的编程语言,由此得到广泛使用。

    语言家族:C。

    👍 垃圾回收

    相比 C++,Java 的最大优点是具有垃圾回收,这极大地消除了各类软件缺陷。

    👍 生态系统

    Java 已经存在很长时间,在后端开发领域形成了大型生态系统,极大地降低了开发负担。

    👎 面向对象语言

    本文不会深入探讨面向对象编程的不足。详细分析可阅读本文作者的另一篇文章,“面向对象编程:亿万美元灾难”。

    在此给出计算机科学中一些最为杰出人士的看法:

    抱歉,我多年前使用了“对象”一词。该词使得很多人聚焦于一个更狭义的理念,虽然更广义的理念是消息传递。

    —— 面向对象编程的创始人 Alan Kay

    Alan Kay 是对的,许多主流面向对象编程语言并未找准关注点。它们聚焦于类和对象,而忽视了消息传递。幸运的是,Erlang 和 Elixir 等一些现代编程语言找准了方向。

    受面向对象编程影响的编程语言,会导致计算机软件冗长、可读性不好、描述性差、难修改和维护。

    —— Richard Mansfield

    所有使用 Java、C# 等面向对象编程语言的开发人员,如果曾具有使用非面向对象编程语言的经验,对此应深有体会。

    👌 速度

    大家都知道,Java 运行在 JVM 之上,而 JVM 的启动速度是出名的慢。我曾看到有运行在 JVM 上的程序耗时 30 多秒才启动起来。对于现代云原生程序,这是不可接受的。

    一个大型项目,如果编译速度慢,就会对开发人员的生产效率产生显著影响。Java、Scala 等 JVM 语言存在同样的问题。

    但从好的一面说,JVM Runtime 的性能还算不错。

    👎 学习难度

    尽管 Java 是一种相当简单的语言,但 Java 以面向对象编程为主,这使得 Java 很难做到优秀。编写一个简单的 Java 程序可信手拈来,但是掌握如何编写可靠、可维护的面向对象代码,则需要十数年的 Java 功力。

    👎 并发

    Java 设计于单核计算时代,和 C++ 一样,仅支持基本的并发特性。

    👎 空值

    Java 中,所有引用均可为空值。

    👎 错误处理

    抛出并捕获错误是 Java 的首选错误处理机制。

    👎 不可变性

    未内置对不可变数据结构的支持。

    判定

    图片

    Java 在刚推出时,的确是一种很好的编程语言。但遗憾的是不同于 Scala 等语言,Java 始终专注于面向对象编程。Java 编程严重受模板代码的影响,冗余代码多。

    Java 应该退居二线了。

       C#      

    图片

    C# 和 Java 并没有本质上的差异。C# 的早期版本,就是微软的 Java 实现。

    C# 具有 Java 的大部分优点。C# 于 2000 年推出,比 Java 晚 5 年,借鉴了 Java 的经验教训。

    语言家族:C

    👌 语法

    C# 在语法上一直保持略微领先 Java。尽管是一种面向对象语言,但 C# 在解决模板代码问题上比 Java 有所改进。很高兴看到 C# 每个新版本都能改进语法。例如,添加了表达体函数成员(expression-bodied function members)、模式匹配、元组等特性。

    👎 面向对象语言

    和 Java 一样,C# 主要针对面向对象编程。面向对象编程的缺点如上所列,在此不再详述。下面列出一些知名人士的观点。

    我认为相比函数式语言,面向对象语言中缺失可重用性。问题在于,面向对象语言需要处理其所提供的所有隐含(implicit)环境。尽管我们想要的只是一根香蕉,但却得到了一只握着香蕉的大猩猩,甚至是整个丛林。

    —— Erlang 的创建者 Joe Armstrong

    我完全同意这个说法,相比函数式编程,命令式编程非常难以重用面向对象代码。

    面向对象编程提供了对正确做法的一个反面教材……

    —— 计算机科学先驱 Edsger W. Dijkstra

    从我自己使用面向对象和非面向对象编程的经验看,我完全同意面向对象代码更难以正确实现功能。

    👎 多范式(Multi-paradigm)

    C# 声称是一种多范式语言,尤其是声称支持函数式编程,但我并不同意。对函数提供一流支持(first-class functions),并不足以称之为函数式语言。

    那么什么语言可称为具备函数式特性?应至少内置支持不可变数据结构、模式识别、组合函数的管道操作符、代数数据类型(ADT)等特性。

    👎 并发

    和 Java 一样,C# 创立于单核计算时代,仅提供基本的并发支持。

    👎 空值

    NullsC# 中,所有引用均可为空。

    👎 错误处理

    抛出并捕获错误是 C# 的首选错误处理机制。

    👎 不可变性

    未内置对不可变数据结构的支持。

    评判

    尽管我本人的职业生涯中主要使用的是 C#,但还是对这种语言评价不高。与对 Java 的评判一样,我建议读者寻找更现代的替代语言。C# 在本质上依然是 Java,只是具有更现代的语法。不幸的是,C# 本身并不“sharp”。

    Python

    图片

    Python 早在 1991 年提出,和 JavaScript 并称当前使用最广的两种语言。

    语言家族:C

    👍 生态系统

    Python 软件库几乎无所不能。不同于 JavaScript,Python 不能用于 Web 前端开发,但大规模的数据科学软件库弥补了这方面的不足。

    👍 学习难度

    Python 语言非常简单,初学者数周就能上手。

    👎 类型系统

    Python 是动态类型的,因此谈不上需要类型系统。

    👎 速度

    Python 是一种解释性语言,性能慢。对性能有严格要求的程序,可使用 Cython 替代原生的 Python。

    相对于原生语言,Python 的启动也相当慢。

    👎 工具

    对比其他的现代编程语言,难免会对 Python 的依赖管理颇为失望。目前存在 pip、pipenv、virtualenv、pip freeze 等工具。相比之下,JavaScript 只需要 NPM 这一种工具。

    👎 并发

    Python 在创建时并未全面考虑并发,仅提供基本的并发特性。

    👎 空值

    Python 中所有引用均可为空。

    👎 错误处理

    抛出并捕获错误是 Python 的首选错误处理机制。

    👎 不可变性

    未内置对不可变数据结构的支持。

    评判

    图片

    很不幸,Python 并不提供对函数式编程的支持。函数式编程非常适合处理数据科学所面对的问题。即便是在 Python 擅长的 Web 爬虫领域,Elixir 等函数式语言表现更好。

    我并不推荐使用 Python 完成大型项目,该语言在构建中并未充分地考虑软件工程。

    如果有更好的选择,不推荐在数据科学之外使用 Python。在数据科学领域,Julia 可能是 Python 的很好替代,尽管相比 Python 而言,Julia 的生态系统近乎不存在。

       Rust   

    图片

    Rust 是一种现代低层语言,最初设计用于替代 C++。

    语言家族:C

    👍 速度

    运行快速是 Rust 设计所秉持的初衷。在编译性能上,Rust 程序要慢于 Go 程序,但运行时性能比 Go 稍快。

    👍 空值

    至此,本文推荐列表中终于出现支持现代空值的语言了。Rust 中没有 null 或 nil 值,开发人员使用 Option 模式。

    // 源代码: https://doc.rust-lang.org/rust-by-example/std/option.html// 返回值或者是 T 类型的 Some,或是 None。enum Option{ Some(T), None,}// 整数除法不会出错。fn checked_division(dividend: i32, divisor: i32) -> Option{ if divisor == 0 { // 错误表示为 None。None } else { // 结果使用 Some 封装。Some(dividend / divisor) }}// 该函数用于处理失败的除操作。fn try_division(dividend: i32, divisor: i32) { // 与其他枚举一样,Option 值可模式匹配。match checked_division(dividend, divisor) { None => println!("{} / {} failed!", dividend, divisor), Some(quotient) => { println!("{} / {} = {}", dividend, divisor, quotient) }, }

    👍 错误处理

    Rust 的错误处理引入了现代函数式方法,使用特定的 Result 类型,声明可能会产生失败的操作。Result 模式非常类似于 Option 模式,只是在 None 的情况下依然有值。

    // 结果或者是 T 类型的 OK 函数值,或是 E 类型的 Err 函数值。enum Result<T,E> { Ok(T), Err(E),}// 存在失败可能的函数。fn random() -> Result<i32, String> { let mut generator = rand::thread_rng(); let number = generator.gen_range(0, 1000); if number <= 500 { Ok(number) } else { Err(String::from(number.to_string() + " should be less than 500")) }}// 处理函数的结果。match random() { Ok(i) => i.to_string(), Err(e) => e,

    👎 内存管理

    在本文列出的现代编程语言中,Rust 是唯一不提供垃圾回收的。Rust 迫使开发人员去考虑如何实现底层的内存管理,这影响了开发人员的效率。

    👎 并发

    由于 Rust 中缺少垃圾回收,因此实现并发是相当困难的。开发人员必须考虑“装箱”(boxing)和“钉住”(Pinning)。这在具有垃圾回收机制的语言中,通常是自动完成的。

    👎 不可变性

    未内置对不可变数据结构的支持。

    👎 低层语言

    作为一种低层语言,开发人员的生产效率无法其他高层语言相比。同时,语言的学习难度明显增大。

    评判

    图片

    Rust 非常适合系统编程。尽管比 Go 更复杂,但 Rust 提供了强大的类型系统。Rust 提供了现代的空值替换和错误处理方法。

    为什么本文将 Rust 排在 TypeScript 和 JavaScript 之后?Rust 是一种设计用于系统编程的低层语言,并非后端和 Web API 开发的最适合选项。Rust 缺少垃圾回收机制,未内置对不可变数据结构的支持。

    TypeScript

    图片

    TypeScript 语言编译为 JavaScript,通过对 JavaScript 添加静态类型,意在成为一种“更好的 JavaScript”。类似于 JavaScript,TypeScript 同样用于前端和后端开发。

    TypeScript 由同是 C# 设计者的 Anders Hejlsberg 设计的,因此代码看上去非常类似 C#,可认为是一种用于浏览器的 C#。

    语言家族:C。

    👎 JavaScript 的超集

    TypeScript 将自己定位为 JavaScript 的超集,这有助于人们采用。毕竟大多数人对 JavaScript 耳熟能详。

    但作为 JavaScript 的超集,更多程度上是一种缺点。这意味着 TypeScript 继承了 JavaScript 的全部问题,局限于 JavaScript 所有的不良设计决策。

    例如,应该没有开发人员喜欢 this 关键词吧。但 TypeScript 依然刻意原封照搬。

    再有,其类型系统时常令人感到奇怪。

    [] == ![]; // -> 为真NaN === NaN; // -> 为假!

    换句话说,TypeScript 具有 JavaScript 的所有缺点。一种糟糕语言的超集并不会变身成为一种优秀的语言。

    👍 生态系统

    TypeScript 完全分享了 JavaScript 庞大的生态系统。这是其最大优点。特别是相比 Python 等语言,NPM 非常好用。

    缺点在于,并非所有的 JavaScript 软件库都可在 TypeScript 中使用,例如 Rambda/Immutable.js 等。

    👌 类型系统

    个人感觉,TypeScript 的类型系统毫无亮点。

    好的一面是甚至提供对 ADT 的支持。例如下面给出的差别联合(discriminated union)类型:​​​​​​​

    // 源代码来自 https://stackoverflow.com/questions/33915459/algebraic-data-types-in-typescriptinterface Square { kind: "square"; size: number;}interface Rectangle { kind: "rectangle"; width: number; height: number;}interface Circle { kind: "circle"; radius: number;}type Shape = Square | Rectangle | Circle;function area(s: Shape) { switch (s.kind) { case "square": return s.sizes.size; case "rectangle": return s.heights.width; case "circle": return Math.PI * s.radius ** 2;

    下面是使用 ReasonML 实现的同样代码:​​​​​​​

    type shape = | Square(int) | Rectangle(int, int) | Circle(int);let area = fun | Square(size) => sizesize | Rectangle(width, height) => widthheight | Circle(radius) => 2piradius;

    差别联合类型是在 TypeScript 2.0 中增添的,TypeScript 的语法尚未企及函数式语言的高度。例如,在 switch 中的字符串匹配易于出错,编译器无法在大小写错误时给出警告。

    TypeScript 仅提供基本的类型推断。此外在使用 TypeScript 时,any 关键字的出现频次难免过高。

    👌 空值

    TypeScript 2.0 添加了对不可为空(non-nullable)类型的支持,使用编译器选项 --strictNullChecks 启用。但使用不可为空类型并非编程默认,也并非 TypeScript 的惯用做法。

    👎 错误处理

    TypeScript 中,使用抛出和捕获异常处理错误。

    👎 新的 JavaScript 特性

    新酷特性首先在 JavaScript 中得到支持,然后才是 TypeScript。实验特性可使用 Babel 在 JavaScript 中得到支持,而在 TypeScript 中则无此功能。

    👎 不可变性

    TypeScript 对不可变数据结构的处理,要显著劣于 JavaScript。JavaScript 开发人员可使用支持不可变性处理的软件库,但 TypeScript 开发人员通常必须依赖原始数组或对象展开操作符(spread operator),即写入时复制(copy-on-write)。​​​​​​​

    const oldArray = [1, 2];const newArray = [...oldArray, 3];const oldPerson = { name: { first: "John", last: "Snow" }, age: 30};// 执行对象深拷贝(deep copy)非常繁琐。const newPerson = { ...oldPerson, name: { ...oldPerson.name, first: "Jon" }

    正如上面代码所示,原生扩展操作符并不支持深拷贝(deep copy),而手工扩展深度对象非常繁琐。大型数组和对象的拷贝的性能也非常不好。

    但 TypeScript 中,readonly 关键字非常好用,用于定义属性是不可变的。虽然如此,TypeScript 要对不可变数据结构提供很好支持,依然需要很多工作。

    JavaScript 提供了一些操作不可变数据的很好软件库,例如 Rambda/Immutable.js。但是,实现此类软件库对 TypeScript 的支持并非易事。

    👎 TypeScript 对比 React

    相比 Clojure 等从设计上考虑到不可变数据处理的语言,在 JavaScript 和 TypeScript 中不可变数据的处理相对更为困难。

    —— 原文引用自 React 官方文档

    继续说缺点。前端 Web 开发推荐使用 React。

    React 并未针对 TypeScript 设计。最初,React 是针对函数式语言设计的,本文稍后会详细介绍。二者在编程范式上存在冲突,TypeScript 是面向对象编程优先的,而 React 是函数优先的。

    React 中,函数参数 props 是不可变的;而 TypeScript 中,没有内置提供适用的不可变数据结构支持。

    在开发中,TypeScript 相比 JavaScript、React 的唯一优点是,无需操心 PropTypes。TypeScript 是否是 JavaScript 的超集?这取决于开发人员的认识。至少我认为是的。做为超集的最大优点,是可接入整个 JavaScript 生态系统。

    为什么 JavaScript 的超集语言备受关注?这与 Java、C# 广为采用是同样的原因,是因为背后有市场营销预算充足的大厂在提供支持。

    评判

    图片

    尽管 TypeScript 常被认为是“更好的 JavaScript”,但我依然评判其劣于 JavaScript。TypeScript 相比 JavaScript 的优点被夸大了,尤其是对于使用 React 做前端 Web 开发。

    TypeScript 保留了 JavaScript 的所有不足,实际上也继承了 JavaScript 中数十年积累不良设计决策,的确并非一种成功的交付,

    Go    

    图片

    Go 设计上主要考虑了提高多核处理器和大规模代码库的编程效率。Go 的设计者们当时任职于谷歌,因对 C++ 的共同不喜而得到灵感。

    语言家族:C。

    👍 并发

    并发是 Go 的杀手级特性。Go 从本质上就是为并发而构建。和 Erlang/Elixir 一样,Go 使用邮箱模型(Mailbox)实现并发。不幸的是,goroutines 并未提供 Erlang/Elixir 进程那样的统一容错特性。换句话说,goroutine 中的异常将导致整个程序宕机,而 Elixir 进程中的异常只会导致当前进程终止。

    👍 👍 速度编译

    速度是谷歌创建 Go 的一个重要考虑。有个笑话,谷歌利用 C++ 编译代码的时间就创建出了 Go。

    Go 是一种高效的语言。Go 程序的启动时间非常快。Go 编译为原生代码,所以运行时速度也非常快。

    👍 学习难度

    Go 是一种简单的语言,如果得到有经验前辈的指导,新手能在一个月内掌握。

    👍 错误处理

    Go 并不支持异常,由开发人员显式处理各种可能的错误。和 Rust 类似,Go 也返回两个值,一个是调用的结果,另一个是可能的错误值。如果一切运行正常,返回的错误值是 nil。

    👍 不支持面向对象编程

    虽然这么说有人会反对,但我个人认为,不支持面向对象特性是很大的优势。

    重申 Linux Torvalds 的观点:

    C++ 是一种很糟的(面向对象)语言……将项目局限于 C,意味着整个项目不会因为任何愚蠢的 C++“对象模型”而搞砸。

    —— Linux 创建者 Linus Torvalds

    Linus Torvalds 公开对 C++ 和面向对象编程持批评态度。限制编程人员在可选的范围内,是他完全正确的一面。事实上,编程人员的选择越少,代码也会更稳定。

    在我看来,Go 可以回避了许多面向对象特性,免于重蹈 C++ 的覆辙。

    👌 生态系统

    一些标准库的确很笨重。大部分并不符合 Go 返回带外(out-of-band,OOB)错误的自身哲学。例如,有的库对索引返回 -1 值,而非 (int, error)。还有一些库依赖全局状态,例如 flag 和 net/http。

    Go 的软件库缺少标准化。例如在错误时,有的库返回 (int, error),也有软件库返回 -1 等值。还有一些库依赖标识等全局状态。

    Go 的生态系统规模远比不上 JavaScript。

    👎 类型系统

    图片

    几乎所有的现代编程语言都具有某种形式的泛型,其中包括 C# 和 Java,甚至是 C++ 也提供模板类。泛型支持开发人员重用不同类型的函数实现。如果不支持泛型,那么开发人员就必须对整型、双精度和浮点型单独实现加法函数,这将导致大量的代码冗余。换句话说,Go 缺失对泛型的支持导致了大量冗余代码。正如有人指出的,“Go”是“去写一些模板代码”(Go write some boilerplate)的缩写。

    👎 空值

    不幸的是,即使更安全的空值替代方案已存在数十年,Go 依然在语言中添加了空值。

    👎 不可变性

    未内置对不可变数据结构的支持。

    评判

    图片

    Go 并非一种好的语言,但也谈不上不好,只是不够优秀。使用一种并不优秀的语言时需谨慎,因为这可能会导致我们在随后的二十年中陷入困境。

    Will Yager 的博客文章“Why Go Is No Good”

    如果你并非供职于谷歌,也没有面对类似谷歌的用例,那么 Go 可能并非好的选择。Go 是一种最适合系统编程的简单语言,但并非 API 开发的好选择。原因是因为我们有更多更好的替代语言,本文稍后介绍。

    我认为总体而言,尽管 G 的类型系统略弱,但比 Rust 还是略好。Go 是一种简单的语言,非常快,易于学习,并且具有出色的并发功能。当然,Go 成功地实现了做为“更好的 C++”这一设计目标。

    最佳系统语言奖授予 Go

    图片

    实至名归,Go 是系统编程的理想选择。Go 是一种低层语言,使用 Go 构建的大量成功项目,例如 Kubernetes,Docker 和 Terraform,证明其非常适合系统编程。

     

    JavaScript

    图片

    作为当前最流行的编程语言,JavaScript 无需过多介绍。

    当然,将 JavaScript 排在 Rust、TypeScript 和 Go 之前是正确的。下面给出原因。

    语言家族:C

    👍 👍 生态系统

    生态系统是 JavaScript 的最大优势。我们能想到的所有,,包括 Web 的前端和后端开发,CLI 编程、数据科学,甚至是机器学习,都可使用 JavaScript。JavaScript 可能具有提供任何功能的软件库。

    👍 学习难度

    JavaScript 和 Python 都是非常容易学习的编程语言。几周就能上手做项目。

    👎 类型系统

    和 Python 类似,JavaScript 是动态类型的。无需过多解释,但是其类型系统时常看起来很奇怪:

    [] == ![] // -> 为真NaN === NaN; // -> 为假[] == '' // -> 为真[] == 0 // -> 为真

    👌 不可变性

    在 TypeScript 一节中已经介绍,展开操作符(spread operator)会影响性能,甚至并没有在拷贝对象时执行深拷贝。尽管有 Ramda/Immutable.js 等软件库,但 JavaScript 缺少对不可变数据结构的内建支持。

    👎 JavaScript 并非针对响应式设计的

    在 JavaScript 中使用 React,必须借助 PropTypes。但这也意味着必须去维护 PropTypes,这会导致灾难性后果。

    此外,如果在编程中不加注意的话,可能会导致严重的性能问题。例如:

    <HugeList options=[] />

    这个看上去无害的代码会导致严重性能问题。因为在 JavaScript 中, [] != []。上面的代码会导致 HugeList 在每一次更新时重渲染,尽管 options 值并未发生变化。此类问题会不断叠加,直到用户界面最终无法响应。

    👎 关键字 this

    关键字 this 应该是 JavaScript 中的最大反特性。其行为持续表现不一致,在不同的情况下可能意味完全不同,其行为甚至取决于谁调用了指定的函数。使用 this 关键字通常会导致一些细微而奇怪的错误,难以调试。

    👌 并发

    JavaScript 使用事件循环支持单线程并发,无需考虑加锁等线程同步机制。尽管 JavaScript 在构建时并未考虑并发性,但与大多数其他语言相比,更易于实现并发代码。

    👍 新的 JavaScript 特性

    相比 TypeScript,新特性能更快地在 JavaScript 中支持。即便是实验性特性,也可使用 Bable 支持在 JavaScript 中使用。

    👎 错误处理

    Error handling抛出并捕获错误是 JavaScript 的首选错误处理机制。

    评判

    图片

    JavaScript 并非一种很好设计的语言。JavaScript 的最初版本仅用十天就拼凑出来,尽管在后期版本中修正了许多缺点。

    抛开上述缺点,JavaScript 依然是全栈 Web 开发和很好选择。如果加以适当的代码修炼和分析,JavaScript 是一种很好的语言。

    原文链接:

    These Modern Programming Languages Will Make You Suffer

    https://www.infoq.cn/article/9VXbENMFH9Y65VCOI4D1

    展开全文
  • Inger是一种命令式编程语言(inger)的编译器,它非常类似于C(但更简洁)。 除了Inger编译器外,我们还编写了电子书“ Compiler Construction:A Practical Approach”,该书可供下载。
  • 编程语言的主要类型Common programming paradigms include imperative which allows side effects, functional which disallows side effects, declarative which does not state the order in which operations ...

    编程语言的主要类型

    Common programming paradigms include imperative which allows side effects, functional which disallows side effects, declarative which does not state the order in which operations execute
    (翻译:常见的编程语言类型包括允许有副作用的命令式编程,不允许副作用的函数式编程和不描述操作执行顺序的声明式编程)

    A programming paradigm is a fundamental style of computer programming. There are four main paradigms: imperative, declarative, functional (which is considered a subset of the declarative paradigm) and object-oriented.

    Declarative programming : is a programming paradigm that expresses the logic of a computation(What do) without describing its control flow(How do). Some well-known examples of declarative domain specific languages (DSLs) include CSS, regular expressions, and a subset of SQL (SELECT queries, for example) Many markup languages such as HTML, MXML, XAML, XSLT… are often declarative. The declarative programming try to blur the distinction between a program as a set of instructions and a program as an assertion about the desired answer.

    Imperative programming : is a programming paradigm that describes computation in terms of statements that change a program state. The declarative programs can be dually viewed as programming commands or mathematical assertions.

    Functional programming : is a programming paradigm that treats computation as the evaluation of mathematical functions and avoids state and mutable data. It emphasizes the application of functions, in contrast to the imperative programming style, which emphasizes changes in state. In a pure functional language, such as Haskell, all functions are without side effects, and state changes are only represented as functions that transform the state.

    ( 出处:维基百科)

    翻译总结:

    编程语言主要有四种类型

    1. 声明式编程:专注于”做什么”而不是”如何去做”。在更高层面写代码,更关心的是目标,而不是底层算法实现的过程。
      ex: css, 正则表达式,sql 语句,html, xml…
    2. 命令式编程(过程式编程) : 专注于”如何去做”,这样不管”做什么”,都会按照你的命令去做。解决某一问题的具体算法实现。
    3. 函数式编程:把运算过程尽量写成一系列嵌套的函数调用。
      函数式编程强调没有”副作用”,意味着函数要保持独立,所有功能就是返回一个新的值,没有其他行为,尤其是不得修改外部变量的值。
      所谓”副作用”(side effect),指的是函数内部与外部互动(最典型的情况,就是修改全局变量的值),产生运算以外的其他结果。(详细了解函数式编程请看阮一峰的函数编程初探文章)

    举个简单的例子,了解三者区别
    1.现有这样一个数学表达式

    (1+2) * 3 / 4
    

    命令式编程可能会这样写

    var a = 1 + 2;
    var b = a * 3;
    var c = b / 4;
    

    函数式编程如下写法

    divide(multiply(add(1, 2), 3), 4)
    

    函数式编程是声明式的一种类型,声明式强调目标而不是具体过程。

    1. 我们想让一个数组里的数值翻倍。

    我们用命令式编程风格实现,像下面这样:

    var numbers = [1,2,3,4,5]
    var doubled = []
    for(var i = 0; i < numbers.length; i++) {
      var newNumber = numbers[i] * 2
      doubled.push (newNumber)
    }
    console.log (doubled) //=> [2,4,6,8,10]
    

    我们直接遍历整个数组,取出每个元素,乘以二,然后把翻倍后的值放入新数组,每次都要操作这个双倍数组,直到计算完所有元素。

    而使用声明式编程方法,我们可以用 Array.map 函数,像下面这样:

    var numbers = [1,2,3,4,5]
    var doubled = numbers.map (function (n) {
      return n * 2
    })
    console.log (doubled) //=> [2,4,6,8,10]
    

    map利用当前的数组创建了一个新数组,新数组里的每个元素都是经过了传入map的函数(这里是function (n) { return n*2 })的处理。
      map函数所做的事情是将直接遍历整个数组的过程归纳抽离出来,让我们专注于描述我们想要的是什么(what)。注意,我们传入map的是一个纯函数;它不具有任何副作用(不会改变外部状态),它只是接收一个数字,返回乘以二后的值。

    参考文章
    1.声明式编程和命令式编程的比较
    2.阮一峰的函数编程初探文章

    展开全文
  • Wybe是一种编程语言,旨在结合声明式和命令式编程的最佳功能。 它处于开发的早期阶段,尚不适合认真使用。 语言设计的主要原则是接口完整性,这意味着流入或流出过程或函数的所有信息均流经其接口。 这样可以确保...
  • 命令式编程与函数式编程

    千次阅读 2017-11-09 23:05:26
    命令式编程命令式编程(英语:Imperative programming),是一种描述电脑所需作出的...食谱和行动清单,虽非计算机程序,但与命令式编程有相似的风格:每步都是指令,有形的世界控制情况。因为命令式编程的基础观念,

    命令式编程

    命令式编程(英语:Imperative programming),是一种描述电脑所需作出的行为的编程典范。几乎所有电脑的硬件工作都是指令式的;几乎所有电脑的硬件都是设计来运行机器码,使用指令式的风格来写的。较高级的指令式编程语言使用变量和更复杂的语句,但仍依从相同的典范。食谱和行动清单,虽非计算机程序,但与命令式编程有相似的风格:每步都是指令,有形的世界控制情况。因为命令式编程的基础观念,不但概念上比较熟悉,而且较容易具体表现于硬件,所以大部分的编程语言都是指令式的。

    大部分的高级语言都支持四种基本的语句:

    运算语句:一般来说都表现了在内存内的数据进行运算的行为,然后将结果存入内存中以便日后使用。高级命令式编程语言更能处理复杂的表达式,可能会产生四则运算和函数计算的结合。

    循环语句容许一些语句反复运行数次。循环可依据一个默认的数目来决定运行这些语句的次数;或反复运行它们,直至某些条件改变。

    条件分支语句容许仅当某些条件成立时才运行某个区块。否则,这个区块中的语句会略去,然后按区块后的语句继续运行。

    无条件分支语句容许运行顺序转移到程序的其他部分之中。包括跳跃(在很多语言中称为Goto)、副程序和Procedure等。

    循环、条件分支和无条件分支都是控制流程。

    说白了,命令式编程就是让机器按照规定的命令执行下去。

    函数式编程

    函数式编程(英语:functional programming)或称函数程序设计,又称泛函编程,是一种编程典范,它将电脑运算视为数学上的函数计算,并且避免使用程序状态以及易变对象。函数编程语言最重要的基础是λ演算(lambda calculus)。而且λ演算的函数可以接受函数当作输入(引数)和输出(传出值)。

    它属于”结构化编程”的一种,主要思想是把运算过程尽量写成一系列嵌套的函数调用。举例来说,现在有这样一个数学表达式:

      (1 + 2) * 3 - 4
    

    传统的过程式编程,可能这样写:

      var a = 1 + 2;
    
      var b = a * 3;
    
      var c = b - 4;
    

    函数式编程要求使用函数,我们可以把运算过程定义为不同的函数,然后写成下面这样:

      var result = subtract(multiply(add(1,2), 3), 4);
    

    这就是函数式编程。

    函数式编程中的函数这个术语不是指计算机中的函数(实际上是Subroutine),而是指数学中的函数,即自变量的映射。也就是说一个函数的值仅决定于函数参数的值,不依赖其他状态。比如sqrt(x)函数计算x的平方根,只要x不变,不论什么时候调用,调用几次,值都是不变的。

    比起命令式编程,函数式编程更加强调程序执行的结果而非执行的过程,倡导利用若干简单的执行单元让计算结果不断渐进,逐层推导复杂的运算,而不是设计一个复杂的执行过程。

    函数式编程的特点

    函数式编程近些年异军突起,又重新回到了人们的视线,并得到蓬勃发展。总结起来,无外乎如下好处:

    1.减少了可变量(Immutable Variable)的声明,程序更为安全。
    2.相比命令式编程,少了非常多的状态变量的声明与维护,天然适合高并发多现成并行计算等任务,这也是函数是编程近年又大热的重要原因。
    3.代码更为简洁,可读性更强,对强迫症的同学来说是个重大福音。

    展开全文
  • 命令式编程vs声明式编程

    千次阅读 2016-07-13 15:08:32
    命令式编程:命令“机器”如何去做事情(how),这样不管你想要的是什么(what),它都会按照你的命令实现。 声明式编程:告诉“机器”你想要的是什么(what),让机器想出如何去做(how)。 声明式编程和命令式编程的代码...
  • :water_pistol: 优雅的类似于ML的命令式语言 基本的 抽象语法树 Lexer 解析器 口译员 编译为LLVM IR 静态分析(可视化控制流程图) 编译器优化 语言功能 类型定义 模式匹配 集成开发环境 VSCode中的代码...
  • 命令式编程和声明式编程

    千次阅读 2012-07-09 20:53:58
    Aspect Oriented Programming(面向方面编程,简称AOP)是一种Declarative Programming... Programming(命令式编程)相对的概念。我们平时使用的编程语言,比如C++、Java、Ruby、Python等,都属于Imperative Progra
  • 最近接触到了K8S的架构,其中一点很重要的是整体架构采用的是声明式编程,主要的编程范式三种:命令式编程,声明式编程和函数式编程。 #命令式编程命令式编程的主要思想是关注计算机执行的步骤,即一步一步...
  • 命令式编程: def 定义函数 if else 条件判断 for 循环 包、模块;类、对象也是重要部分,但不是必须 函数式编程: 最基本的三个函数 map / reduce / filter lambda表达式 -- 在函数式编程里被称为算子 lambda ...
  • 本来自以为已经了解了函数式编程,但当看到这篇文章时,才发现自己之前的见解多么肤浅,因此决定重新整理有关声明式编程、命令式编程以及函数式编程相关的概念、以及知识点,方便未来某一时刻快速回忆以及使用。...
  • 声明式和命令式编程

    千次阅读 2018-06-29 09:50:09
    Aspect Oriented Programming...Declarative Programming(声明式编程)是和Imperative Programming(命令式编程)相对的概念。我们平时使用的编程语言,比如C++、Java、Ruby、Python等,都属于Imperative Programm...
  • 编程语言的主要的种类and趋势 逻辑式语言..函数式语言...命令式语言   1. 编程语言的主要的种类 逻辑式语言..函数式语言...命令式语言 1 2. 逻辑式语言,,不必考虑实现过程而只需考虑定义和结果 1 3. 第五...
  • 函数式编程和命令式编程

    万次阅读 2011-05-16 23:28:00
    <br />所谓命令式编程,是以命令为主的,给机器提供一条又一条的命令序列让其原封不动的执行。程序执行的效率取决于执行命令的数量。因此才会出现大O表示法等等表示时间空间复杂度的符号。 <br />而函数...
  • 黑斑羚 一种命令式和功能性编程语言。 制作说明 请参阅。 文献资料 请参阅我们的。
  • 命令式编程 vs 符号式编程

    千次阅读 2018-07-01 13:14:32
    命令/符号式编程的定义并不明确,CXXNet/Caffe因为依赖于配置文件,配置文件看作计算图的定义,也可以被当作符号式编程 源 源网址 什么是symblic/imperative style编程 使用过python或C++对imperative ...
  • 声明式编程和命令式编程

    千次阅读 2017-11-17 17:10:35
    ) 命令式编程 命令“机器”如何去做事情(how),这样不管你想要的是什么(what),它都会按照你的命令实现。 var numbers = [1,2,3,4,5] var doubled = [] for(var i = 0; i  varnewNumber =numbers[i] * 2 ...
  • 2.相比命令式编程,少了非常多的状态变量的声明与维护,天然适合高并发多现成并行计算等任务,这也是函数是编程近年又大热的重要原因。 3.代码更为简洁,可读性更强,对强迫症的同学来说是个重大福音。2.函
  • 命令式编程和函数式编程的区别

    千次阅读 2012-11-07 20:23:45
    命令式编程和函数式编程的区别 第一次尝试翻译外国的技术文章,很担心自己的语言不够准确。尝试尝试,总要迈出这一步的!   你是否过这样的沮丧经历?我要开车去一个我从来没去过的地方,于是我网上查看关于...
  • 面向方面编程(Aspect Oriented Programming,简称AOP)是一种...我们平时使用的编程语言,比如C++、Java、Ruby、Python等,都属命令式编程命令式编程的意思是,程序员需要一步步写清楚程序需要如何做什么(How to d
  • owoScript是一个基于堆栈,命令式和图灵完整的编程语言
  • 参考: http://blog.jobbole.com/42178/ https://www.jianshu.com/p/8d8555dc7569 ... 命令式编程:命令“机器”如何去做事情(how),这样不管你想要的是什么(what),它都会按照你的命令实现。 声明式...
  • 命令式编程范式简介

    千次阅读 2016-10-16 23:35:59
    命令编程范式的核心观念和观念范式:以机器的运作方式看待一切。
  • 编程语言一直都在进化,而且现在的趋势是命令式语言正在想函数式语言靠拢。 首先我们来看看函数式编程的一些典型特征: 函数作为第一级的值(第一类对象)和高阶函数广泛的多态递归机构性的函数
  • 函数式编程和命令式编程   函数式编程是最近被热炒的一个概念。国内外众多大牛纷纷发表文章,认为函数编程可能会再度兴起。搞得一向喜欢跟风的小弟我如坐针毡。因此,也抽空研究了一下函数式编程这个时髦的概念。...
  • 静态语言:编译阶段,确定变量的类型 动态语言:运行阶段,确定变量的类型 ...命令式 or 函数式 语言 静态 && 命令式 JAVA 静态 && 函数式 Scala 动态 && 命令式 Groovy 动态 && 函数式 Clousure
  • 浅谈函数式编程语言

    千次阅读 2019-02-27 09:22:28
    函数式编程语言(functional language) 1.什么是函数式编程语言 函数式语言(functional language)一类程序设计语言,是一种非冯·诺伊曼式的程序设计语言。函数式语言主要成分是原始函数、定义函数和函数型。这种语言...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 133,135
精华内容 53,254
关键字:

命令式编程语言有哪些