使用Java 8函数式编程生成字母序列

简介:

在 Java 8 中使用函数式编程生成字母序列是一个很大的挑战。Lukas Eder 愉快地接受了这个挑战,他将告诉我们如何使用 Java 8 来生成ABC的序列——当然,肯定不是一种蹩脚的方式。

我被 Stack Overflow 上网友“mip”提的一个有趣的问题给难住了。该问题是:

1
2
3
我正在寻找一种生成下列字母序列的方式:
 
A, B, C, ..., Z, AA, AB, AC, ..., ZZ.

大家应该能够很快认出这是 Excel spreadsheet 的头部,准确的样子如下:
excel

到现在为止,没有一个答案是使用 Java 8 的函数式编程实现的,因此我接受此挑战。我将使用 jOOλ,因为 Java 8 的 Stream API 提供的功能不足以完成该任务(我承认我错了——非常感谢 Sebastian 对这个问题的有趣解答)。

首先,我们用函数的方式分解这个算法。我们所需要的组件有:

1、一个(可重复)的字母表。

2、一个上界,例如想生成多少个字母。如要求生成序列ZZ,那上界就是2。

3、一种将字母表中的字母与先前生成的字母联合成一个笛卡尔积(cartesian product)的方法。

让我们看一下代码:

1、生成字母表

我们可以这样写入字母表,如:

1
List<String> alphabet = Arrays.asList( "A" , "B" , ..., "Z" );

但这很差劲。我们使用 jOOλ 代替:

1
2
3
4
List<String> alphabet = Seq
     .rangeClosed( 'A' , 'Z' )
     .map(Object::toString)
     .toList();

上面的代码生成从字符 A 到 Z 的封闭区间(Java-8-Stream-speak 是包含上边界的),然后将字符映射成字符串,最后将其转换为列表。

目前为止,一切都很好。现在:

2、使用上边界:

要求的字符序列包括:

1
A .. Z, AA, AB, .. ZZ

但是我们应该很容易想到扩展该需求,能生成如下字符序列,或者更多:

1
A .. Z, AA, AB, .. ZZ, AAA, AAB, .. ZZZ

因此,我们将再次使用 rangeClosed():

1
2
3
4
// 1 = A .. Z, 2 = AA .. ZZ, 3 = AAA .. ZZZ
Seq.rangeClosed( 1 , 2 )
    .flatMap(length -> ...)
    .forEach(System.out::println);

这种方法是为范围[1..2]中每个长度生成一个单独的流,然后再将这些流合并到一个流中。flatMap() 的本质与命令式编程(imperative programming)中的嵌套循环类似。

3、合并字母到一个笛卡尔积中

这是最棘手的部分:我们需要合并字符及出现的次数。因此,我们将使用如下的流:

1
2
3
4
5
Seq.rangeClosed( 1 , length - 1 )
    .foldLeft(Seq.seq(alphabet), (s, i) ->
        s.crossJoin(Seq.seq(alphabet))
         .map(t -> t.v1 + t.v2))
     );

我们再次使用 rangeClosed() 来生成范围 [1 .. length-1] 的值。foldLeft() 与 reduce() 基本一致,区别在于 foldLeft() 保证在流中的顺序是从“左至右”的,不需要 fold 函数来关联。

另一方面,这是一个共容易懂的词汇:foldLeft() 仅代表一条循环的命令。循环的“起源”(即循环的初始化值)是一个完整的字母表(Seq.seq(alphabet))。现在,在范围 [1..length-1] 中的值生成一个笛卡尔积(crossJoin()),产生一个新的字母表,然后我们将每个合并的字母再组成一个单独的字符串(t.v1 与 t.v2)。

这就是整个过程。

将上面的内容合并到一起

下面是一个简单的打印 A .. Z, AA .. ZZ, AAA .. ZZZ 到控制台的程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import org.jooq.lambda.Seq;
public class Test {
     public static void main(String[] args) {
         int max = 3 ;
         List<String> alphabet = Seq
             .rangeClosed( 'A' , 'Z' )
             .map(Object::toString)
             .toList();
         Seq.rangeClosed( 1 , max)
            .flatMap(length ->
                Seq.rangeClosed( 1 , length - 1 )
                   .foldLeft(Seq.seq(alphabet), (s, i) ->
                       s.crossJoin(Seq.seq(alphabet))
                        .map(t -> t.v1 + t.v2)))
            .forEach(System.out::println);
     }
}

声明

对于这个问题,这确实不是最优的算法。在Stack Overflow,有一个匿名用户给出了一种最好实现方法。

