Erlang 基礎ポイント3 - case式, if式 例外
case式
Erlang 基礎ポイント2 - DukeLabでパターン集合について説明しました。
パターン集合の結果から処理を複数の関数に分けることができましたが、
関数の数が無駄に多くなる恐れがありますね。
もちろん、プログラムの処理は可能な限り小さくした方が
モジュール化側面でいいでしょうが、やりすぎると、
かえって複雑なプログラムになりかねないですね。
case式を使うと、一つの関数内で処理部を分けておくことができます。
Javaのswitch caseのようなものですが、判断条件に固定値だけでなく、パターンが使えるので、
もっと強力です。
まず、例をみてみましょう。
前のguard_module.erlがErlang 基礎ポイント2 - DukeLabのパターン集合節で紹介したもので
後のcase_test.erlがcase式を使うように修正したものです。
% guard_module.erl -module(guard_module). -export([guard_test/1]). guard_test(X) when X < 5, X > 1 -> X * X; guard_test(X) when X >= 5; X < 10 -> X - 2; guard_test(X) -> X - 2. % case_test.erl -module(case_test). -export([guard_test/1]). guard_test(X) -> case X of X when X < 5, X > 1 -> X * X; X when X >= 5; X < 10 -> X - 2; X -> X - 2 end.
caseの使い方は次のようです。
他のプログラミングの経験があるなら、問題ないでしょう。
case 評価対象 of パターン1 [when ガード...] -> 結果; ... パターンn [when ガード...] -> 結果 end
最後のパターンにはセミコロンをつけないことに注意して下さい。
評価対象に判断対象(関数引数・変数など)を指定し、
パターンnに判断対象と照合するパターンを書きます。
必要ならガードに追加条件を指定できますが、ガードは省略可能です。
評価対象がパターンに一致すれば、結果が返されます。
一致するパターンがないと、例外が発生するので、注意しましょう。
caseの方が関数の長さが増えましたが、関数の数は減りましたね。
どちらも優先的に使うのではなく、関数の数とcaseの数をバランスよく
配置すればいいと思います。
if式
他のプログラミング言語のif文と同じですが、違いは式なので、戻り値があるということです。
上記のcase式のサンプルをif式に修正したものを載せます。
% if_test.erl -module(if_test). -export([if_test/1]). if_test(X) -> if X < 5, X > 1 -> X * X; X >= 5, X < 10 -> X - 2; true -> X - 3; end. % if_testをREPL環境で試す。 19> c(if_test). {ok,if_test} 20> if_test:if_test(7). 5 21> c(if_test). {ok,if_test} 22> if_test:if_test(3). 9 23> if_test:if_test(6). 4 24> if_test:if_test(-1). -4
if式の文法は他のプログラミング言語のif文と似ていますが、ちょっと違いますね。
まず、複数個の条件を書く時、ifと複数書く必要がありません。
最初にifと書いて、case式のように条件と結果を次々と書いていけばいいです。
それから、最後の条件にtrueと書きましたね。これはelseと考えればいいです。
Erlangのifにはelseがありません。なので、全ての条件に一致しない時の条件を
直接書く必要がありますが、trueと書いておけばOKです。
case 評価対象 -> パターン1 [when ガード...] -> 結果; ... パターンn [when ガード...] -> 結果 end
trueを書かないで、全ての条件に一致しない場合、例外が発生しますので、注意しましょう。
例外
Erlangで例外処理は、ちょっと形式は違いますが、Javaと同じくtry-catch式を使います。
以下の例を見ながら、try-catchを身につけましょう。
% trycatch.erl -module(trycatch). -export([go/1]). go(X) -> try test(X) of Val -> {value, Val} catch throw:exception1 -> io:format("Catch Exception_Test1~n"); throw:exception2 -> io:format("Catch Exception_Test2~n"); exit:_ -> io:format("Catch Exit~n"); error:_ -> io:format("Catch Error~n") after io:format("After~n") end . test(X) -> case X of 1 -> throw(exception1); 2 -> throw(exception2); 3 -> exit(unknown); 4 -> error(unknown) end . % trycatchをREPL環境で試す。 1> c(trycatch). {ok,trycatch} 2> trycatch:go(1). Catch Exception_Test1 After ok 3> trycatch:go(2). Catch Exception_Test2 After ok 4> trycatch:go(3). Catch Exit After ok 5> trycatch:go(4). Catch Error After ok
関数goに1から4までの数字を入れると、例外(throw)2種類、プロセス終了(exit)1種類、エラー(error)1種類を演じてくれます。
throwは呼出元が例外を処理することを期待する時、呼出元に対してエラーをなげる関数です。JavaのChecked Exceptionと似ていますね。
但し、throwでエラーを発生させても、呼出元がtry-catchでエラーを捉えなくても実行はできます。
Javaのようにコンパイルエラーは発生しません。
プログラムで捉えられなかったエラーはErlangが捉えます。その場合、スタックトレースのようなものが表示されるでしょう。
exitはプロセスを終了させたい時、呼出元にエラーをなげる関数です。try-catchでこのエラーを捉えないと、プロセスが終了してしまいます。
errorは呼出元が例外を処理できないような致命的なエラーが発生したことを知らせるため関数です。
Javaでいうと、RuntimeExceptionのようなものでしょうか。
よく見るとErlangのtry-catchはcaseと似ていることが分かります。
tryとcatchの間のコードがまさにcaseのような形式、その後、catchとafterが追加されていますね。
catchはJavaのcatchと同じで、afterはJavaのfinallyのようなものです。
以下はtry-catch式の使い方です。
try 評価対象 of パターン1 [when ガード...] -> 結果; ... パターンn [when ガード...] -> 結果 catch 例外種類1(throw, exit, error): 例外パターン1 [when ガード1] -> 結果1; ... 例外種類n(throw, exit, error): 例外パターンn [when ガードn] -> 結果n after catch結果に関係なく、返す結果 end
Erlangのtry-catchはJavaと違って、式なので戻り値があります。
今日はここまで。
次はErlangでの並行処理について書きます。
[WebSocket] [Java] WebSocketを試す
WebSocketとは
HTML5仕様を構成する要素の一つで一方向通信しかできなかった既存のHTML通信とは違って、
JavascriptのWebSocket APIとサーバサイドのWebSocket機能でクライアントとサーバ間の両方向通信を可能にする技術だ。
WebSocketの特徴
WebSocketは、通信ごとにHTTPコネクションを開く・閉じるのを繰り返すのではなく、
いったんHTTPコネクションを開いたら、コネクションを開いたままにしておいて、データをやりとりする。
コネクションが維持される間は、Ajaxのようにクライアントからサーバへの通信だけでなく、
サーバからクライアントに直接データを送ることもできる。
以下は、ChromeでWebSocketの公式サイトにあるデモを試したキャプチャである。
"Rock it with HTML5 WebSocket"という文字列を何回もSendしているのにコネクションは一つ(?encoding=text)しか発生していない。
通信の内容はFramesタブをクリックすれば、分かる。
WebSocketのAPIについて
サーバAPI
昔はサーブレットコンテナごとに違うWebSocketのAPIがあったが、
今は、JSR 356: JavaTM API for WebSocketという標準ができて、
各ベンダが自分たちのサーブレットコンテナに実装している。
標準APIがあるので、ベンダ依存のAPIは使わないこと。
WebSocketサーバプログラムはWeb(サーブレット・JSP)でもStandalone(mainメソッド)でも作れる。
JavaでWebSocketサーバプログラムを実装するために以下のアノテーションとクラスを使用する。
- @ServerEndPoint
- @OnOpen
- @OnMessage
- @OnClose
- @PathParam
- Encoder
- Decoder
- 他にも色々。
クライアントAPI
WebSocketのクライアントプログラムはどの言語でも作れるが、ここではJavascriptを取り上げる。
Javascriptでは以下のように非常に単純で分かりやすいAPIが存在する。
- WebSocket(url)
- WebSocket.send
- WebSocket.onopen
- WebSocket.onmessage
- WebSocket.onerror
- WebSocket.onclose
サンプル作成
必要なもの
- WebSocketをサポートするサブレットコンテナ(Tomcat8, GlassFish、Jetty等)
- サーバプログラム(Java、JSP)
- クライアントプログラム(Javascript)
サンプル作成スタート
時間を節約するため、サンプルは早く作りたい。
今回はNetBeansを利用してサンプル作ろう。
Netbeansをダウンロード・インストールし、立ち上げて新規プロジェクトを作成する。
カテゴリでJava Web→Webアプリケーションを選択し、次へ、プロジェクト名を入力し、次へ、
サーバーと設定画面で終了ボタンを押すと、プロジェクトが作成される。
さあ、コーディングをしてみよう。
サーバプログラム
送ったメッセージをそのまま返すだけである。
package dukelab.websocket.demo; import javax.websocket.OnMessage; import javax.websocket.server.ServerEndpoint; /** * WebSocketデモ。 * * @author DukeLab */ // Webソケットのサーバ側クラスであること表すアノテーション。 // 引数(wsdemo)はクライアントから接続時、使われるURIを表す。 @ServerEndpoint(value = "/wsdemo") public class WebSocketDemo { @OnMessage public String onMessage(String text) { return text; } }
WebSocketのサーバプログラムは@ServerEndpointアノテーションをつけることで始まる。
@ServerEndpointの引数valueにはクライアントから接続時、使われるURIを指定する。
URIは/で始まらなければならない。
例えば、ウェブアプリケーションがlocalhost:8080で動いていてコンテキストパスがtestなら、
上記のサーバプログラムへのURLはws://localhost:8080/test/wsdemoになる。
ここでは指定していないが、@ServerEndpointアノテーションへの引数に、
メッセージ変換のためのencoder、decoderも指定できる。
本格的なアプリケーションを作る時に、encoderとdecoderが必要になりそうだ。
それから、@OnMessageアノテーションをメッセージを受信するメソッドに指定する。
メソッドの引数textがクライアントから受信したデータということだ。
onMessageメソッド内で色々処理を行って、その応答をreturnすればいい。
@OnMessageアノテーション以外に@OnOpen、@OnCloseもある。
@Onxxxアノテーションがつくメソッド(例 : onMessage(String Text))の引数や応答を返す方法は上記以外にもある。
リクエストが来た時だけでなく、サーバから自発的に送ることもできる。
それらは実装の状況に応じてドキュメントを参照すればいいだろう。
ここでは至極簡単なサンプルサーバプログラムを作った。
上記のクラスだけだ。
web.xmlとかサーブレットは要らない。
本当に便利だ。
クライアントプログラム
サンプルを実行したブラウザはChrome 33.0.1750.117である。
<!DOCTYPE html> <html> <head> <title>WebSocketDemo Client</title> <meta charset="UTF-8"> </head> <body> <script type="text/javascript"> WebSocketDemo = {}; (function(d) { function $(query) { return document.querySelector(query); } function printMessage(msg) { $("#msgbox").innerHTML += "<div>" + msg + "</div>"; } d.connect = function() { var ws = new WebSocket("ws://localhost:8080/WebApplication3/wsdemo"); ws.onmessage = function(event) { printMessage("Server : " + event.data); }; d.webSocket = ws; $("#connect").disabled = true; $("#send").disabled = false; }; d.send = function() { var msg = $("#msg").value; d.webSocket.send(msg); printMessage("Client : " + msg); }; }) (WebSocketDemo); </script> <form action="javascript:void(0);"> <input type="text" id="msg" size="20"> <input type="button" id="connect" value="Connect" onclick="WebSocketDemo.connect();"> <input type="button" id="send" value="Send" onclick="WebSocketDemo.send();" disabled> </form> <div id="msgbox" style="border-style: solid;width: 500px;height: 400px"></div> </body> </html>
Connectボタンを押すと、WebSocketオブジェクトを生成する。
WebSocketのコンストラクタにはスキームがws://又はwss://(SSLの場合)で始まるURLを指定する。
後、サーバからメッセージを受信すると、onmessageハンドラーが呼ばれる。
引数のオブジェクトのdataプロパティからメッセージの取得ができる。
Sendボタンを押すと、WebSocketオブジェクトのsendメソッドを呼び出してメッセージをサーバに送信する。
サーバより実装が簡単だ。
注意点は、IEの場合、WebSocketから使えるのはIE10からだということだ。
IE9まではWebSocketが使えない。
さらに進化したサンプル
上記までは、内部的にやり取りされるのが変わっただけで、既存のAjaxでも同じ機能のアプリケーションは作ることができる。
ここで、サーバから自発的にクライアントにメッセージを送る機能をつけてみよう。いわゆるPUSH型通信。
これはAjaxではできないのだ。
setIntervalで周期的にサーバに見に行くしかないのだ。いわゆるPULL型通信。
Ajax以外にCometというのもあるが、これはコネクション確立後、ただ、Pendingして待つだけだ。
実際の通信が送られるまで待つので、通信量を減らすことはできるが、サーバから応答が返ってくると、
また次のHTTPコネクションを確立しなければならない。
ずっと通信を確立しているWebSocketはAjaxとCometとは歴然と違うのだ。
このさらに進化したサンプルでは、制御用JSPを一個作って、そのJSPで何かを入力すると、
サーバから全てのクライアントにメッセージが送信される機能をつける。
以下は上記のWebSocketDemoクラスに機能を追加したものだ。
package dukelab.websocket.demo; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import javax.websocket.OnClose; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; /** * WebSocketデモ。 * * @author dukelab */ @ServerEndpoint(value = "/wsdemo") public class WebSocketDemo { private static Set<Session> ses = new CopyOnWriteArraySet<>(); @OnOpen public void onOpen(Session session) { System.out.println("onOpen : " + session); ses.add(session); } @OnMessage public String onMessage(String text) { return "echo => " + text; } @OnClose public void onClose(Session session) { System.out.println("onClose : " + session); ses.remove(session); } public static void sendMessage(String msg) { for (Session ses : ses) { ses.getAsyncRemote().sendText(msg); } } }
要は、接続時(onOpen)にセッションをスレッドセーフなコレクションに入れておいて、
そのセッションに対して操作をすればいい。
切断時(onClose)にセッションをスレッドセーフなコレクションから削除する処理は忘れないように。
<%@page import="dukelab.websocket.demo.WebSocketDemo"%> <%@page contentType="text/html" pageEncoding="utf-8"%> <!DOCTYPE html> <% boolean isPOST = request.getMethod().toLowerCase().equals("post"); if (isPOST) { WebSocketDemo.sendMessage(request.getParameter("msg")); } %> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>WebSocketDemo Control</title> </head> <body> <%= isPOST ? "Message was sent." : "" %> <form action="" method="post"> <input type="text" name="msg"> <input type="submit" name="submit" value="Send"> </form> </body> </html>
以下の画面で、左が制御用JSPで、右がエコデモである。
制御用JSPでメッセージを入力し、Sendボタンを押すと、全てのクライアントにメッセージが送信される。
これは、なかなか面白い。
Erlang 基礎ポイント2 - プログラムの形式, 無名関数, パターン照合など
Erlangプログラムの形式
REPL環境でなく、ソースファイル(erlファイル)から実行するためには
以下の形式に従う。
-module(test). -export([func1/0,func3/1,func4/3]). func1() -> func2() + 5. func2() -> 3. func3(X) -> Y = X * 2, Y * 2. func4(plus, X, Y) -> X + Y; func4(minus, X, Y) -> X - Y; func4(_, _, _) -> "nothing". % コメント
まず、module文にモジュール名を書く。
モジュール名はソースコードを含んでいるファイル名(拡張子はerl)にする。
モジュールはErlangコードの基本単位だ。
export文には外部からアクセスを許容する関数名・引数の数を記述する。
Javaにおけるpublicといえば、分かりやすい。
コンマで区切って複数指定可能。
次はJavaのようにメンバー変数が来そうだが、そんなものはない。
すぐ処理ロジックを含む関数が来る。
関数のシグネチャにリターンタイプとか、引数の型の指定がない。
関数名と引数名だけを書いて、->の後、すぐ処理内容を記述する。
関数の処理内容は行ごとにコンマで区切って、関数の最後にピリオドを打つ。
引数の数が同じ関数を複数(いわゆる、オーバーロード)書く場合、
各関数の終わりにコンマでなくセミコロンを書く。
但し、最後の関数の終わりにはコンマを書く。
型のチェックは実行時に行われる。
つまり、Erlangは動的型付け言語である。
コメントは%の後に書く。
無名関数
Erlangでは関数を無名で宣言して、変数に代入したり関数の引数や戻り値として使用することができる。
1> F = fun(A) -> A * A end. #Fun<erl_eval.6.80247286> 2> F(2). 4 3> Y = F. #Fun<erl_eval.6.80247286> 4> Y(4). 16
funで始まって、end.(ピリオドがあることに注意)で終わる。
次は無名関数を関数の引数に渡したり、戻り値として返す例だ。
% 引数 9> F = fun(A) -> A * A end. #Fun<erl_eval.6.80247286> 10> lists:map(F, [1, 2, 3, 4]). [1,4,9,16] % 戻り値 12> Plus = fun(X) -> (fun(Y) -> X + Y end) end. #Fun<erl_eval.6.80247286> 13> PlusFive = Plus(5). #Fun<erl_eval.6.80247286> 14> PlusFive(9). 14
リスト内包表記
リストから特定条件を満たす要素で構成された新しいリストを作るためのErlangの機能。
普通、リストから特定要素を持つ新しいリストを作るためには
新しい空のリストを宣言しておいて、ループと条件文を利用し、
条件にマッチした要素を空のリストに入れる処理が必要なので、
コードが長くて読みづらくなる。
しかし、リスト内包表記を使うと、リスト(例 : [ 1, 2, 3])の中で
新しいリストを作るための専用の表記ができるので、コードが短くて
読みやすい。
1> L = [1, 2, 3]. [1, 2, 3] 2> [X - 1 || X <- L]. [0, 1, 2].
後ろの部分、X <- LはリストLから要素Xを取り出すという意味で、
"||"の左側に要素Xで行う式を書く。
レコード
Cにおける構造体のようなもの。
Erlangでレコードの宣言方法は色々あるが、よく使われる方法は
拡張子がhrlのファイルにレコードを定義し、
そのhrlファイルをerlファイルで
インクルードする方法だ。
% person.hrl -record(person, { name, age, tel = 000-0000-0000 }).
% test.erl -module(test). -export([duke/0]). -include("person.hrl"). #インクルード duke() -> #person{name=duke, age=34, tel="0800000-0000"}.
レコードの宣言は、まず、-recordキーワードを書いて、
1番目の引数にレコード名、次に要素名のタプルを指定する。
要素にはデフォルト値の指定ができる。
レコードの使用は
#レコード名{要素1=値, ... , 要素x=値}
のようにする。
パターン照合
Erlang 基礎ポイント1 - DukeLabでちょっとだけ、触れたが、ここでもうちょっと説明しよう。
Erlangで、=は代入するのではなく、パターン照合するという。
以下の場合、Aに100を代入するのではなく、右側(100)の評価結果と左側のパターン(A)を照合するのだ。
A = 100
上記の例だと、ぱっとイメージしにくいだろう。
パターン照合とイメージが似ているのは正規表現ではないかな。
正規表現を使うと、ある複雑な文字列からパターンにマッチする特定の部分を手軽に抽出することができる。
ただし、正規表現のルールが分かりにくい短所はある。
同じくErlangのパターン照合も正規表現のようなものだが、
文字列だけでなく、タプル、リスト、関数の引数など、何にでも使えると考えればいいだろう。
以下の例を見てみよう。
19> T = {{name, "duke"}, {age, 34}}. {{name,"duke"},{age,34}} 20> {{_,_},{_,MyAge}} = T. {{name,"duke"},{age,34}} 21> MyAge. 34
Tにタプルが二つ({name, "duke"}と{age, 34})が入っているが、
二つ目のタプルから年齢(34)を取得したい場合、20>のように
パターン照合すればいい。20>の左側にあるパターンのうち、
_(アンダーバー)はダミー要素で何でもマッチする。
マッチするだけで変数に束縛したくない場合、_(アンダーバー)を使う。
パターンのうち、大文字で始まっているMyAgeが
二つめのタプルにある2番めの要素(34)に一致して
年齢(34)を取り出せたことが分かる。
パターン照合の基本が分かれば、後はマニュアルか
ググってパターン照合の例を参考すればいいだろう。
最後に関数のパターン照合に関する例を載せる。
% func_test.erl -module(func_test). -export([add/2]). add(X,plus) -> X + X; add(X,pow) -> X * X; add(X,Y) -> X + Y + 1. % 以下はREPL環境で実行したもの。 1> c(func_test). {ok,func_test} 2> func_test:add(3, plus). 6 3> func_test:add(3, pow). 9 4> func_test:add(3, 5). 9 5> c(func_test). {ok,func_test}
ガード
ある関数を呼び出すと、呼び出される関数を探すため、
引数に指定した値の型と関数のシグネチャとのパターン照合を行う。
Javaのオーバーロードと似ているものだと考えればよさそう。
追加的にメソッド内で引数の値の内容によって、違う動作をする必要がある時、
Javaではif文やポリモーフィズムを使うが、Erlangではガードを使えばいい。
ここでは関数を中心にガードを使い方を説明するが、
case式やif式でも使用できる。
以下のソースを見てみよう。
% guard_module.erl -module(guard_module). -export([guard_test/1]). guard_test(X) when X < 5, X > 1 -> X * X; guard_test(X) when X >= 5; X < 10 -> X - 2; guard_test(X) -> X - 2.
上記のソースを解析してみると、
呼び出された関数がguard_testで一番目の引数(X)に値が指定されていて、
Xが5未満かつ1より大きければ(コンマ[,]はandを意味)、X * Xを、
Xが5以上か10未満なら(セミコロン[;]はorを意味)、X - 2を、
それ以外の場合(つまり、elseなら)、X - 2
を返すという意味だ。
以下は上記の関数をREPL環境でテストしたものだ。
1> c(guard_module). {ok,guard_module} 2> guard_module:guard_test(5). 3 3> guard_module:guard_test(1). -1 4> guard_module:guard_test(6). 4 5> guard_module:guard_test(15). 13 6>
ガードは条件文のようなものだと考えればいい。
ガードは便利そうだが、関数の宣言部が複雑になる恐れがあるので、
なるべくパターン照合だけで済ませた方がいいと思う。
今日はここまで。
Javascript 注意点
Javascriptを使うにあたって
注意すべきところをメモっておこう。
会社、家でJavascriptプログラミング時に
経験したのを書くつもりだ。
もちろん、全て汎用的な内容だし、本に書いてある内容も
あるはずだ。
-----------------------------------------------
1)オブジェクトリテラルで最後の要素の後にコンマをつけてはならない。
=> IE7でエラーになる。
例)var o = { name : "Duke", hobby : ["movie", "animation", "reading", ""] , // ここにコンマを入れちゃだめ!!};
hobbyの次にコンマを入れるとIE7でエラーになる。
IE8とFireFox、Chromeではコンマを入れても問題ないが、
世の中未だIE6を使う人がいる現実を考えると
コンマを抜く方が精神健康上、いいだろう。
2)配列の最後の要素の後にもコンマをつけてはいけない。
いけないというか、ブラウザ間の動作が違うので避けましょうとの話だ。
今回もIEが問題だ。バージョン関係なく(正確にはIE7とIE8)。
var a = [1, 2, ];
console.log(a.length);
// 結果
// IE7 : 3
// IE8 : 3
// Chrome : 2
// FireFox : 2
IEでは最後のコンマの後をundefined要素と見なしているようだ。
ChromeかFireFoxでは本当の要素のみを配列の要素と見なしているようだ。
とにかく、結果が一つになる文法を使おう。
配列の最後の要素の後にコンマを入れちゃアカン!
3)with文は使っちゃだめ。
ある意味すごく便利なキーワードだが、間違った動作を起こす恐れがある。
(Visual BasicのWith-End Wih文と同じ感じ)
単純に参照名の省略だけでなく、内部で参照オブジェクトの有無を判別する
分岐文など、余分のコードが実行される。
コスト的にも問題だが、with文の内部がおかしくなると、バグを探すのが
非常に難しくなるので、使うのはやめた方がいい。
例えば、以下のコードは
with (obj) {
a = b;
}
実際は以下のコードと同じ処理をする。
f (obj.a === undefined) {
a = obj.b === undefined ? b : obj.b;
} else {
obj.a = obj.b === undefined ? b : obj.b;
}
出典
JavaScript: The Good Parts: Working with the Shallow Grain of JavaScript
- 作者: Douglas Crockford
- 出版社/メーカー: Yahoo Press
- 発売日: 2008/12/17
- メディア: Kindle版
- この商品を含むブログを見る
これを見ると分かるように実際どの処理が行われるかを
すく知ることはできない。
よって、with文は避けた方がいい。
4)parseInt使用する時、2番目の引数に進数を指定しなければならない。
var str = "051";
var a = parseInt(str);
console.log(a);
さあ、何が表示されるか。
51?
ブー。
41が表示される。
なぜなら、parseIntにソース文字列が何進数かを指定しないと、
文字列表現を見て自動判別するからだ。
前に0がつくと、8進数と判断する。
前に0xがつくと、16進数と判断する。
例の051は前に0がついているので8進数の51と判断し、
それを10進数の41に変換したのだ。
こういう自動判別を防ぐためには2番目の
引数に進数を指定すればいい。
var str = "051";
var a = parseInt(str, 10); // "strは10進数です"と指定する。
console.log(a);
結果は正しく51になる。
私たちが普段外からもらう数字は10進数なので、
parseIntの2番目の引数には必ず10をつけるように
しよう。
5)==, !=の代わりに===、!==を使おう。
Javascriptで==と!=は互いのオペランドの型が違う場合、
強制に型変換が行われる。
便利な面もあるが、分かりにくいバグを出してしまう場合もある。
以下の例のように想像と全然違う結果を出してしまう。
1)'' == '0' // false
2)0 == '' // true
3)0 == '0' // true
4)false == '0' // true
5)' \t\r\n ' == 0 // true
===と!==は型の変換が行われず、実際型までチェックするので
安心だ。
6)より安全なクラス宣言の仕方 --- 7)を参照すること。
Javascriptでクラス宣言する方法は色々ある。
次にPersonというJavaクラスがある。
public class Person {
private final String name;
private final int age;
public Person(String name, String age) {
this.name = name;
this.age = age;
}
public String getName() { return name;
}
public int getAge() {
return age;
}
}
上記のPersonクラスをJavascriptで書いてみると、
方法は色々あるが、二つだけ、挙げてみる。
1番目
var Person = function(name, age) {
this.name = name;
this.age = age;
};
Person.prototype = { // publicメソッド
getName : function() {
return this.name;
},
getAge : function() {
return this.age;
}
};
Personクラスの宣言とメンバーメソッドの宣言が分かれている。
長所 :
1)インスタンス生成時、prototypeに宣言されている内容を再利用するのでメモリ管理面で有利である。
短所 :
1)宣言部が分かれているので少々分かりにくい印象がある。
2)publicメソッドでprivateメソッドが呼べない。prototypeではthisで始まるメンバーしか呼べない。
2番目
var Person = function(name, age) {
this.name = name;
this.age = age;
// private field
var count = 0;
// private method
var getCalledCount = function() {
return count++;
}
// public method
this.getName = function() {
return this.name + " : " + getCalledCount();
};
this.getAge = function() {
return this.age;
};
};
長所:
1)宣言部が統合されているので分かりやすい(変数の使用スコープが把握しやすいので)。一般的なクラス宣言に近い。
2)publicメソッド内でprivateメソッドが呼べる。
長所:
1)インスタンス生成時(つまり、new時)、毎回メソッドを動的に生成するので遅い。
答えは出た。
Javascriptはプロトタイプ型OOP言語だが、まだprototypeに慣れていない人が多いので
無理して使っても、メンテナンスする人に文句ばかり言われるだろう。
で、2番目のような宣言方式を使えば無難ではないかと思われる。
速度の問題はあるが、これ以外の問題(間違ったJavascriptの使用を意味)がもっと多いはずなので、
別に問題にならないと思う。クライアントPCの性能もよくなったし。
まず、内容が把握しやすくてメンテナンスがしやすい2番目を取ってみて、
本当に遅くなったと感じた(おそらくないと思うのだが)時、1番目を取るという方式がいいだろう。
7)クラスをnewしてオブジェクトを生成するより、オブジェクトリテラルを使う。
6)でより安全なクラス宣言の仕方を言っているが、実はこの記事全体は2年前に書いた内容で、今では好ましくないと思われる。
なぜなら、Javascriptではクラス宣言にも関数宣言にもfunctionキーワードを使うため、
クラスとして宣言しているのに間違って関数として実行してしまうおそれがあるからだ。
例えば、以下のように点を意味するPointクラスを定義したとしよう。
function Point(x,y) {
this.getX = function() {
return x;
}
this.getY = function() {
return y;
}
};
これは普通に以下のようにnewキーワードをつけて呼び出すと、問題なくオブジェクトを生成できるが、
var p = new Point(10, 15);
p.getX();
10
以下のようにnewをつけないと、グローバルオブジェクトを汚してしまう。
Point(10,15);
window.getX();
10
簡単な例だが、大規模なプロジェクトでは、探しにくいバグの元になるだろう。
なので、代わりにオブジェクトリテラルを使う方がいい。
例えば、上記のPointは以下のようにオブジェクトリテラルを使える。
function createPoint(x, y) {
return {
getX : function() {
return x;
},
getY : function() {
return y;
}
};
}
var p = createPoint(10, 15);
p.getX();
10
newをつける必要もなく、グローバルオブジェクトを汚すこともない。
Object.createを使うと、継承もできる。
なので、prototype方式を使ったクラス宣言も使う必要がなさそう。
Javascriptでオブジェクト生成はオブジェクトリテラルに限ると思う。
Oracle 12CのPluggable Database
DBにも仮想化が到来した。
Oracle 12CのPluggable Database機能を利用すると、
一つの物理マシンだけで、複数の仮想DBをコンテナDBというものに
まとめて管理できるそうだ。
詳しくは以下の記事を参照
http://www.publickey1.jp/blog/12/oracle_database_12c.html
仮想化は
Oracleのような大型アプリケーションから小さなクラスまで
応用できるものが多く、よくできた概念だと思う。
BeanShell その2
今回はBeanShellの使い方をメモっておく。
1)Beanshellのダウンロード先 :
http://www.beanshell.org/download.html
ここでbsh-X.XXX.jarをダウンロードする。
2)実行
BeanshellにはコンソールモードとGUIモードがある
テキストモードの実行方法:
java -jar bsh-X.XXX.jar
又は
java -cp bsh-X.XXX.jar; bsh.Interpreter
GUIモードは
java -cp bsh-X.XXX.jar; bsh.Console
3)基礎
文法はJavaと同じ。
違うのはInterpreterなので一行ずつ解析するということ。
また、クラス宣言ができないなどの一部制限もあるようだ。
BeanShellを実行すると以下のようなプロンプトが現れる。
bsh %
このプロンプト上でコードを一行ずつ実行させることができる。
以下は試しに打ってみたもの。
bsh % System.out.println("Hello");
Hello
bsh % import java.io.File;
bsh % File file = new File(".");
bsh % System.out.println(file.getCanonicalPath());
D:\down
bsh %
BeanShellでは変数宣言時に型宣言を省略することができる。
bsh % import java.math.BigDecimal;
bsh % money = new BigDecimal("500"); // 型宣言を省略。
bsh % System.out.println(money);
500
もちろん、LinuxのshファイルやWindowsのbat・cmdファイルのように
シェルスクリプトとしても活躍できる。
以下のソースをtest.bshに保存しておく。
import java.io.File;
File current = new File(".");
for (file : current.listFiles()) {
System.out.println(file);
}
それから、コマンドプロンプトから以下を入力して実行できる。
java -cp down\bsh-2.0b4.jar bsh.Interpreter test.bsh
又は、BeanShellのプロンプト上から
bsh % run("test.bsh");
という風にrunコマンドで実行できる。
runコマンドの代わりにsourceコマンドを利用すると、runコマンドと
結果は同じだが、スクリプト内の変数が続けて利用できる。
bsh % source("test.bsh")
...
結果....
...
bsh % System.out.println(current); // test.bshで宣言したcurrent変数が使える。
しかし、runコマンドではスクリプトファイル内で使った変数の利用はできない。
Java標準パッケージ以外に自作のJavaクラスも勿論利用できる。
4)Scripted Methods
BeanShellではメソッドを定義することができる。
int addTwoNumbers( int a, int b ) {
return a + b;
}
sum = addTwoNumbers( 5, 7 ); // 12
マニュアルに出てるサンプルだが、public / staticなどの修飾子を
わざとつけてみてもエラーにはならなかった。
しかし、何の効果もないだろう。クラス内にメソッドがあるわけでないので...
まるでJavascriptでグロバール関数を定義したのと同じ感じ。
BeanShellではメソッド定義時、型宣言を省略することができる。
add( a, b ) {
return a + b;
}
foo = add(1, 2); // 3
System.out.println(foo);
foo = add("Oh", " baby"); // "Oh baby"
System.out.println(foo);
まさにJavascriptのような感じ。
5)Scripted Object
BeanShellではJavaと同じ文法でクラスを定義することはできないが、
Scripted Objectというのを使って複数のメソッドと変数をまとめた
オブジェクトを返すことができる。
例)
person(pName, pAge) {
String name = pName;
int age = pAge;
toString() {
return "name : " + name + ", age : " + age;
}
isOlderThan(other) {
return age > other.age;
}
return this; // 最後にthisを返すのは必須だ。
}
bsh % java = person("java", 16)
bsh % print(duke.toString());
name : duke, age : 31
bsh % print(java.toString());
name : java, age : 16
bsh % print(duke.isOlderThan(java));
true
6)JavaからBeanShellを使う。
以下のサンプルソースで説明を省略する。
package dukelab.beanshell.test;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.StringReader;
import bsh.EvalError;
import bsh.Interpreter;
/**
* BeanShellとJavaとの連携をテストします。
*
* @author dUkE
*/
public class BeanShellTest {
public static void main(String[] args) throws EvalError, FileNotFoundException, IOException {
Interpreter ipr = new Interpreter();
// 単純に1行ずつ実行。
ipr.eval("int i = 2011;");
ipr.eval("print(i);");
// 結果 : 2011
// ReaderオブジェクトもOK.
StringReader sr = new StringReader(
"JOptionPane.showMessageDialog(null, \"こんなのもできるんだ。\");"
);
ipr.eval(sr);
// 結果 : ダイアログが表示される。
// スクリプトファイルの実行も勿論可能。
ipr.source("test.bsh");
// 結果 : ファイルのリストが出力される。
// Java側で変数をBeanShellのコンテキストに予め入れておくとスクリプト内で使用できる。
ipr.set("thisYear", 2011);
ipr.eval("print(\"this year : \" + thisYear);");
// 結果 : this year : 2011
// 実行した結果はJava側で使える。
File f = (File) ipr.eval("f = new File(\".\");");
System.out.println("path : " + f.getCanonicalPath());
// 結果 : path : 現在のディレクトリ。
}
}
7)自動importされるパッケージ
javax.swing.event
javax.swing
java.awt.event
java.awt
java.net
java.util
java.io
java.lang
8)BeahShellの主要コマンド
ディレクトリ関連
dir()
source()
run(),
cat()
load()
save()
mv()
rm()
addClassPath()
javap() - 指定したオブジェクトが持っているメンバーを見ることができる。
APIを調べたい時に便利だろう。
例)結果は皆同じ。
javap( Date.class );
javap( new Date() );
javap( "java.util.Date" );
javap( java.util.Date );
BeanShellの終了はexit()コマンドを使う。
importCommandsコマンドを使うと自分だけのコマンドが作れる。
9)BeanShellの活用
以下は私が個人的に注目する活用面だ。
実際、BeanShellだけでなく、Groovy、JRubyなど他のスクリプト言語でも
可能なことでもある。
だから「BeanShellの活用」ではなく「スクリプト言語の活用」
がより正しいタイトルだと思われるが、BeanShellのメモなので、
そのままおいておく。
1)プログラムの機能の一部をスクリプトで実装する。
=> コンパイルが要らないし、結果をその場で確認できるので、アジャイルな開発に役立ちそうだ。
アジャイルな開発? 簡単に言って"効率よく、早く開発する"を意味する。
コアな部分は、かなり重要なので以前のようにJavaで作るのが正しい。
それ以外の機能的な部分はスクリプトで作成したりすると、開発スピードがあがるような気がする。
機能の一部にスクリプトを使うプログラムは数多くある。
例えば、
AutoCadではLisp言語を
World of WarcraftではLua言語を
そんな理由で私はCompile + Interpreterのハイブリッド形式を最近、注目しつつある。
2)設定ファイルに動的にな設定ロジックを組み込める。
=> 例えば、年齢制限を男性 : 30歳未満、女性 : 25歳未満とする。
これは既存の方式ではどうしてもロジックの方(つまりJavaクラス)に入れなければならない。
性別が男子か女性かを判別するロジックを設定ファイルに書くことができなかったからだ。
しかし、スクリプトを使えば、例えば、
age = (sex == "男性" && age < 30 || (sex == "女性" && age < 25)
のように書くことができる。適当な例だが、私のためのメモなのでこれで十分だと思う(笑)。
10)総評
JavascriptとJavaを連携するのと変わりないような気もしたが、
Javaをそのままスクリプトで活用できるということは非常に魅力的だ。
Java文法だけ知っていれば、Javaの全ての資産をスクリプトで利用できるからだ。
JSR-274 the BeanShell Scripting Languageという標準もあるので
ただのJavaライブラリではない、標準のものなので、習っておくのも
悪くはないと思う。
しかも実行に必要なjarファイルは機能の割りにスーリムそのままだ。
(フール版が276KB、コア版が141KB)
さあ、使ってみようじゃないか。
11)参考URL
http://allabout.co.jp/gm/gc/80686/2/
http://journal.mycom.co.jp/column/jsr/031/index.html