CUBは子供の白熊

Java SE 8 実践プログラミングの練習問題を解く

第7章 Nashorn JavaScript エンジンの活用 : 問題 2 : jjs でラムダ式

問題

jjs を実行し、Stream ライブラリを使用して次の問題に対する解法をインタラクティブに求めよ

あるファイルに含まれている長い単語(12文字より長い)を、重複なしで全てソートして表示せよ

解答

まず、Streamの要素をコンソールに表示して元のStreamを返す JavaScript の関数prを定義する

function pr(stream) {
    return stream.peek(function(x) { print(x) })
           .collect(java.util.stream.Collectors.toList())
           .stream();
}

                           注)実際には1行で記述する

関数prを使うと、(無限 Stream に対応できないが)Stream を生成するたびに中身を確認できる

では、第2章で使った "不思議の国のアリス" のテキストファイルでトライしてみよう

jjs での入力 コメント
var path = java.nio.file.Paths.get('alice.txt') ファイルのパス
var binary = java.nio.file.Files.readAllBytes(path) ファイルの読み込み
var contents = new java.lang.String(binary) 文字列へ変換
var words = contents.split(/\W+/) 単語に分割
var list = java.util.Arrays.asList(words) 配列 → List<String>
var st = list.stream() 単語の Stream
st = st.filter(function(w) w.length > 12) 長い単語を抽出
st = st.distinct() 重複を除去
st = st.sorted() ソート
st.forEach(function(w) { print(w) }) 表示

得られた結果は、次のとおり

CONSEQUENTIAL
Contributions
International
MERCHANTIBILITY
Multiplication
Redistributing
Redistribution
affectionately
circumstances
contemptuously
contributions
conversations
disappointment
electronically
extraordinary
identification
inquisitively
nonproprietary
opportunities
redistributing
redistribution
representations
straightening
transcription
uncomfortable
uncomfortably
unenforceability

おまけ

いや~、この練習問題ははまりました

■ 文字列contentsを単語に分解するところ

var contents = new java.lang.String(binary)

としたのでcontentsのデータ型はjava.lang.Stringだと思い込んでました
そこで、単語に分解するsplitjava.lang.Stringメソッド

public String[] split(String regex)

になると思い

var words = contents.split("[\P{L}]+")

とやったのです
ところがcontentsは、マーシャリングされて JavaScript の文字列になっているので、splitメソッドに渡す引数は JavaScript正規表現オブジェクトでなければならず

var words = contents.split(/\W+/)

が正しいんですね
… 引数に文字列を渡したときにエラーが起これば早く気づいたんですが

■ 配列wordsからjava.util.stream.Streamを得るところ

配列 → List<String> → Stream<String>

と二段階で Stream をゲットしているが、java.util.Arraysクラスのstreamメソッドを使えば一発じゃないの…
ところが

jjs> var st = java.util.Arrays.stream(words)
java.lang.RuntimeException: java.lang.NoSuchMethodException: Can't unambiguously select 
between fixed arity signatures [(double[]), (int[]), (long[]), (java.lang.Object[])] of
 the method java.util.Arrays.stream for argument types [jdk.nashorn.internal.objects.Na
tiveArray]

となっちゃいます
つまり “overload されていて、どのメソッドを選んでいいのか分からない” ということであえなく撃沈
Java.toメソッドを使って型変換すればいいのだが、別に配列が欲しい訳ではないのでList経由にしました

さらに問題

このインタラクティブな取り組み方は、普通のワークフローと比較してどうか?

解答

問題 1 と違って、それほど楽ではなかった

ラムダ式ないし Stream のフィルターは遅延実行なので、その場で中身を確認できない
かと言って中身を表示すると、それは終端操作なので Stream が閉じてしまい何もできなくなる

Stream の中身を確認する関数prを定義してからは楽になったが、ここまでするぐらいなら Java でいいかなというのが本音である