Java8のラムダ式と「Javaオブジェクトの集合(Collections)とそのソート」には、重要な関係があります。非常に簡単な例は下記のものです。
=====================================
// 学部名のリストを作り、その文字数で昇順に並べる。
import java.util.*;public class ListSort_Lambda{
public static void main(String... args){
List<String> list = Arrays.asList("E:工学部", "C:創造工学部",
"I:情報学部","A:応用バイオ科学部","N:看護学部" );
// sortの第⒉引数に(Lambda Expressions)を与える
Collections.sort(list, (s1, s2)-> s1.length() - s2.length());
System.out.println("学部名を文字数の昇順に列挙します:");
list.forEach(s -> System.out.println(s)); //内部反復にラムダ式
}
}
=====================================
上記のリストについての、詳細な説明は以下にありますので、ご覧いただければ幸いです。
http://kait-kbook.sakura.ne.jp/it/+6360-4/^06-Sort-of-List-Map(A)@(2016.5.23%2013.00-).pdf
I am a professor emeritus of CS at Kanagawa Institute of Technology, Japan. Originally my specialty was parallel and distributed systems. My current interests include machine learning, natural language processing, creating mobile apps with MIT App Inventor, and quantum computing. In the web version of this blog, clicking the icon on the right (a plastic sphere) will take you to the "List of Quantum Computing Articles". - Fujio Yamamoto (for e-mail, add "@ieee.org" after "yamamotof")
2017年5月14日日曜日
2017年5月13日土曜日
Java 8 ラムダ式の初歩(続き1)
前回の続編として、ラムダ式(Lambda Expressions)の続きを検討してみます。
下図は、自分が最も関心のあるキーワード(一つだけ)を含むタイトルの書籍を何冊持っているかを示しています。そのキーワード毎に、学生が持っている書籍数を合計するカウントプログラムを考えます。
![]() |
| 最も関心の高いキーワードを含む書籍を何冊持っているか? |
やりたいことは自明であり、とにかくプログラムを作ることはできます。しかし、ここでは、書籍のカウントの仕方を問題にします。よくあるプログラムでは、カウントするためのメソッド(関数)の内容は、「指定されたものをカウント(合計)する」のと「カウントすべきものは何か、それをどのような手順で決めるか」が混っていることがあります。
ここでは、後者の「カウントされるものが何か(どの本か)」を、前者の「カウントすること」から分離させることを考えます。それによって、書籍の種類を追加したり、合計したい本に何らかの条件を付けたりしても、前者の「「指定されたものをカウント(合計)する」関数は変更を受けないはずです。これは古くからあるデザインパタンの一つ(関心の分離:seperation of concerns)ですが、ラムダ式を用いることによって、いっそう柔軟な実装ができます。
以下のプログラムは、Venkat Subramaniam氏の書籍[1]を参考に作りました。このなかで、【方式1】は、氏の方式に沿ったものです。一方、【方式2】は、当方で独自に修正してみたものです。【方式1】では、カウント関数 countAを呼び出す際に、「何をカウントすべきか」を決める手続きをラムダ式で(t -> t.type == Type.Keras のように)渡している所に特徴があります。
一方、【方式2】は、関数呼び出しの度に同じような形のラムダ式を与えたくないので、ラムダ式自体を分離してみました。しかし、それは、カウントする関数 countBの中に含まれているので、「関心の分離」からは好ましくないようです。結論として、【方式1】に従うのがよさそうです。ただし、【方式2】もラムダ式の活用練習にはなるでしょう。
========================================
package designing.fpij;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import designing.fpij.Student.Type;
public class StudentManager2 {
// 【方式1】Venkat Subramaniamの書籍の方式(但し、以下のコードは山本自作)
public static int countA(List<Student> sts, Predicate<Student> selector){
return sts.stream().filter(selector).mapToInt(s -> s.number).sum();
}
// 【方式2】Venkat Subramaniamの方式の変更版(以下のコードは山本自作)
public static int countB(List<Student> sts, Type type){
Predicate<Student> selector = s -> type == null ? true: s.type == type;
return sts.stream().filter(selector).mapToInt(s -> s.number).sum();
}
public static void main(final String[] args) {
List<Student> students = Arrays.asList(
new Student(Type.Java8, 3), new Student(Type.Python, 1),
new Student(Type.NetLogo, 1), new Student(Type.Python, 2)
);
// 【方式1】を使った実行
System.out.println("total:" + countA(students, t->true));
System.out.println("Keras:" + countA(students, t->t.type==Type.Keras));
System.out.println("Python:" + countA(students, t->t.type==Type.Python));
// 【方式2】を使った実行
System.out.println("total:" + countB(students, null));
System.out.println("Keras:" + countB(students, Type.Keras));
System.out.println("Python:" + countB(students, Type.Python));
}
}
class Student {
public enum Type {Java8, Python, NetLogo, Keras };
protected Type type;
protected int number;
public Student(Type type, int number) {
this.type = type;
this.number = number;
}
}
========================================
実行結果:【方式1】,【方式2】とも同一
実行結果:【方式1】,【方式2】とも同一
total:7
Keras:0
Python:3
Python:3
========================================
[参考文献]
[1] Venkat Subramaniam : Functional Programming in Java, O'Reilly, 2014.
(O'Reilly Japanから、和訳本も出版されている)
2017年5月10日水曜日
Java 8 ラムダ式の初歩
3年生の実験1のひとつのテーマとして、「実践Java」をやっています。ここでは、2年生授業「Java言語」の復習を行ったあと、2年生ではやっていなかった「ラムダ式とストリーム」(Lambda Expressions and Streams)に取り組みます。先週から、このラムダ式に取りかかりました。今後は、これらの概念と利用の普及がより進むものと考えられます。
そこで、ここでは、ご存じない方のために、簡単な例で(どんな雰囲気かを)説明してみます。以下のような簡単な問題を考えます。ボタンが3つ(黄、青、赤)あります。いずれかのボタンを押すと、背景がボタンに応じた色に変わるというものです。
これを、2年生Javaまでの知識でプログラムを書くと、以下のようになります。ボタンを押したというイベントの処理プログラムになっています。ボタンイベントを処理するための内部クラスInListenerを作っているところポイントのひとつです。また、この内部クラスのオブジェクトisを作って、それを各ボタンのイベントリスナ(それが発生したイベントを処理してくれるオブジェクト)として登録しています。
// プログラム1:通常のJava SE7のプログラム----------------------
class BackColor_Basic extends JPanel{
JButton yB, bB, rB;
BackColor_Basic(){
yB = new JButton("黄");
bB = new JButton("青");
rB = new JButton("赤");
add(yB);
add(bB);
add(rB);
InListener is = new InListener();
yB.addActionListener(is);
bB.addActionListener(is);
rB.addActionListener(is);
}
class InListener implements ActionListener{
public void actionPerformed(ActionEvent e){
Color c;
if(e.getSource() == yB){
c = Color.yellow;
}else if (e.getSource() == bB){
c = Color.blue;
}else if (e.getSource() == rB){
c = Color.red;
}else{
c = Color.black;
}
setBackground(c);
}
}
public static void main(String args[]) {
//固定的なので省略
}
}
// ここまで ------------------------------------------------
上記プログラム1は、何ら問題なく、意図通りの動作をします。これと同じ内容をJava SE8のラムダ式を使って書くと、以下のようになります。(プログラム2はその一例ですが)プログラム1と比べて、短くなっているだけではありません。ラムダ式が2重に使われているところに注目して下さい。
特に、「オブジェクトがくるべきところが、関数らしきものに置き換わっている」点が特徴的です。関数型っていうのはこのことを指すようです。Javaのラムダ式は、「突然、予期せぬものが出て来て、不意を突かれる」ような感覚もありますが、少し馴れると、その簡潔さと美しさにとらわれます!
// プログラム2:Java SE8のラムダ式を使ったプログラム-------------
class BackColor_Lambda2 extends JPanel{
BackColor_Lambda2(){
BiConsumer<JButton,Color> setBackC = (b, c) ->
{ add(b);
b.addActionListener(e -> setBackground(c));
};
setBackC.accept(new JButton("黄"), Color.yellow );
setBackC.accept(new JButton("青"), Color.blue );
setBackC.accept(new JButton("赤"), Color.red );
}
public static void main(String args[]) {
//固定的なので省略
}
}
そこで、ここでは、ご存じない方のために、簡単な例で(どんな雰囲気かを)説明してみます。以下のような簡単な問題を考えます。ボタンが3つ(黄、青、赤)あります。いずれかのボタンを押すと、背景がボタンに応じた色に変わるというものです。
![]() |
| 例:押したボタンに応じて背景色を変える |
これを、2年生Javaまでの知識でプログラムを書くと、以下のようになります。ボタンを押したというイベントの処理プログラムになっています。ボタンイベントを処理するための内部クラスInListenerを作っているところポイントのひとつです。また、この内部クラスのオブジェクトisを作って、それを各ボタンのイベントリスナ(それが発生したイベントを処理してくれるオブジェクト)として登録しています。
// プログラム1:通常のJava SE7のプログラム----------------------
class BackColor_Basic extends JPanel{
JButton yB, bB, rB;
BackColor_Basic(){
yB = new JButton("黄");
bB = new JButton("青");
rB = new JButton("赤");
add(yB);
add(bB);
add(rB);
InListener is = new InListener();
yB.addActionListener(is);
bB.addActionListener(is);
rB.addActionListener(is);
}
class InListener implements ActionListener{
public void actionPerformed(ActionEvent e){
Color c;
if(e.getSource() == yB){
c = Color.yellow;
}else if (e.getSource() == bB){
c = Color.blue;
}else if (e.getSource() == rB){
c = Color.red;
}else{
c = Color.black;
}
setBackground(c);
}
}
public static void main(String args[]) {
//固定的なので省略
}
}
// ここまで ------------------------------------------------
上記プログラム1は、何ら問題なく、意図通りの動作をします。これと同じ内容をJava SE8のラムダ式を使って書くと、以下のようになります。(プログラム2はその一例ですが)プログラム1と比べて、短くなっているだけではありません。ラムダ式が2重に使われているところに注目して下さい。
setBackC = (b, c) -> { ... ( e -> ... ) };
特に、「オブジェクトがくるべきところが、関数らしきものに置き換わっている」点が特徴的です。関数型っていうのはこのことを指すようです。Javaのラムダ式は、「突然、予期せぬものが出て来て、不意を突かれる」ような感覚もありますが、少し馴れると、その簡潔さと美しさにとらわれます!
// プログラム2:Java SE8のラムダ式を使ったプログラム-------------
class BackColor_Lambda2 extends JPanel{
BackColor_Lambda2(){
BiConsumer<JButton,Color> setBackC = (b, c) ->
{ add(b);
b.addActionListener(e -> setBackground(c));
};
setBackC.accept(new JButton("黄"), Color.yellow );
setBackC.accept(new JButton("青"), Color.blue );
setBackC.accept(new JButton("赤"), Color.red );
}
public static void main(String args[]) {
//固定的なので省略
}
}
// ここまで -------------------------------------------------
今後、また、「ラムダ式とストリーム」について書いて行きたいと思います。時期は未定ですが。
2017年4月27日木曜日
小さなことだが大きい:リスト出力で最後にカンマを付けない
今回は、Java SE8のあるチップスについて書きます。ご存じの方も多いかも知れませんが、知らないと損します。「何らかのコレクション(リストなど)の要素をカンマで区切って並べて出力する際に、最後にカンマを付けたくない!」です。例えば、以下のリストを考えます。
◎ 方法1 ----------------------------------------
これだと、最後の要素の後ろにも", "が付いてしまいます。
◎ 方法2 ----------------------------------------
最後の要素の後ろに", "が付かないようにしました。but、美しくない!
◎ 方法3 ----------------------------------------
SE8のString.joinを使うと一発です。SE8のstream処理にも適する。美しい!
final List<String> planet =
Arrays.asList("水星", "金星", "地球", "火星", "木星");
これだと、最後の要素の後ろにも", "が付いてしまいます。
for(String name: planet)
System.out.print(name + ", ");
System.out.println();
出力結果:水星, 金星, 地球, 火星, 木星,
出力結果:水星, 金星, 地球, 火星, 木星,
◎ 方法2 ----------------------------------------
最後の要素の後ろに", "が付かないようにしました。but、美しくない!
for(int i = 0; i < planet.size() -1; i++)
System.out.printf(planet.get(i) + ", ");
if(planet.size() > 0)
System.out.println(planet.get(planet.size() -1));
出力結果:水星, 金星, 地球, 火星, 木星
出力結果:水星, 金星, 地球, 火星, 木星
◎ 方法3 ----------------------------------------
SE8のString.joinを使うと一発です。SE8のstream処理にも適する。美しい!
System.out.println(String.join(", ", planet));
出力結果:水星, 金星, 地球, 火星, 木星
出力結果:水星, 金星, 地球, 火星, 木星
2017年4月20日木曜日
TensorFlowのさらに上位のTFLearn
Deep Learningを作るための定番プログラミング言語はPythonのようです。さらにその上に抽象化されたフレームワークTensorFlowがあります。それをさらにもっと抽象化したTFLearnというのがあります。さっそく、下記のチュートリアルを読んで、環境設定し、例題の学習と検証をやってみました。
http://tflearn.org/tutorials/quickstart.html
例題が分かりやすくて、面白いものでしたので、ご存じかもしれませんが以下に記します。タイタニック号が沈んで、ほとんどの乗客、乗員が海へ沈んだりして亡くなったのですが、生き残った人(surviving)もいます。約1,300人くらいの人についての「生存/死亡、客室クラス、性別、年齢、... 」などのデータセットが公開されています。これらのどんな特性の人が生存したのか、死亡したのかを学習させるニューラルネットワークを作る問題です。
このTFLearnでそれを書いています。3層のフル結合ニューラルネットワークにした場合、以下のとおり、僅か数行でできています。(以下のソースリストは、上記URLで示されているものです。)すばらしい!
# Build neural network net = tflearn.input_data(shape=[None, 6]) net = tflearn.fully_connected(net, 32) net = tflearn.fully_connected(net, 32) net = tflearn.fully_connected(net, 2, activation='softmax') net = tflearn.regression(net)
学習の実行も以下の2行ですんでいます。これもすばらしい!
# Define model model = tflearn.DNN(net) # Start training (apply gradient descent algorithm) model.fit(data, labels, n_epoch=10, batch_size=16, show_metric=True)
その学習の状況は以下のようになりました。
===== 学習状況 (終盤のみ)===========================
Training Step: 1634 | total loss: 0.52194 | time: 0.255s
| Adam | epoch: 020 | loss: 0.52194 - acc: 0.7666 -- iter: 1216/1309
Training Step: 1635 | total loss: 0.50945 | time: 0.258s
| Adam | epoch: 020 | loss: 0.50945 - acc: 0.7775 -- iter: 1232/1309
Training Step: 1636 | total loss: 0.50597 | time: 0.260s
| Adam | epoch: 020 | loss: 0.50597 - acc: 0.7747 -- iter: 1248/1309
Training Step: 1637 | total loss: 0.49217 | time: 0.265s
| Adam | epoch: 020 | loss: 0.49217 - acc: 0.7848 -- iter: 1264/1309
Training Step: 1638 | total loss: 0.47261 | time: 0.268s
| Adam | epoch: 020 | loss: 0.47261 - acc: 0.8000 -- iter: 1280/1309
Training Step: 1639 | total loss: 0.47787 | time: 0.270s
| Adam | epoch: 020 | loss: 0.47787 - acc: 0.7888 -- iter: 1296/1309
Training Step: 1640 | total loss: 0.47622 | time: 0.273s
| Adam | epoch: 020 | loss: 0.47622 - acc: 0.7974 -- iter: 1309/1309
最後に、興味深いのは、テストの結果です。後に作られたタイタニック号の映画の主演男女、デカプリオ(DiCaprio)とウインスレ(Winslet)の生存確率です。このふたりに、上記の特性データを付与して、計算させています。映画の結末と合致して、DiCaprioは亡くなるが、Winsletは生き残るという結果がでました。単なる偶然とは思われない!
===== 主演男女の生存確率 ===============
DiCaprio Surviving Rate: 0.138
Winslet Surviving Rate: 0.904
======================================
http://tflearn.org/tutorials/quickstart.html
![]() |
| タイタニック号(WikiPediaより引用) |
このTFLearnでそれを書いています。3層のフル結合ニューラルネットワークにした場合、以下のとおり、僅か数行でできています。(以下のソースリストは、上記URLで示されているものです。)すばらしい!
# Build neural network net = tflearn.input_data(shape=[None, 6]) net = tflearn.fully_connected(net, 32) net = tflearn.fully_connected(net, 32) net = tflearn.fully_connected(net, 2, activation='softmax') net = tflearn.regression(net)
学習の実行も以下の2行ですんでいます。これもすばらしい!
# Define model model = tflearn.DNN(net) # Start training (apply gradient descent algorithm) model.fit(data, labels, n_epoch=10, batch_size=16, show_metric=True)
その学習の状況は以下のようになりました。
===== 学習状況 (終盤のみ)===========================
Training Step: 1634 | total loss: 0.52194 | time: 0.255s
| Adam | epoch: 020 | loss: 0.52194 - acc: 0.7666 -- iter: 1216/1309
Training Step: 1635 | total loss: 0.50945 | time: 0.258s
| Adam | epoch: 020 | loss: 0.50945 - acc: 0.7775 -- iter: 1232/1309
Training Step: 1636 | total loss: 0.50597 | time: 0.260s
| Adam | epoch: 020 | loss: 0.50597 - acc: 0.7747 -- iter: 1248/1309
Training Step: 1637 | total loss: 0.49217 | time: 0.265s
| Adam | epoch: 020 | loss: 0.49217 - acc: 0.7848 -- iter: 1264/1309
Training Step: 1638 | total loss: 0.47261 | time: 0.268s
| Adam | epoch: 020 | loss: 0.47261 - acc: 0.8000 -- iter: 1280/1309
Training Step: 1639 | total loss: 0.47787 | time: 0.270s
| Adam | epoch: 020 | loss: 0.47787 - acc: 0.7888 -- iter: 1296/1309
Training Step: 1640 | total loss: 0.47622 | time: 0.273s
| Adam | epoch: 020 | loss: 0.47622 - acc: 0.7974 -- iter: 1309/1309
最後に、興味深いのは、テストの結果です。後に作られたタイタニック号の映画の主演男女、デカプリオ(DiCaprio)とウインスレ(Winslet)の生存確率です。このふたりに、上記の特性データを付与して、計算させています。映画の結末と合致して、DiCaprioは亡くなるが、Winsletは生き残るという結果がでました。単なる偶然とは思われない!
===== 主演男女の生存確率 ===============
DiCaprio Surviving Rate: 0.138
Winslet Surviving Rate: 0.904
======================================
2017年4月10日月曜日
Webページをスマホで勉強する
Webには、プログラミング関係他、IT技術を学べる優れたコンテンツが多数存在します。それをスマホで見れば、どこでも、いつでも勉強できます。これをぜひ、活用したいですね。スマホのブラウザで、該当Webページを見ると、スマホ画面用に(文字も大きくして)最適化表示してくれるサイトも増えているようです。
しかし、それだけだと、ちょっと足りません。例えば、幾つかの章(項目)がある場合、何時それを見て勉強したか、そのときの理解の程度はどうだったか、等を簡単に記録できれば、復習する際にとても有用です。何事も、復習、復習で身につきます!
多くのWebページに対してそういうことをしたいので、一応、汎用的?スマホアプリ(Androud用)を目指して作って見ました。今回の具体例は、下記のC++入門です。
◎このアプリとその説明書は、下記で公開していますので、お試しいただければ嬉しいです。
しかし、それだけだと、ちょっと足りません。例えば、幾つかの章(項目)がある場合、何時それを見て勉強したか、そのときの理解の程度はどうだったか、等を簡単に記録できれば、復習する際にとても有用です。何事も、復習、復習で身につきます!
多くのWebページに対してそういうことをしたいので、一応、汎用的?スマホアプリ(Androud用)を目指して作って見ました。今回の具体例は、下記のC++入門です。
[原典]
小林健一郎教授「C++入門」
(↑アクセス回数が300万回を越える(2017-4-8現在)人気のWebページです。)![]() |
Webページに対応した目次を作り、List Pickerから選択可能にする |
![]() |
Androidのブラウザで読んで勉強する(文字サイズは最適表示になっている) |
![]() |
その章を前回読んだ際に書いたメモを表示したりできる |
登録:
投稿 (Atom)





