最近整理多范式编程语言共性及趋势,再次翻出今年夏天的时候瓜哥(@2gua)在微博上出的一个题目:
【来做题】功能实现倒是很简单~ 用你熟悉的语言,统计一个字符串abcdefghijklmnopqrstuvwxyz…abcdefghijklmnopqrstuvwxyz(1千万个a-z,不可直接a=1千万……) 中每个字母的个数,最后输出类似图示。要求除了更好的方式(如更加Pythonic的方式),还要计算越快越好,并打印出代码执行时间(打印效果类似图示)
当时分别写了 Rust、Kotlin 和 Julia 版本,加上近期写的 Java 8 版,其速度分别与作为基准的 Ruby 代码做对比,在一台配置略低的虚拟机中运行多次取中位数如下:
8.4s+ |
6.4s+ |
18.8s+ |
5.8s+ |
170s+ |
之所以用 Ruby 版代码作为基准,是因为它的速度非常快,而且其代码也很优雅,代码在 @mulder 的基础上改了一点,使其更合乎函数式编程风格:
require "benchmark"
time = Benchmark.realtime do
s = (('a'..'z').to_a.join * 1000_0000)
h = Hash[('a'..'z').collect {|c| [c, s.count(c)]}]
puts h
end
puts time
Julia
从上面速度数据看,Julia 版代码最慢,但其代码最简洁:
function f()
s = repeat(join('a':'z'), 1000_0000)
[ c=>count(i->i==c, s) for c in 'a':'z' ]
end
@time println(f())
它与 Ruby 版的实现很类似,但从速率上看,它优化的并不是很好。
Rust
Rust 版的速度很快,但是其实多写了不少代码:
use std::time::SystemTime;
fn logarithmic_repeat(mergeable: &Vec<u8>, num: usize) -> Vec<u8> {
let mut result: Vec<u8> = Vec::with_capacity(mergeable.len() * num);
let mut mergeable: Vec<u8> = mergeable.clone();
let mut num = num;
while num != 0 {
if num & 1 != 0 {
result.extend(mergeable.as_slice().iter());
}
num >>= 1;
if num == 0 {
break;
}
let mergeable_clone: Vec<u8> = mergeable.clone();
mergeable.extend(mergeable_clone.as_slice().iter());
}
result
}
fn main() {
let now = SystemTime::now();
let letters: Vec<u8> = (b'a'..b'z'+1).collect();
let repeated: Vec<u8> = logarithmic_repeat(&letters, 1000_0000);
for b in letters {
print!("{}: {}, ", b as char, repeated.iter().filter(|&x| *x==b).count());
}
let elapsed = now.elapsed().unwrap();
println!("\ntime: {}.{:09}s", elapsed.as_secs(), elapsed.subsec_nanos());
}
上述代码已经是比较简洁的了,Rust 语言因为独有的生命周期机制,写起来略显复杂。但是更多的代码是为避免速度输在起跑线上而实现对数级的重复(相当于 Ruby 的 *
和 Julia 的 repeat()
函数),这是 Rust 标准库所欠缺的地方。
Java 8
更快的就是近期写的 Java 8 版代码:
import java.util.Collections;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class Z {
public static void main(String[] args) {
long startTime=System.currentTimeMillis();
String letters = IntStream.rangeClosed('a', 'z')
.mapToObj(c -> "" + (char)c)
.collect(Collectors.joining());
String repeated = Collections.nCopies(1000_0000, letters)
.parallelStream()
.collect(Collectors.joining());
Map<String, Long> result = repeated.chars()
.parallel()
.mapToObj(c -> "" + (char)c)
.collect(
Collectors.groupingBy(
Function.identity(),
Collectors.counting()
)
);
long endTime=System.currentTimeMillis();
System.out.println(result);
System.out.println("" + (endTime - startTime) / 1000.0 + "s");
}
}
Java 8 版代码是最啰嗦的,它并不需要自己实现对数级重复,但是它的代码量已经与 Rust 版相当了;另一 Java 8 的不足是,尚未支持简单变量的类型推断,当然这也是其啰嗦的成因之一。 说完了缺点,现在看其优点:
- 丰富的 Collector:
groupingBy()
与counting()
的组合完美实现了此功能,从而避免了像 Rust 版、Ruby 版那样重复扫描巨大字符串 26 次。 -
.parallelStream()
和.parallel()
方法获得并行流,其后能够并行计算,这是它能够在速度上胜出的另一个因素。
Kotlin
Kotlin 版的实现与 Ruby 非常类似,只是没有 Ruby 那么多的语法糖:
import kotlin.system.measureTimeMillis
fun main(args : Array<String>) {
print(measureTimeMillis {
val letters = ('a'..'z').joinToString(separator="")
val repeated = letters.repeat(1000*10000)
println(letters.map { x -> Pair(x, repeated.count {it == x}) })
} / 1000.0)
println('s')
}
Kotlin 代码对“1 千万”的表达与其他语言不同,它的数字字面值还未支持分隔符,这是有待改进的一小处。与 Java 相比缺少 groupingBy()
与 counting()
这样的 Collector,因此性能不及 Java 8,但是比 Java 8 要简洁很多倍,未来有取代 Java 的潜质。
小结
除了作为对照基准的 Ruby,其他的都是新兴语言(包括 Java 8,它相对于 Java 7 也是革命式的更新)。
- Julia 是动态语言,其定位是统计与科学计算,从上文看简洁性不错,性能改进是关键。
- Rust、Kotlin 与 Java 8 都是多范式静态类型语言。对于 Java 8 致命弱点是不够简洁;而 Rust 和 Kotlin 目前的不足是完善程度,但目前两门语言都在快速发展期,具有很大潜力。
相关推荐
Python作为一门多范式语言,支持函数式、过程式以及面向对象的编程风格。在Python中,OOP的应用使得代码更加模块化,易于理解和维护。 在Python中,类(Class)是创建对象的蓝图,它定义了一组特性和行为。特性通常...
以下是对给定压缩包中各编程语言规范的详细解释: 1. **C#编程规范**: C#是一种面向对象的、现代化的编程语言,广泛应用于Windows桌面应用、游戏开发和Web服务。规范包括命名约定(如PascalCase命名类和接口,...
ABCs编程语言是一种独特而有趣的编程系统,它的设计灵感来源于我们日常生活中的基本元素——字母表。标题"ABCs:使用字母作为命令的深奥编程语言"揭示了这种编程语言的核心特性,即它将编程指令与字母对应起来,使得...
逻辑编程的核心在于它的语言结构,其中最著名的逻辑编程语言包括Prolog(Procedural Logic Programming)和Answer Set Programming(ASP)。 在Prolog中,程序是由一系列的“事实”和“规则”组成。事实是已知的...
1. **形式语言**:形式语言是由一组符号组成的有限或无限集合,这些符号通常来自一个预定义的字母表。在计算机科学中,形式语言常用于描述编程语言的语法结构或者数据传输协议。例如,正则语言是由正规式生成的语言...
形式语言是数学上定义的一类特定的语言,通常由有限的字母表上的符号序列组成。在计算机科学中,这些语言常被用来描述可以被计算机程序处理的数据结构或模式。例如,正则语言、上下文无关语言和上下文敏感语言等,...
符号串则是由字母表中的符号按照特定顺序排列形成的序列,如0101是Σ={0,1}上的符号串。符号串有前缀、后缀和子串的概念,这些都是对符号串进行操作的基础。 此外,形式描述语言是一种数学系统,由Noam Chomsky在...
在计算机科学领域,编译原理是研究编程语言结构和转换的学科,主要关注如何将高级编程语言转化为机器可执行的指令。清华大学的编译原理课件第三章主要探讨了文法和语言的相关概念,这是理解编译器工作原理的关键。 ...
形式语言通常由一个字母表(Alphabet)组成,其中包含有限个基本符号,这些符号可以组合成字符串(String)。接着,我们需要了解语言(Language),它是一组按照特定规则(Grammar)生成的字符串集合。形式语言的...
这个项目的核心是实现一种自定义的编程语言——魔王语言。魔王语言允许用户自定义字母与汉字的映射关系,以及设定两种规则:A规则和B规则。这可能涉及到词法分析、语法解析和语义分析等编译器设计的关键步骤。 1. *...
- Grammar:语法规则,编程语言中定义正确代码结构的规则。 H - Hash Table:哈希表,通过键(Key)快速查找数据的数据结构。 - HTML (HyperText Markup Language):超文本标记语言,用于创建网页的标准标记语言。 ...
**算法描述语言Scheme**是一种高级、多范式编程语言,它是Lisp语言的一个方言。Scheme语言的核心特点包括静态作用域和正确的尾递归,这些特征是Scheme区别于其他编程语言的关键。由于它的设计哲学追求异常清晰和简单...
- **基础概念**:定义形式语言的基本元素,如字母表、字符串、语言等,以及自动机模型的构建。 - **正则表达式与正则语言**:介绍正则表达式的操作(如并、交、闭包),以及如何通过正则表达式构造DFA和NFA。 - *...
在编程领域,掌握一定的英语词汇和术语是至关重要的,因为大多数编程语言、文档、框架和工具都是用英文编写的。编程英语翻译不仅涉及到基础的计算机科学概念,还涵盖各种编程语言的关键字、函数、类库和API接口。...
LUA脚本语言是一种轻量级的、扩展性强的程序设计语言,它支持过程式、面向对象、函数式和数据驱动等编程范式。LUA的设计使其成为一个灵活且强大的脚本工具,可用于多种应用场景。LUA语言由cleanC编写而成,不包含...
《语言设计规格说明书1》详述了一种新型编程语言的设计理念和规范,旨在结合Python的易用性与静态类型的高效性。该语言受到Pascal语言的启发,目标是创建一个结构严谨、执行效率高且具备强大错误检测能力的编译型...
Lua是一种设计为扩展编程语言的编程语言,它支持一般过程式编程语言的数据描述机制,并且提供了面向对象语言、函数式程序设计和数据驱动编程的良好支持。Lua的主要目标是作为一个强大而轻型的配置语言使用。Lua语言...
Python是一种高级的、解释性的编程语言,它的设计理念是易于学习、易于使用,强调代码的可读性。Python支持多种编程范式,包括面向对象、命令式、函数式、过程式编程等。 1. Python命令行提示符是>>>。 2. Python...
1. **描述型语言**:PROLOG语言是一种声明式的编程语言,程序员只需描述问题的逻辑关系,而无需指定执行顺序。 2. **数据和程序统一**:在PROLOG中,数据和程序都是通过逻辑表达式来体现的,没有明确区分。 3. **...
Decaf 语言是一种简化版的面向对象编程语言,它的设计灵感来源于 C++ 和 Java,尽管在特性上进行了精简,但仍具有足够的表达能力来编写复杂的面向对象程序。Decaf 语言的关键特性包括: 1. **强类型**:Decaf 语言...