發(fā)布于:2021-01-21 13:42:33
0
255
0
我偶然發(fā)現(xiàn)了用戶提出的一個有趣的堆棧溢出問題。問題是:我正在尋找一種生成字母序列的方法:
A, B, C, ..., Z, AA, AB, AC, ..., ZZ.
這可以很快被識別為Excel電子表格的標(biāo)題,它正好做到:
到目前為止,沒有一個答案使用任何java8函數(shù)式編程,我認(rèn)為這是一個挑戰(zhàn)。我們將使用jOOλ,因?yàn)閖ava8流API沒有為這個任務(wù)提供足夠的功能。
但是首先,讓我們以函數(shù)的方式分解算法。我們需要的是這些組件:
字母的(可再現(xiàn))表示
上限,即我們要產(chǎn)生多少個字母。請求的序列轉(zhuǎn)到ZZ,這意味著上限為2
一種將笛卡爾積中的每個字母與先前生成的組合字母進(jìn)行組合的方法
讓我們看一些代碼:
1.生成字母
我們可以這樣寫字母:
List<String> alphabet = Arrays.asList("A", "B", ..., "Z");
但這很low。讓我們使用jOOλ生成它:
List<String> alphabet = Seq .rangeClosed('A', 'Z') .map(Object::toString) .toList();
上述產(chǎn)生一“關(guān)閉”范圍(java8-流發(fā)言與包容上限的范圍內(nèi))的字符之間A和Z,映射字符串和收集它們到一個列表中。
2.使用上限
請求的字符序列包括:
A..Z,AA,AB,.. ZZ
但是我們可以輕易地想象將這一要求擴(kuò)展到產(chǎn)生以下甚至更多的需求。
A..Z,AA,AB,.. ZZ,AAA,AAB,.. ZZZ
為此,我們將再次使用rangeClosed()
:
// 1 = A .. Z, 2 = AA .. ZZ, 3 = AAA .. ZZZ Seq.rangeClosed(1, 2) .flatMap(length -> ...) .forEach(System.out::println);
這里的想法是為該范圍內(nèi)的每個長度生成一個新的流[1 .. 2],并將這些流展平為一個單個流。flatMap()本質(zhì)上與命令式編程中的嵌套循環(huán)相同。
3.將字母組合成笛卡爾積
這是最棘手的部分:我們需要將每個字母與每個字母組合length一次。為此,我們將使用以下流:
Seq.rangeClosed(1, length - 1) .foldLeft(Seq.seq(alphabet), (s, i) -> s.crossJoin(Seq.seq(alphabet)) .map(t -> t.v1 + t.v2)) );
我們再次使用rangeClosed()產(chǎn)生范圍內(nèi)的值[1 .. length-1]。foldLeft()與相同reduce(),不同之處在于foldLeft()可以保證在流中從左向右移動,而無需折疊功能具有關(guān)聯(lián)性。
換句話說,更容易理解的詞foldLeft()是:命令循環(huán)。循環(huán)的“種子”,即循環(huán)的初始值,是完整的字母(Seq.seq(alphabet))。現(xiàn)在,對于該范圍內(nèi)的每個值[1 .. length-1],我們crossJoin()在到目前為止“已折疊”的字母和一個新字母之間產(chǎn)生一個笛卡爾乘積(),并將每個組合連接成一個新的字符串(t.v1和t.v2)。
結(jié)合一切
以下簡單程序?qū)⑺兄祻闹写蛴 .. Z, AA .. ZZ, AAA .. ZZZ到控制臺:
import java.util.List; 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); } }
免責(zé)聲明
對于這種特殊情況,這當(dāng)然不是最佳算法。一名不知名的用戶在Stack Overflow上給出了最好的實(shí)現(xiàn)之一:
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); }
不必說后者比以前的功能算法快得多。
作者介紹