1
2
3
4
5
6
7
8
9
10
import static java.lang.Math.*;
private static String getString( int n) {
     char [] buf = new char [( int ) floor(log( 25 * (n + 1 )) / log( 26 ))];
     for ( int i = buf.length - 1 ; i >= 0 ; i--) {
         n--;
         buf[i] = ( char ) ( 'A' + n % 26 );
         n /= 26 ;
     }
     return new String(buf);
}

不用说,这个算法比之前的函数式算法会快很多。


来源:51CTO

相关文章
|
2月前
|
存储 Java 数据处理
|
20天前
|
Java 开发者
探索 Java 的函数式接口和 Lambda 表达式
【4月更文挑战第19天】Java 中的函数式接口和 Lambda 表达式提供了简洁、灵活的编程方式。函数式接口有且仅有一个抽象方法,用于与 Lambda(一种匿名函数语法)配合,简化代码并增强可读性。Lambda 表达式的优点在于其简洁性和灵活性,常用于事件处理、过滤和排序等场景。使用时注意兼容性和变量作用域,它们能提高代码效率和可维护性。
|
21天前
|
安全 Java API
函数式编程在Java中的应用
【4月更文挑战第18天】本文介绍了函数式编程的核心概念,包括不可变性、纯函数、高阶函数和函数组合,并展示了Java 8如何通过Lambda表达式、Stream API、Optional类和函数式接口支持函数式编程。通过实际应用案例,阐述了函数式编程在集合处理、并发编程和错误处理中的应用。结论指出,函数式编程能提升Java代码的质量和可维护性,随着Java语言的演进,函数式特性将更加丰富。
|
1月前
|
Java API 开发者
Java 8新特性之函数式编程实战
【4月更文挑战第9天】本文将深入探讨Java 8的新特性之一——函数式编程,通过实例演示如何运用Lambda表达式、Stream API等技术,提高代码的简洁性和执行效率。
|
1月前
|
缓存 安全 Java
Java中函数式接口详解
Java 8引入函数式接口,支持函数式编程。这些接口有单一抽象方法,可与Lambda表达式结合,简化代码。常见函数式接口包括:`Function&lt;T, R&gt;`用于转换操作,`Predicate&lt;T&gt;`用于布尔判断,`Consumer&lt;T&gt;`用于消费输入,`Supplier&lt;T&gt;`用于无参生成结果。开发者也可自定义函数式接口。Lambda表达式使实现接口更简洁。注意异常处理和线程安全。函数式接口广泛应用于集合操作、并行编程和事件处理。提升代码可读性和效率,是现代Java开发的重要工具。
31 0
|
2月前
|
Java API
Java中的Lambda表达式和函数式编程
传统的Java编程方式在处理一些简单的逻辑时显得繁琐,而Lambda表达式的引入为我们提供了一种更加简洁、灵活的编程方式。本文将介绍Lambda表达式和函数式编程在Java中的应用以及其与传统编程方式的对比,帮助读者更好地理解并运用这一特性。
8 0
|
2月前
|
Java
【java8新特性篇】常用函数式接口
【java8新特性篇】常用函数式接口
17 0
【java8新特性篇】常用函数式接口
|
2月前
|
JavaScript Java 程序员
Java 8新特性解析:Lambda表达式与函数式编程
【2月更文挑战第12天】 本文深入探讨Java 8引入的两大革命性特性:Lambda表达式和函数式编程接口,旨在为Java开发者提供一个清晰的指南,帮助他们理解和应用这些新特性以提升代码的简洁性和效率。通过对Lambda表达式的基本概念、语法及其与函数式接口的结合使用进行详细分析,本文展示了如何利用这些新特性来编写更加简洁、易读且易于维护的代码。同时,文章还将通过实例探讨Lambda表达式在实际开发中的应用,包括在集合处理、事件监听和并发编程等方面的具体使用场景,以期让读者能够充分理解并有效利用Java 8的这些新工具,从而在日常开发工作中提高效率。
|
3月前
|
Java 开发者
Java中的Lambda表达式与函数式接口
【2月更文挑战第3天】传统的面向对象编程语言在处理函数式编程方面存在一定的局限性,而Java 8 引入了Lambda 表达式和函数式接口的概念,极大地提升了语言的灵活性和表达能力。本文将介绍Lambda 表达式和函数式接口在Java 中的基本概念、用法以及与传统面向对象编程的区别,帮助读者更好地理解并应用这些新特性。
|
3月前
|
Java 编译器
如何在Java中使用Lambda表达式实现函数式编程
Lambda表达式是Java 8中引入的一种新的语法,它可以使我们以更简洁的方式编写函数式代码。本文将介绍如何在Java中使用Lambda表达式实现函数式编程,并通过实例展示其优点。
19 3