天天看點

使用Java 8函數式程式設計生成字母序列

在 java 8 中使用函數式程式設計生成字母序列是一個很大的挑戰。lukas eder 愉快地接受了這個挑戰,他将告訴我們如何使用 java 8 來生成abc的序列——當然,肯定不是一種蹩腳的方式。

使用Java 8函數式程式設計生成字母序列

1

2

3

<code>我正在尋找一種生成下列字母序列的方式:</code>

<code>a, b, c, ..., z, aa, ab, ac, ..., zz.</code>

大家應該能夠很快認出這是 excel spreadsheet 的頭部,準确的樣子如下:

使用Java 8函數式程式設計生成字母序列

首先,我們用函數的方式分解這個算法。我們所需要的元件有:

1、一個(可重複)的字母表。

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

3、一種将字母表中的字母與先前生成的字母聯合成一個笛卡爾積(cartesian product)的方法。

讓我們看一下代碼:

1、生成字母表

我們可以這樣寫入字母表,如:

<code>list&lt;string&gt; alphabet = arrays.aslist(</code><code>"a"</code><code>,</code><code>"b"</code><code>, ...,</code><code>"z"</code><code>);</code>

但這很差勁。我們使用 jooλ 代替:

4

<code>list&lt;string&gt; alphabet = seq</code>

<code>    </code><code>.rangeclosed(</code><code>'a'</code><code>,</code><code>'z'</code><code>)</code>

<code>    </code><code>.map(object::tostring)</code>

<code>    </code><code>.tolist();</code>

目前為止,一切都很好。現在:

2、使用上邊界:

要求的字元序列包括:

<code>a .. z, aa, ab, .. zz</code>

但是我們應該很容易想到擴充該需求,能生成如下字元序列,或者更多:

<code>a .. z, aa, ab, .. zz, aaa, aab, .. zzz</code>

是以,我們将再次使用 rangeclosed():

<code>// 1 = a .. z, 2 = aa .. zz, 3 = aaa .. zzz</code>

<code>seq.rangeclosed(</code><code>1</code><code>,</code><code>2</code><code>)</code>

<code>   </code><code>.flatmap(length -&gt; ...)</code>

<code>   </code><code>.foreach(system.out::println);</code>

這種方法是為範圍[1..2]中每個長度生成一個單獨的流,然後再将這些流合并到一個流中。flatmap() 的本質與指令式程式設計(imperative programming)中的嵌套循環類似。

3、合并字母到一個笛卡爾積中

這是最棘手的部分:我們需要合并字元及出現的次數。是以,我們将使用如下的流:

5

<code>seq.rangeclosed(</code><code>1</code><code>, length -</code><code>1</code><code>)</code>

<code>   </code><code>.foldleft(seq.seq(alphabet), (s, i) -&gt;</code>

<code>       </code><code>s.crossjoin(seq.seq(alphabet))</code>

<code>        </code><code>.map(t -&gt; t.v1 + t.v2))</code>

<code>    </code><code>);</code>

我們再次使用 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 到控制台的程式:

6

7

8

9

10

11

12

13

14

15

16

17

<code>import</code> <code>org.jooq.lambda.seq;</code>

<code>public</code> <code>class</code> <code>test {</code>

<code>    </code><code>public</code> <code>static</code> <code>void</code> <code>main(string[] args) {</code>

<code>        </code><code>int</code> <code>max =</code><code>3</code><code>;</code>

<code>        </code><code>list&lt;string&gt; alphabet = seq</code>

<code>            </code><code>.rangeclosed(</code><code>'a'</code><code>,</code><code>'z'</code><code>)</code>

<code>            </code><code>.map(object::tostring)</code>

<code>            </code><code>.tolist();</code>

<code>        </code><code>seq.rangeclosed(</code><code>1</code><code>, max)</code>

<code>           </code><code>.flatmap(length -&gt;</code>

<code>               </code><code>seq.rangeclosed(</code><code>1</code><code>, length -</code><code>1</code><code>)</code>

<code>                  </code><code>.foldleft(seq.seq(alphabet), (s, i) -&gt;</code>

<code>                      </code><code>s.crossjoin(seq.seq(alphabet))</code>

<code>                       </code><code>.map(t -&gt; t.v1 + t.v2)))</code>

<code>           </code><code>.foreach(system.out::println);</code>

<code>    </code><code>}</code>

<code>}</code>

聲明

對于這個問題,這确實不是最優的算法。在stack overflow,有一個匿名使用者給出了一種最好實作方法。

<code>import</code> <code>static</code> <code>java.lang.math.*;</code>

<code>private</code> <code>static</code> <code>string getstring(</code><code>int</code> <code>n) {</code>

<code>    </code><code>char</code><code>[] buf =</code><code>new</code> <code>char</code><code>[(</code><code>int</code><code>) floor(log(</code><code>25</code> <code>* (n +</code><code>1</code><code>)) / log(</code><code>26</code><code>))];</code>

<code>    </code><code>for</code> <code>(</code><code>int</code> <code>i = buf.length -</code><code>1</code><code>; i &gt;=</code><code>0</code><code>; i--) {</code>

<code>        </code><code>n--;</code>

<code>        </code><code>buf[i] = (</code><code>char</code><code>) (</code><code>'a'</code> <code>+ n %</code><code>26</code><code>);</code>

<code>        </code><code>n /=</code><code>26</code><code>;</code>

<code>    </code><code>return</code> <code>new</code> <code>string(buf);</code>

不用說,這個算法比之前的函數式算法會快很多。

來源:51cto