最近整理多范式编程语言共性及趋势,再次翻出今年夏天的时候瓜哥(@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 目前的不足是完善程度,但目前两门语言都在快速发展期,具有很大潜力。
相关推荐
给定的字母表A 由26 个小写英文字母组成A={a,b,…,z}。该字母表产生的升序字符串是指字符串中字母按照从左到右出现的次序与字母在字母表中出现的次序相同,且每个字符最多出现1 次。例如,a,b,ab,bc,xyz 等字符串都...
1、输出希腊字母表 首先获取希腊字母表的第一个和最后一个字母在unicode表中的位置,然后使用循环输出其余的希腊字母。对字符型变量可以使用强制类型转换,如int(‘a’),获取它在unicode表中的位置。 2、回文数 ...
适用与多国字母表,适用于开发多国语言类软件参考。
希腊字母表,cStart做int型转换据运算,并将结果赋值给startPosition,cEnd做int型转换运算,并将结果赋值给endPosition
java java java 输出希腊字母表
把一个单词插入到字母链表,创建字母链表C和指针第十二章编程练习7VC6.0
编写一个类A,该类创建的对象可以调用方法f输出英文字母表,然后再编写一个该类的子类B,要求子类B必须继承A类的方法f(不允许重写),子类创建的对象不仅可以调用方法f输出英文字母表,而且调用子类新增的方法g输出希腊...
日语学习必备,图片展示了标准的日语字母表。
韩语字母表FLASH(语音)韩语字母表FLASH(语音) 学习汉语的好资料
使用字符串与数组排序技术,编写一个按照字母表顺序排列字符串列表的application。允许用户在文本区中输入字符串。在文本区域中显示结果。
希腊字母表及其读音与意义;希腊字母表及其读音与意义;希腊字母表及其读音与意义
韩语字母表FLASH(语音)
一年级汉语拼音字母表.pdf
12306的火车站高铁站相对应的字母码表,excel格式,随下随用,方便快捷,excel可编辑,站名和字母码表一一对应,可直接修改网址。
26个汉语拼音字母表的发音~26个拼音字母表.doc
一年级语文下册字母表习题.pdf
小学汉语拼音字母表.doc
给定的字母表A 由26 个小 写英文字母组成A={a,b,…,z}。该字母表产生的升序字符串是指字符串中字母按照从左到 右出现的次序与字母在字母表中出现的次序相同,且每个字符最多出现1 次。例如, a,b,ab,bc,xyz 等字符串...
java语言实现的二叉树。。。用java语言实现的二叉树,输入英文字母,按字母表顺序输出
word文件doc