|
任天堂が、大画面の「ニンテンドーDSi LL」を発表。欲しいと思いますか?
|
Java開発者のためのRubyガイドはじめにJava開発者がRubyを学ぶ理由は何でしょうか。Rubyはその多様性と柔軟性でJavaを見事に補完してくれるので、両方の言語を使うことで、より効率的で効果的な開発ができるためです。実際、私は開発にはすべてJava、Ruby、Common Lispを使っていますが、その中でもRubyが開発の中心になりつつあります。特に、Java開発者がRubyを好む理由は次のようなものです。
この記事では、RubyがJava開発者と非常に相性が良い理由を示すために、開発をより効率的にする言語的な特長を紹介し(表1「RubyとJavaの特長の比較」を参照)、両方の言語の短いサンプルプログラムを紹介します。 表1 RubyとJavaの特長の比較
必要なものこの記事を読み進めるには、外部のRubyライブラリをインストールする必要があります。RubyGemsライブラリシステムをインストールすると、この作業を簡単に行うことができます。RubyGemsをRubyForgeからダウンロードして、ご使用のオペレーティングシステムのインストール手順に従ってインストールしてください。既にRubyがセットアップされている場合、RubyGemsが含まれているかを確認してください(多くのRubyインストールパッケージにはRubyGemsが含まれています)。インストールされているかを確認するには、コマンドシェルでgemと入力します。ライブラリを一括管理できる保管場所とRubyGemsなどの標準ツールがあれば、必要なライブラリを探し、それをインストールして複数のプロジェクトで使うといった作業が必要なくなり、多くの時間を節約できます。 以下のコマンドを使って必要なgemsをインストールします。 gem query --remote # if you want to see all available remotely # installable gems sudo gem install activerecord sudo gem install mysql # if you want to use MySQL sudo gem install postgres-pr # optional: install "pure ruby" # PostgreSQL interface sudo gem install postgres # optional: install native PostgreSQL # interface sudo gem install ferret # a search library like Lucene (same API) sudo gem install stemmer # a word stemming library for demonstrating # extending a class gem query # to show gems locally installed gem specification activerecord # info on gem # (ActiveRecord in this example) 編集部注
上記コマンドにはコメントが記述されていますが、これはコマンドの解説をするために記載されているものであり、実際のコマンド入力時には記述不要です。なお、2行にわたっているコメントがいくつかありますが、これはページの都合によるものであり、原文では1行で記述されています。
Mac OS XやLinuxでは、sudoを使ってgem installを実行する必要がありますが、Windowsユーザーの場合は最初に「sudo」と入力する必要はありません。 また、この記事では、次のようにRuby irbシェルを使用します。この記事を読み終えるまでは、Ruby irbシェルを開いたままにしておいてください。 markw$ irb >> s = "a b c" => "a b c" >> サンプルプログラムとコード例は、コピーしてirbインタラクティブセッションに貼り付けることができる程度に短くしています。 Rubyの文字列操作 Rubyの # use the "pretty print" library. Defines the function ’pp’ require ’pp’ # define some strings to use in our examples: s1 = "The dog chased the cat down the street" s2 = "quickly" puts s1 # a substring slice up to and including character at index==6 puts s1[0..6] # a substring slice up to (but not including) the character at index==6 puts s1[0...6] # expressions inside #{} are inserted into a double quote string puts "He is a #{s2} dog #{1 + 6} days a week." # create a copy of the string: the new copy has white space removed puts " test ".strip # string literals can also be formed with single quotes puts s1 + ’ ’ + s2 puts s2 * 4 # find index (zero based) of a substring puts s1.index("chased") # replace a substring (/dog/ -> /giant lizard/) s1[4..6] = ’giant lizard’ puts s1 # the << operator, which also works for arrays and other collections, # copies to then end s2 = s2 << " now" puts s2 puts "All String class methods:" pp s1.methods # the method "methods" returns all methods for any object 出力結果は次のようになります。 The dog chased the cat down the street The dog The do He is a quickly dog 7 days a week. test The dog chased the cat down the street quickly quicklyquicklyquicklyquickly 8 The giant lizard chased the cat down the street quickly now All String class methods: ["send", "%", "index", "collect", "[]=", "inspect", ....] # most methods not shown for brevity--try this in irb 上の例の<<演算子は、実はメソッド呼び出しです。式の評価時、Rubyでは中置演算子はメソッド呼び出しに変換されます。例えば、次のコードの<<演算子は、右側の式の値を左側の式の値に追加します。 >> "123" << "456" => "123456" >> "123".<<("456") => "123456" >> 1 + 2 => 3 >> 1.+(2) => 3 上の例の「.<<」という書き方は、メソッド呼び出しの標準書式です。 多くのクラスでは、<<演算子を使ってオブジェクトをクラス固有のコレクションに追加します。例えば、後述のFerret検索ライブラリ(先ほどインストールしたRuby gem)を見ると、インデックスにドキュメントを追加するために<<演算子がどのように定義されているのかが確認できます。 既存のクラスの編集Rubyの多様性のポイントは、Rubyのすべてのクラスをメソッドとデータを追加して拡張できるという点です。私はよく、コアRubyクラスのオリジナルのソースコードを拡張するのではなく、自分が開発しているアプリケーション内でクラスを拡張するという方法をとります。JavaやC++開発者にとっては奇妙に思えるかもしれませんが、このテクニックを使うと、プロジェクトのリソースを1か所で管理でき、オリジナルのクラスを「膨張」させることなく多くの開発者がアプリケーション固有の機能を追加できるようになります。Javaプログラマの立場で、Javaのルールによる制約を考えてみてください。Javaでは既存のクラスに機能やデータを追加する場合、サブクラスを作成する必要があります。 次のリストは、 begin # stem is undefined at this point puts "The trips will be longer in the future".downcase.stem rescue puts ’Error:’ + $! end require "rubygems" require_gem ’stemmer’ class String # you will extend the String class include Stemmable # add methods and data defined in module Stemmable end puts "The trips will be longer in the future".downcase.stem また、自分のアプリケーション内で、既存のクラスにメソッドや新しいクラスインスタンス変数を追加できると便利なことが分かります。 次の節では、Rubyの強力な柔軟性を証明するもう1つの例である「ダックタイピング」(duck typing)について説明します。 RubyのダックタイピングJavaでは、オブジェクトのクラス階層で(publicやpackageなどの可視性を指定して)定義したオブジェクトのメソッドしか呼び出せません。例えば、あるオブジェクトのコレクションがあり、そのコレクション内の各要素に対して、1つ以上のメソッドを呼び出す処理を繰り返す場合を考えてみましょう。Javaでこのような処理を行う場合、対象となるオブジェクトは同じクラス階層に属しているか、呼び出すメソッドを定義しているインターフェイスを実装している必要があります。 話の流れから予想はついていると思いますが、RubyはJavaよりもはるかに柔軟です。Rubyのランタイムのメソッド呼び出しスキームでは、固有のデータ型やクラスは必要ありません。次のように、 obj.foo.bar obj.foo().bar() もう1つの例として、コレクション内の各オブジェクトから class MyClass2 def name "MyClass2: #{this}" end end Javaのような強力な型チェックを持つ言語に慣れている開発者は、コンパイラやインタプリタがすべての型の使用を静的にチェックしない、このような「安全性に欠ける」柔軟性はプログラムの信頼性を低下させると思うようです。しかし、ランタイムの型チェックで発生するプログラムのバグは、テストですばやく検出できるので、ソフトウェアの信頼性は低下しません。それよりも、言語がより柔軟であるというメリットによって、プログラム自体の長さも開発時間も短くできます。 不明なメソッドの処理 ダックタイピングなんて信頼できないとまだ思っているでしょうか? ではもう1つ、Rubyの裏技を紹介しましょう。Rubyクラスで不明メソッドを処理する方法です。以下は、2つのメソッド(定義済みの markw$ irb >> s = "this is a string" => "this is a string" >> s.length => 16 >> s.foobar NoMethodError: undefined method `foobar’ for "this is a string" 未定義のメソッドの場合、エラーがスローされます。そこで、独自に作成した >> class String >> def method_missing(method_name, *arguments) >> puts "Missing #{method_name} (#{arguments.join(’, ’)})" >> end >> end => nil >> s.foobar Missing foobar () => nil >> s.foobar(1, "cat") Missing foobar (1, cat) => nil >> Rubyのランタイムシステムは、オブジェクトのメソッドを検出できない場合、必ず初期設定で継承されている >> class String >> def method_missing(method_name, *arguments) >> if method_name.to_s==’foobar’ >> arguments.to_s.reverse # return a value >> else ?> raise NoMethodError, "You need to define #{method_name}" >> end >> end >> end => nil >> s.foobar(1, "cat") => "tac1" >> s.foobar_it(1, "cat") NoMethodError: You need to define foobar_it from (irb):38:in `method_missing’ from (irb):43 from :0 >> メソッド名が Rubyのコードブロック Rubyでは、データの繰り返し処理を行う新たな方法としてコードブロックが用意されています。このコードブロックは、Java言語の制限された繰り返し機能よりもはるかに柔軟で強力な機能です。前述の基本的な文字列操作の例では、stemmer gemを使って英単語が含まれる文字列の語幹を検索しました。次の例では、 puts "longs trips study studying banking".split(’ ’) puts "longs trips study studying banking".split(’ ’).each このコード例を実行すると、以下の結果が得られます。 longs trips study studying banking longs : long trips : trip study : studi studying : studi banking : bank さらに、コードブロックの有効な使用例を以下に示します。この例では、 require ’pp’ pp ["the", "cat", "ran", "away"].collect {|x| x.upcase} pp ["the", "cat", "ran", "away"].collect {|x| x.upcase}.join(’ ’) この例では、コードブロックは要素が文字列であると想定して、各要素の ["THE", "CAT", "RAN", "AWAY"] "THE CAT RAN AWAY" コードブロックを使用するメソッドの作成 def cb_test name puts "Code block test: argument: #{name}" s = yield(name) if block_given? puts "After executing an optional code block, =#{s}" end 以下の例では >> puts cb_test("Mark") Code block test: argument: Mark After executing an optional code block, = nil => nil >> puts cb_test("Mark") {|x| x + x} Code block test: argument: Mark After executing an optional code block, =MarkMark nil => nil >> 文字列値「Mark」は引数として Rubyの正規表現 Rubyでは、組み込みの => 4 >> "the dog ran" =~ /[a-e]og/ => 4 >> "the zebra ran" =~ /[a-e]og/ => nil RubyのネットワークプログラミングRubyにはネットワークプログラミング用の標準ライブラリも用意されています。詳しくは、私が執筆した以前の記事を参照してください。私はRubyを使って、インターネットからデータを収集し、そのデータを解析してXMLやデータベースに格納するという作業をよく行います。 RubyのFerretライブラリによるドキュメントのインデックス作成と検索現時点で、Ferretと呼ばれるRuby gemが既にインストールされているはずです。Ferretは、Java Luceneをベースとした高速のインデックス作成および検索ライブラリです(Common LispのバージョンMontezumaよりもはるかに高速です)。Ferretライブラリは、その開発において、作者であるDavid BalmainがそのほとんどをCで開発しRubyでラップしたというおもしろい経緯があります。これは、つまり、Rubyで開発を始めてパフォーマンスに問題が生じた場合は、処理速度が重視される部分を常にCまたはC++で再コーディングできるということです。Ferretでは、Rubyを使用した場合に自分のアプリケーション内で使えるクラスがいくつか定義されています。以下に例を示します。
ローカルファイル、WebのURL、リレーショナルデータベースのテキストデータ(次の節を参照)など、検索対象を表します。
ドキュメントを格納するデータ要素を表します。Fieldにはインデックス付きとインデックスなしがあります。通常、私は単一インデックスを持つ(つまり検索可能な)テキストフィールドを使用し、その後でインデックスのない複数の「メタデータ」フィールドを使用します。オリジナルファイルのパス(WebのURLなど)は、インデックスなしのフィールドに格納できます。
インデックスを格納するディスクファイルを表します。
検索用のAPIを提供します。
Microsoft Word文書のインデックス作成と検索以下は、Microsoft Word文書を読み取ってプレーンテキストを抽出するために私が使用しているRubyクラスです。Rubyで外部プログラムを使用する例として紹介します。 class ReadWordDoc attr_reader :text def initialize file_path # back quotes to run external program @text = `antiword #{file_path}` end end この「裏技」は、私がオープンソースのAntiwordユーティリティを使って実際にWord文書ファイルを処理しているものです。外部コマンドをバッククォートで囲むと、外部プログラムを実行してその結果を文字列に出力することができます。LinuxまたはOS Xの場合は次のコマンドを実行してください(Windowsの場合は`dir`)。 puts `ls -l` この例では、外部コマンド 次のRubyスクリプトを実行すると、Word文書がインデックスに入力されます(プレーンテキストファイルはより簡単です。練習として試してみてください)。 require ’rubygems’ require ’ferret’ include Ferret include Ferret::Document require ’read_word_doc’ # read_word_doc.rb defines class ReadWordDoc # any path to a directory index = Index::Index.new(:path => ’./my_index_dir’) # path to a Microsoft Word doc_path = ’test.doc’ # get the plain text from the Word file doc_text = ReadWord.new(doc_path).text doc = Document.new doc << Field.new("doc_path", doc_path, Field::Store::YES, Field::Index::NO) doc << Field.new("text", doc_text, Field::Store::YES, Field::Index::TOKENIZED) index << doc # a test search index.search_each(’text:"Ruby"’) do |doc, score| # print doc_path meta data puts "result: #{index[doc][’doc_path’]} : #{score}" # print original text puts "Original text: #{index[doc][’text’]}" end index.close # close the index when you are done with it このサンプルコードがいかに短いか分かると思います。Word文書からテキストを抽出するAntiwordを使うためのクラスを含めても、わずか24行で、Wordからテキストを抽出してインデックスを作成し、検索を実行して終了したらインデックスをクローズするというサンプルを作成できるのです。 Rubyを使うと、複雑な作業をほんの数行のコードで処理することができます。Javaでこのようなサンプルコードを作成した場合、(私が作った)非常によくできたLuceneライブラリを使ったとしても、行数ははるかに長くなるでしょう。プログラムは短ければ短いほど、メンテナンスが簡単になり、そのコストも低減されます。 この例では、Word文書を使いましたが、OpenOffice.org文書も読み取り可能です。約30行の純粋なRubyコードで、ドキュメントをunzipし、unzipされたXMLデータストリームのcontent.xml要素からテキストを抽出できます(RubyではXMLも簡単に処理できますが、本稿では触れません)。 RubyによるJavaの補完通常、企業のIT予算においてソフトウェアの開発とメンテナンスにかかるコストは莫大なものです。サーバやインターネット接続などにかかるコストの比ではありません。しかし、Rubyを使うことで、通常はプログラムの長さ自体が短くなるため、システムの構築およびメンテナンスのコストを大いに削減できます(私の場合、コード1行にかかる時間は、どのプログラミング言語でもほとんど差はありません)。 では、Javaはどのような場面で使えばよいのでしょうか? 私の場合は、10年以上コンサルティングしているお客様のシステムを構築するときにJavaプラットフォームを使用したので、これからも確実にJavaを使い続けるでしょう。堅牢なJavaベースのWebアプリケーションは、少なくともサーバがダウンしたりハードウェアメンテナンスで再起動したりする場合以外は、いつまでも稼働するでしょう。実際に数か月間も無人状態で問題がなく稼働するシステムを確認しています。 私の意見としては、大規模なシステムのサーバサイドではJavaを使い続け、小規模なユーティリティプログラムでRubyを使い始めるのが良いと思います。私は、JavaとRubyは互いに競合するのではなく、互いに補完するものと考えています。作業に合わせて最適なツールを使うことをお勧めします。 著者紹介Mark Watson(Mark Watson)
Javaコンサルタント。Java、人工知能、C++、知的エージェントに関する書籍を14冊執筆している。
|