#! /usr/local/bin/ruby -Ku #-----------------------------------------------# # プログラミング入門 - Rubyを使って - # # 著:Chris Pine; 訳:西山 伸 # # (c) 2004, shin@faculty.chiba-u.jp # # # # Original:Learning To Program # # by Chris Pine # # Copyright (c) 2003 # # ruby.tutorial@hellotree.com # #-----------------------------------------------# require 'cgi' require 'stringio' if ENV["HTTP_HOST"] and ENV["SCRIPT_NAME"] LINKADDR = "//" + ENV["HTTP_HOST"] + ENV["SCRIPT_NAME"] else LINKADDR = "//www1.tf.chiba-u.jp/~shin/tutorial/index.rb" end class TutorialWebPage # I couldn't get everything looking the same in Opera and IE # unless I inlined some of the style data. Sorry, folks. @@CODE_BG = 'background-color: #c0c0c0; '+ 'font-weight: 700; ' + 'font-size: 100%; ' @@CODE_STYLE = 'border-right: #d0b0b0 1px solid; '+ 'border-top: #907070 1px solid; '+ 'border-left: #705050 1px solid; '+ 'border-bottom: #c0a0a0 1px solid; '+@@CODE_BG @@OUTPUT_BG = 'background-color: #b0bada; '+ 'font-weight: 700; ' + 'font-size: 100%; ' @@OUTPUT_STYLE = 'border-right: #505a7a 1px solid; '+ 'border-top: #80a0ff 1px solid; '+ 'border-left: #9090ff 1px solid; '+ 'border-bottom: #707a9a 1px solid; '+@@OUTPUT_BG # Opera seems to need the following " ", so everyone gets it. :P @@HLINE = '

 

' # If you decide to change coloring or formatting of this tutorial, # you'll want to change these to something appropriate. @@NUMBER_COLOR = ['green','緑'] @@STRING_COLOR = ['red','赤'] @@KEYWORD_COLOR = ['blue','青'] @@INPUT_DESC = ['white box','白い四角'] @@OUTPUT_DESC = ['blue box','青い四角'] # Delimiter for input to sample code, since it comes out # right alongside the output. Must not be html escapable. @@INPUT = "%%%'f'o'o'" def initialize (cgi) @cgi = cgi @depth = 0 @page = [] # 'format' is a hidden page for testing formatting. @chapters = {'format'=>['Formatting Page', :generateFormattingPage]} @chapters['00' ] = ['はじめに', :generateSetup] @chapters['01' ] = ['数(number)' , :generateNumbers] @chapters['02' ] = ['文字列(string)', :generateLetters] @chapters['03' ] = ['変数と代入', :generateVariables] @chapters['04' ] = ['数と文字列の変換', :generateConversion] @chapters['05' ] = ['メソッド(method)', :generateMethods] @chapters['06' ] = ['制御構造', :generateFlowControl] @chapters['07' ] = ['配列とイテレータ', :generateArrays] @chapters['08' ] = ['メソッドの作り方', :generateDefMethod] @chapters['09' ] = ['クラス', :generateClasses] @chapters['10' ] = ['ブロックと手続きオブジェクト',:generateBlocksProcs] @chapters['11' ] = ['このチュートリアルを超えて', :generateBeyond] @chapters['about' ] = ['このチュートリアルについて...', :generateAbout] end def getChapter (method) @chapters.each do |num, chapter| return num if (method == chapter[1]) end 'Contents' end def selfLink (chap = nil) # REQUEST_URI includes "?Chapter=Foo" LINKADDR+'?Chapter='+(chap ? getChapter(chap) : '') end def makeLink (name, methodName) ''+name+'' end def out @cgi.out { @page.join("\n")+"\n" } end def escapeString (string) string.gsub(/&/n, '&').gsub(/>/n, '>').gsub(/').sub(/#{@@INPUT.reverse}/,'') + escapeOutputNotInput(md.post_match) else escapeString output end end def syntaxColor (str) # str has probably already been html-escaped. lines = str.split /\n/ # L2Pcomment # L2Pstring # L2Pnumber # L2Pkeyword # L2Pdefinition lines.collect! do |line| #line += ' ' # for splitting... Do we need this? md = /'|#/.match line if md # Comment or string. syntaxColor(md.pre_match) + if (md[0] == '#') '' + md[0] + md.post_match + '' else # Big string time... md2 = /(.*?)(^|[^\\])((\\\\)*)'/.match md.post_match if (md2) md[0] + '' + $1 + $2 + $3 + '' + "'" + syntaxColor(md2.post_match) else md[0] end end else # No comment nor string. keywords = %w[__FILE__ and end in or self unless __LINE__ begin ensure redo super until BEGIN break do false next rescue then when END case else for nil retry true while alias elsif if not return undef yield] keywords.each do |keyword| line.gsub!(/(\W|^)(#{keyword})(\W|$)/) do $1+''+$2+''+$3 end end ['def', 'class', 'module'].each do |keyword| line.gsub!(/(\W|^)(#{keyword}) +([\w?]+)/) do $1+'' +$2+''+ ' '+$3+'' end end line.gsub!(/(^|[-{\[( ^+%*\/?;])(\d+(\.\d+)?|\.\d+)/) do $1+''+$2+'' end line end end lines.join "\n" end def input (str) str = escapeString str str.gsub!(/ /, ' ') ''+str+'' end def code (str) str = escapeString str str.gsub!(/ /, ' ') str = syntaxColor str ''+str+'' end def output (str) str = escapeString str str.gsub!(/ /, ' ') ''+str+'' end # This is the cool part... def executeCode (code, input) # Wrap code to catch errors and to stop SystemExit. code = <<-END_CODE begin #{code} rescue SystemExit rescue Exception => error puts error.inspect end END_CODE strIO = StringIO.new if !input.empty? input = input.join("\n")+"\n" input = StringIO.new(input, "r") class << strIO; self; end.module_eval do ['gets', 'getc', 'read'].each do |meth| define_method(meth) do |*params| inStr = input.method(meth).call(*params) puts @@INPUT+inStr.chomp+(@@INPUT.reverse) # Echo input. inStr end end end end # Pass these methods to strIO: kernelMethods = ['puts', 'putc', 'gets'] # Swap out Kernel methods... kernelMethods.each do |meth| Kernel.module_eval "alias __temp__tutorial__#{meth}__ #{meth}" Kernel.module_eval do define_method(meth) do |*params| strIO.method(meth).call(*params) end end end begin strIO.instance_eval code rescue Exception => error # Catch parse errors. return error.inspect end # ...and swap them back in. kernelMethods.each do |meth| Kernel.module_eval "alias #{meth} __temp__tutorial__#{meth}__" end strIO.string end # Tags (or similar) def para (attributes = {}) method_missing(:p, attributes) do s=yield s.gsub!(/^\s*/,'') if s.kind_of?(String) s end end def prog (execute = [], remark = nil, fakeOutput = nil, &block) if !execute return progN(&block) end run = {:input => execute} run[:remark ] = remark if remark run[:fakeOutput] = fakeOutput if fakeOutput progN(run, &block) end def progN (*trialRuns) code = yield # Trim leading whitespace. lines = code.split $/ numSpaces = lines[0].length - lines[0].sub(/ */, '').length lines.each do |line| line.sub!(/ {0,#{numSpaces}}/, '') end code = lines.join($/) prettyCode = $/+syntaxColor(escapeString(code)) # Spit it out. puts '
'+prettyCode+'
' trialRuns.each do |run| if run[:fakeOutput] puts '
'+escapeString(run[:fakeOutput])+'
' end if run[:remark] puts '

'+run[:remark]+'

' end output = escapeOutputNotInput(executeCode(code,run[:input])) puts '
'+$/+output+'
' end nil end # Makes a tag. def method_missing (methodSymbol, attributes = {}) methodName = methodSymbol.to_s attribString = '' attributes.each do |key, val| raise methodName if (key.nil? || val.nil?) attribString += ' '+key.to_s+'="'+val+'"' end if (!block_given?) puts '<'+methodName+attribString+' />' else puts '<'+methodName+attribString+'>' @depth += 1 blockReturn = yield puts blockReturn if (blockReturn.kind_of?(String)) @depth -= 1 puts '' end nil end # # TEST PAGE FOR FORMATTING # def generateFormattingPage h1 { 'Heading 1' } h2 { 'Heading 2' } h3 { 'Heading 3' } h4 { 'Heading 4' } para {'Here\'s some code with fake output:'} prog [], '...just kidding, dude...', 'FUNKADELIC!' do <<-'END_CODE' # Here is some 'Ruby' code. # 5 is better than 6. # def wilma do end if not in, dude. #' '#This shouldn\'t cause any problems.' 'Neither # should this\\' 'do end if elsif else case when then while def class' 'or and not next in' 'to 3 or not to 3, that is 3.7' '' + 'x'+''+''+'..' '' +'o' 8 0.09 9.9 5.times {} puts 'I love cheese.' # yo 'g' puts 5.02 + 8 + 0.002 # s'up, muva jimmy = ['yoyoyo', 66] jimmy.each do |item| puts item.inspect end puts case 'pumpkin' when String then 'yep' when Fixnum then 'nope' else 'maybe' end def yummy if (4 <= 5) 'Yummm!' elsif (4 == 5) 'Huh?' else while (1 == 2) puts 'What?' end end end class JustSomeClass def initialize @var = 5 end end puts Math::PI # Should work. puts PI # Shouldn't work. END_CODE end para {'Here\'s some code with input and output:'} prog ['"Chris"', '&26', ''] do <<-END_CODE puts 'yoyo...wuddup?' puts 'NAME:' name = gets.chomp puts 'AGE:' age = gets.chomp puts 'FAVORITE COLOR' color = gets.chomp puts 'Hello, '+name+', the '+age+' year-old '+color+' lover.' END_CODE end para do <<-END_PARAGRAPH Hello there. I love #{input 'inputting VARIOUS things'}. I also get a kick out of #{code 'coding various things'}. There's such a thrill in seeing all of the exciting #{output 'output you can get'} from a well-written program. END_PARAGRAPH end end # # SETUP # def generateSetup para do <<-END_PARAGRAPH プログラミングをするためには、コンピュータが理解できる言語、 「プログラミング言語」を話すことが必要です。 世の中には数多くのプログラミング言語があり、それぞれ異なっていて、 そのうちの多くはとても優れたものです。 このチュートリアルでは、その中で、私の好きな言語である Ruby を選びました。 END_PARAGRAPH end para do <<-END_PARAGRAPH Rubyは私が好きな言語というだけではなくて 今まで数多くの言語を見て来た中で最も容易な言語といえます。 実際、このチュートリアルを書いている本当の理由はこれなのです。 チュートリアルを書こうと決めてから私の好きな言語のRubyを選んだ、 のではなくて、Rubyが簡単であるのを知り、これを使ったプログラミング初心者用の チュートリアルがあるべきだと思って書き始めました。 つまり、このチュートリアルを書こうと思ったのはRubyの単純明快さゆえであって、 私がこの言語を好きだからというわけではないのです。 (同じようなチュートリアルを他の言語、たとえばC++やJavaなどで 書こうと思ったら、何百ものページ数が必要になったかもしれません。) でも、Rubyが簡単だからといって、初心者専用の言語とは思わないで ください。Rubyは、これだけでプロのプログラマが仕事を行う ことができるくらい十分に強力なプログラミング言語でもあります。 END_PARAGRAPH end para do <<-END_PARAGRAPH 人間の言葉で何かを書くと、その書かれた物はテキストと呼ばれます。 コンピュータ言語で何かを書いた場合、その書かれた物は、コード(code) と呼ばれます。私は多くのRubyのコード例をこのチュートリアルに 含めました。そのほとんどは、あなたの持っているコンピュータで実行 できるはずです。また、コードを読みやすくするため、一部色づけして います。(たとえば、数字はいつも #{@@NUMBER_COLOR[1]} になっています。) キーボードからの入力が促される場所では#{input @@INPUT_DESC[1]}で、 プログラムにより表示されたものは、#{output @@OUTPUT_DESC[1]}で、 表されています。 END_PARAGRAPH end para do <<-END_PARAGRAPH もし、わからないことが出てきたり疑問が出てきたりしても その場ではそれを書き留めて、読み続けて行ってください。 おそらくその後の章で答えが得られると思います。 最後の章までいってもその疑問が解決しない時は、 どこに聞きにいけば良いのかを教えましょう。 そこには、あなたを助けようと待ち構えている多くのすばらしい人たちがいます。 あなたは彼らにどうやって質問すればよいかを知れば良いのです。 END_PARAGRAPH end para do <<-END_PARAGRAPH でも、まずやらなくちゃいけないことがあります。それは、Rubyをダウンロードして あなたのコンピュータにインストールすることです。 END_PARAGRAPH end h2 {'Windows へのインストール'} para do <<-END_PARAGRAPH ウインドウズマシンにRubyをインストールすることはほんとに簡単です。 最初に Ruby installer をダウンロードします。(訳註: Arton氏の作成した ActiveScriptRuby を用いても良いでしょう。) いくつかのバージョンが選択できます。このチュートリアルでは、 バージョン#{RUBY_VERSION}のRubyが用いられています。 そこで、あなたのダウンロードしたRubyがこれと同じか、 それより新しいものであることを確認しましょう。 その後は、インストール用のプログラムを実行するだけです。 プログラムを実行するとどこにインストールするかを聞いてきますが、 特別な理由がなければデフォルトの場所にインストールすれば良いでしょう。 END_PARAGRAPH end para do <<-END_PARAGRAPH プログラミングを行うには、プログラムを書くことと実行することのできる環境が必要です。 つまり、テキストエディタとコマンドラインが必要になります。 END_PARAGRAPH end para do <<-END_PARAGRAPH Ruby installer(訳註:英語版)には、素敵なテキストエディタ SciTE(the Scintilla Text Editor)がついてきます。 スタートメニューから選ぶことでこのSciTEを実行することができます。 もし、このチュートリアルにあるようにコードに色づけをしたかったら、 次のファイルをダウンロードして、SciTEのフォルダ(デフォルトであれば c:/ruby/scite)に入れてください。 END_PARAGRAPH end ul do li {'Global Properties'} li {'Ruby Properties'} end para do <<-END_PARAGRAPH あなたの作ったプログラムすべてをしまうための専用フォルダを作って おくというのも良い考えです。プログラムを保存するときは、 このフォルダにしまうように気をつけてください。 END_PARAGRAPH end para do <<-END_PARAGRAPH コマンドラインを得るには、スタートメニューの「すべてのプログラム」 から、「アクセサリ」を見つけ、その中の「コマンドプロンプト」を 選択してください。実行すると、プロンプトに自分のいるフォルダが表示 されます。ここから、プログラムを保存してあるフォルダの位置に 移動したくなると思います。#{input 'cd ..'}とタイプすればフォルダの上の ほうに移動しますし、#{input 'cd foldername'}とすれば foldernameという名前のフォルダの中に入ることが出来ます。 いまいるフォルダの中にあるすべてのフォルダを見るには、 #{input 'dir /ad'}とタイプしてください。 END_PARAGRAPH end para do <<-END_PARAGRAPH さて、準備は出来ましたね。では #{makeLink 'プログラミング入門', :generateNumbers} に行きましょう。 END_PARAGRAPH end h2 {'Macintosh へのインストール'} para do <<-END_PARAGRAPH もし、Mac OS X 10.2 (Jaguar)を持っているなら、すでにRubyは システムに入っているはずです。簡単。もし、10.1なら、 Rubyをダウンロード しましょう。 残念ながら Mac OS 9以前ではRubyは使えないようです。 END_PARAGRAPH end para do <<-END_PARAGRAPH プログラミングを行うには、プログラムを書くことと実行することのできる環境が必要です。 つまり、テキストエディタとコマンドラインが必要になります。 END_PARAGRAPH end para do <<-END_PARAGRAPH コマンドラインは「ターミナル」(アプリケーション/ユーティリティにあります。) で代用できます。 END_PARAGRAPH end para do <<-END_PARAGRAPH テキストエディタに関しては使い慣れているものを使うと良いでしょう。 もし、「テキストエディット」を使うなら、プログラムをtext-onlyで、保存 するのを忘れないでください。そうでないとプログラムは動作しません。 そのほかの選択肢としては、emacs, vi, そしてpicoがあります。これらすべては コマンドラインからアクセスすることが出来ます。 (訳註:この他にも、フリーウエアの 「mi」(旧称:ミミカキエディット) や、オープンソースソフトウエアの 「Smultron」 などの選択肢があるそうです。) END_PARAGRAPH end para do <<-END_PARAGRAPH さて、準備は出来ましたね。では #{makeLink 'プログラミング入門', :generateNumbers} に行きましょう。 END_PARAGRAPH end h2 {'Linux へのインストール'} para do <<-END_PARAGRAPH 最初にRubyがすでにインストールされているか見てみましょう。 ターミナルにおいて#{input 'which ruby'}とタイプしてください。もし、 #{output '/usr/bin/which: no ruby in (...)'}のように表示されるなら、 Rubyをダウンロード する必要があります。#{output '/usr/bin/ruby'}や\ #{output '/usr/local/bin/ruby'}のように表示されるならRubyはすでに インストールされています。どのバージョンのRubyを実行できるのか #{input 'ruby -v'}と入力してチェックしてみましょう。 このバージョンが上記のダウンロードページにある最新の安定バージョン より古かったら最新版にアップグレードしておいたほうが良いかもしれません。 END_PARAGRAPH end para do <<-END_PARAGRAPH もし、あなたがrootユーザだったら、おそらくRubyのインストールについての 解説は必要ないと思います。rootユーザでない場合には、システム管理者に Rubyをインストールしてもらえるよう頼みましょう。(その場合、システムを 使っているすべての人がRubyを使うことができるようになります。) END_PARAGRAPH end para do <<-END_PARAGRAPH さもなくば、あなたが自分で使うだけのためにインストールすることもできます。 ダウンロードしたファイルを、たとえば$HOME/tmp のようなテンポラリーのディレクトリに移動してください。 もし、そのファイル名が、ruby-1.6.7.tar.gzだったら、 #{input 'tar zxvf ruby-1.6.7.tar.gz'}でファイルを展開し、 今作ったディレクトリ(この場合だと#{input 'cd ruby-1.6.7'})に移動します。 END_PARAGRAPH end para do <<-END_PARAGRAPH そこで、#{input './configure --prefix=$HOME'}とタイプして 設定(configuration)を行い、次に、 #{input 'make'}によって、Rubyインタープリタを構築(build)します。 これには数分かかるかも知れません。それが終わったら、 #{input 'make install'}とタイプして、インストール(install)します。 END_PARAGRAPH end para do <<-END_PARAGRAPH つぎに、$HOME/.bashrc のファイルを修正して コマンドサーチパスに$HOME/binを追加します。 (この変更を反映するためには、いったんログアウトして入りなおす必要が あります。)これを行った後、インストールした結果をテストします: #{input 'ruby -v'}。もし、これで、どのバージョンのRubyを手にしたか 確認できたら、$HOME/tmp(か、あるいはダウンロード ファイルを展開した先のディレクトリ)にあるファイルを消去することができます。 END_PARAGRAPH end para do <<-END_PARAGRAPH さて、準備は出来ましたね。では #{makeLink 'プログラミング入門', :generateNumbers} に行きましょう。 END_PARAGRAPH end end # # NUMBERS # def generateNumbers para do <<-END_PARAGRAPH さて、#{makeLink('準備', :generateSetup)}はすべて整いました。 プログラムを書きましょう。まずは、好みのテキストエディタを開いて以下を タイプしてください。 END_PARAGRAPH end prog false do <<-END_CODE puts 1+2 END_CODE end para do <<-END_PARAGRAPH このプログラム(はい、これもれっきとしたプログラムです。)を#{input 'calc.rb'}という 名前で保存します。(.rbというのは、Rubyで書かれたプログラムのファイル によく使われる拡張子です。) さて、コマンドラインで#{input 'ruby calc.rb'}と 入力して実行させてみましょう。画面に#{output '3'}が表示されるはずです。 どうです?プログラムはそんなに難しくないですよね。 END_PARAGRAPH end h2 {(code ('puts'))+' 入門'} para do <<-END_PARAGRAPH さて、このプログラムでは何が行われているんでしょう? #{code '1+2'} が何をやっているのかは推測できますね。このプログラムは基本的に 次のものと同じです。 END_PARAGRAPH end prog false do <<-END_CODE puts 3 END_CODE end para do <<-END_PARAGRAPH #{code 'puts'}は、単にその後に来るものを画面に出力します。 END_PARAGRAPH end h2 {'整数(Integer)と浮動小数点数(Float)'} para do <<-END_PARAGRAPH ほとんどのプログラム言語では(Rubyも例外ではないのですが) 小数点なしの数字は整数(integer) と呼ばれます。 そして、小数点つきの数字は浮動小数点数 (floating-point numbers; float) と呼ばれます。 END_PARAGRAPH end para do <<-END_PARAGRAPH ここにいくつかの整数の例を示します。 END_PARAGRAPH end prog false do <<-END_CODE 5 -205 9999999999999999999999999 0 END_CODE end para do <<-END_PARAGRAPH 次に、浮動小数点数の例を示します。 END_PARAGRAPH end prog false do <<-END_CODE 3.14 0.001 -205.3884 0.0 END_CODE end para do <<-END_PARAGRAPH 実際、プログラムで浮動小数点数が使われることはあまり多くありません。 たいていは整数だけでこと足ります。 (誰も7.4通のe-mailを受け取ったり、1.8ページのウェブページを見たり、 はたまた、5.24曲の歌を聴いたりなんてしたいとは思いませんから。) 浮動小数点数は学術的な目的(物理実験など)や3Dのグラフィックスなどでは より多く用いられます。また、ほとんどの会計プログラムも整数しか使いません。 それらは1円単位(アメリカではドルではなくセント単位)の整数で金額を保持しています。 END_PARAGRAPH end h2 {'単純な計算'} para do <<-END_PARAGRAPH ここまでの説明だけで、もう簡単な計算機は作れてしまいます。 (電卓では小数を使っています。そこで、もしコンピュータに電卓として 振舞ってもらうのであれば、浮動小数点数を用いるべきです。) 足し算と引き算には、それぞれ+-を用います。 掛け算には*を、割り算には/を用います。 ほとんどのキーボードは右のほうにテンキーがあってそこにこのような算術演算の 記号のキーがあります。ノートパソコンなどの場合はShift + :/(?と同じキー)を使います。 それでは、私たちのcalc.rbを少し拡張していきましょう。 次のコードをタイプして実行してみてください。 END_PARAGRAPH end prog [], 'プログラムの出力は次のとおり' do <<-END_CODE puts 1.0 + 2.0 puts 2.0 * 3.0 puts 5.0 - 8.0 puts 9.0 / 2.0 END_CODE end para do <<-END_PARAGRAPH (プログラム中のスペースはコードを読みやすくします。) ここには驚くようなことはありませんね。さて、では整数ではどうでしょう。 END_PARAGRAPH end prog [], 'ほとんどは、浮動小数点のときと同じですけど・・' do <<-END_CODE puts 1+2 puts 2*3 puts 5-8 puts 9/2 END_CODE end para do <<-END_PARAGRAPH 最後のだけ違いますね。整数どうしでの計算をするときには、答えは整数になります。 ぴったりした答えにならないときはコンピュータは切捨てを行ってしまいます。 (もちろん、#{output '4'}が#{code '9/2'}という整数演算の正しい答えです。 あなたの期待通りではないとしてもです。) END_PARAGRAPH end para do <<-END_PARAGRAPH おそらく、整数の割り算に何の意味があるのか不思議に思うことでしょう。 では、たとえば、あなたは映画を見に行こうと思ったとしましょう。ただし、 今9ドルしかもっていません。ここポートランドでは2ドルで映画を見ることができます。 では、いくつの映画を見ることができるでしょう? #{code '9/2'}... #{output '4'}本 です。この場合は、4.5本というのは、正しい答えではありません。 映画の半分だけ見るとか、あなたの半分だけ全部の映画を見るとかは出来ない相談 です。要するに分割できないものもあるのだということです。 END_PARAGRAPH end para do <<-END_PARAGRAPH さて、自分で好きなプログラムを書いて実験してみましょう。 より複雑な数式が書きたくなったら、括弧()を使うことが出来ます。 たとえば: END_PARAGRAPH end prog do <<-END_CODE puts 5 * (12-8) + -15 puts 98 + (59872 / (13*8)) * -52 END_CODE end h2 {'練習問題'} para do puts '次の結果を表示するプログラムを書いてみましょう。' end ul do li {'1年は何時間でしょうか?'} li {'10年間は何分でしょうか?'} li {'あなたが生まれてから今日まで何秒たっているでしょうか?'} li {'あなたは一生のうちいくつのチョコレートを食べたいですか?
' + '注:この計算には多少時間がかかるかもしれません。'} end para do puts "次は、もう少し難しい質問です。" end ul do li {"私が生まれてから#{t = (Time.now - Time.mktime(1976,8,3)).to_i s = t / 100000000 u = (t % 100000000)/1000000 ans=sprintf("%d億", s) ans+=sprintf("%d00万", u) if u != 0 ans+="秒"} たっているとしたら、私は今何歳でしょう?"} end para do <<-END_PARAGRAPH 数に関して十分に練習したら、次は#{makeLink('文字', :generateLetters)}へと進みましょう。 END_PARAGRAPH end end # # LETTERS # def generateLetters para do <<-END_PARAGRAPH さて、ここまでで#{makeLink('数', :generateNumbers)}と簡単な計算に関して学んで きました。それでは、文字や単語、文章などはどうでしょう? END_PARAGRAPH end para do <<-END_PARAGRAPH プログラムの中の文字の並びを文字列(string) と呼びます。 (横断幕などに印刷された文字が広げられる(strung)のを思い浮かべると 良いでしょう。)コードの中のどの部分が文字列なのかをわかりやすくするため、 ここでは、文字列の色を #{@@STRING_COLOR[1]} にしています。
いくつかの文字列の例です。 END_PARAGRAPH end prog false do <<-END_CODE 'Hello.' 'Ruby rocks.' '5 は私の好きな数です... あなたの好きな数は何ですか?' 'スヌーピーはつま先をぶつけて #%^?&*@! としゃべった。' ' ' '' END_CODE end para do <<-END_PARAGRAPH このように文字列には、通常のアルファベット文字以外に句読点、数字、 記号、そしてスペースが含まれます。(訳註:日本語の文字も含まれます。) 最後の文字列は中に何も含まれていません。 このような文字列は空文字列 と呼ばれます。 END_PARAGRAPH end para do <<-END_PARAGRAPH 前の章で数字を表示するために、#{code 'puts'}を使ってきましたが、 これを文字列に対しても試してみましょう。 END_PARAGRAPH end prog do <<-END_CODE puts 'Hello, world!' puts 'こんにちは' puts '' puts 'Good-bye.' END_CODE end para do <<-END_PARAGRAPH うまくいきました。では、いろいろな文字列で試してみてください。 END_PARAGRAPH end h2 {'文字列の計算'} para do <<-END_PARAGRAPH 数に計算を行ったように、文字列に対しても計算が出来ます! まぁ、計算のようなものという感じですが。とにかく文字列を加えることは出来ます。 2つの文字列を加えて#{code 'puts'}で、どう表示されるか見てみましょう。 END_PARAGRAPH end prog do <<-END_CODE puts 'I like' + 'apple pie.' END_CODE end para do <<-END_PARAGRAPH おっと。#{code "'I like'"}と#{code "'apple pie.'"}の間にスペースを入れ忘れてしまいました。 スペースはプログラムの中ではあまり意味を持ちませんが、文字列の中では重要です。 (よく、「コンピュータはあなたのやって欲しい ことを行うのではなくて、やるように 指示された ことだけを行う」といわれますが、これは本当ですね。) それじゃ、再度挑戦です。 END_PARAGRAPH end prog do <<-END_CODE puts 'I like ' + 'apple pie.' puts 'I like' + ' apple pie.' END_CODE end para do <<-END_PARAGRAPH 今度はうまくいきました。 (このように、どちらの側の文字列にスペースを追加しても結果は同じです。) END_PARAGRAPH end para do <<-END_PARAGRAPH さて、文字列を加えることはできるようになりましたが、掛け算はどうでしょう。 (ただし、掛けるのは数ですが。) これを見てください。 END_PARAGRAPH end prog [], '彼女と目があった? 冗談、冗談...。本当はこうです :)', 'batting her eyes' do <<-END_CODE puts 'blink ' * 4 END_CODE end para do <<-END_PARAGRAPH これは、次のように考えるとわかると思います。#{code '7*3'}というのは、結局 #{code '7+7+7'}と同じ意味ですよね。これから類推して、#{code "'moo'*3"} は #{code "'moo'+'moo'+'moo'"}のことを意味しているのです。 END_PARAGRAPH end h2 {"#{code '12'} vs #{code "'12'"}"} para do <<-END_PARAGRAPH 先に進む前に、数(numbers)数字(digits)の違いについて 理解しておきましょう。 #{code '12'}は数そのものです。しかし#{code "'12'"}は2つの数字からなる文字列です。 END_PARAGRAPH end para do <<-END_PARAGRAPH いろいろ試してみましょう。 END_PARAGRAPH end prog do <<-END_CODE puts 12 + 12 puts '12' + '12' puts '12 + 12' END_CODE end para do <<-END_PARAGRAPH これはどうでしょう。 END_PARAGRAPH end prog do <<-END_CODE puts 2 * 5 puts '2' * 5 puts '2 * 5' END_CODE end para do <<-END_PARAGRAPH ここで挙げた例はどれも素直なものです。しかし、文字列と数字を注意深く混ぜないと.... END_PARAGRAPH end h2 {'問題発生'} para do <<-END_PARAGRAPH いろいろやってみると、うまくいかない 場合があったのではないでしょうか? ここで、そういう例を挙げてみましょう。 END_PARAGRAPH end prog do <<-END_CODE puts '12' + 12 puts '2' * '5' END_CODE end para do <<-END_PARAGRAPH んー、エラーメッセージが出ているようです。 問題は、文字列に数を加えることや、文字列に別の文字列を 掛け合わせたりはできないというところにありそうです。 次のように書けば、これが意味のないことなんだということが 理解できるのではないでしょうか? END_PARAGRAPH end prog false do <<-END_CODE puts 'Betty' + 12 puts 'Fred' * 'John' END_CODE end para do <<-END_PARAGRAPH ほかにも次のことを覚えておくべきでしょう。#{code "'pig'*5"}というコードは 文字列#{code "'pig'"}を#{code '5'}回足し合わせるということを意味していて、 プログラム中に書くことができます。しかし、#{code "5*'pig'"}というコードを書く ことはできません 。なぜなら、このコードは#{code '5'}を#{code "'pig'"}回(?) 足し合わせるという意味になり、ナンセンスですから。 END_PARAGRAPH end para do <<-END_PARAGRAPH 最後に、#{output 'You\'re good!'}という文字列を出力するプログラムを 書きたいと思ったとします。 普通にやってみましょう。 END_PARAGRAPH end prog false do <<-END_CODE puts 'You're good!' END_CODE end para do <<-END_PARAGRAPH さて、このままでは うまく動きませんね。文法エラーが起きてしまいます。 この理由は、コンピュータが#{code "'"}のところで文字列が終わりと思ってしまうからです。 (テキストエディタが、文法による色分け をしてくれるのが便利なわけがこれです。) それでは、どうやってコンピュータにここで文字列が終わりではないことを知らせるのでしょうか。 そのためには、アポストロフィ記号をエスケープ しなければなりません。このように。 END_PARAGRAPH end prog do <<-END_CODE puts 'You\\'re good!' END_CODE end para do <<-END_PARAGRAPH バックスラッシュ記号(訳註:日本語の端末では円記号に見えます。 以降、読み替えてください。)は、エスケープキャラクタです。 このことを言い換えると、バックスラッシュ記号と他の文字が 並んでいたら、それは新しい文字に翻訳されるということです。 バックスラッシュがエスケープするのはアポストロフィ記号と バックスラッシュ記号それ自身です。(よく考えると、 エスケープ記号はいつもそれ自身でエスケープされなければならない ことがわかると思います。) いくつか例を挙げておきましょう。 END_PARAGRAPH end prog do <<-END_CODE puts 'You\\'re swell!' puts '文字列の最後のバックスラッシュ: \\\\' puts 'up\\\\down' puts 'up\\down' END_CODE end para do <<-END_PARAGRAPH バックスラッシュ記号は#{code "'d'"}をエスケープせず 、 バックスラッシュ記号自身をエスケープする ので、最後の 2つの文字列は同じものになります。コード上では同じもののようには 見えませんが、コンピュータの中では同じものです。 END_PARAGRAPH end para do <<-END_PARAGRAPH さて、他に多少疑問があったとしても #{makeLink('読み進めて', :generateVariables)}ください。 結局、この時点でこの ページに関するすべての質問に答えることは できませんから。 END_PARAGRAPH end end # # VARIABLES # def generateVariables para do <<-END_PARAGRAPH ここまで、文字列や数を#{code 'puts'}したら、#{code 'puts'}したものはどこかへ 行ってしまっていました。つまり、言いたいことは、もし何かを2回表示したいときには、 それを2回プログラムに書かなければならなかったということです。 END_PARAGRAPH end prog do <<-END_CODE puts '...もう一回言います...' puts '...もう一回言います...' END_CODE end para do <<-END_PARAGRAPH もし、タイプするのを1回だけにして、それをどこかにしまっておくことができたら 良いと思いませんか? もちろんできます、—そうではなかったら、言い出したりはしませんよ。 END_PARAGRAPH end para do <<-END_PARAGRAPH 文字列をコンピュータのメモリに保存するには、その文字列に名前をつける必要があります。 プログラマはしばしばこの操作のことを代入(assignment) と呼び、その名前を 変数(variable) と呼びます。 (訳註:日本語では変「数」と呼びますが、これは数学におけるvariableを訳した言葉だからです。 variableは「変われるもの」という意味なので、コンピュータでは指し示しているものが、 必ずしも数であるとは限りません。) 変数名は文字と数字の羅列です。 ただし、最初の文字はアルファベットの小文字でなければなりません。 では、先ほどのプログラムを改良してみましょう。文字列に #{code 'myString'}という名前をつけてみます。(ここで、変数の名前は好きなようにつけ ることができるので、#{code 'str'}や#{code 'myOwnLittleString'}でもいいし、あるいは #{code 'henryTheEighth'}としても大丈夫です。(訳註:でもあとで自分が見たり、他人が 見たりすることを考えると、わかりやすい名前が良いと思いますが)。) END_PARAGRAPH end prog do <<-END_CODE myString = '...もう一回言います...' puts myString puts myString END_CODE end para do <<-END_PARAGRAPH #{code 'myString'}に何かを行おうとするたび、プログラムは #{code "'...もう一回言います...'"}に対してその操作を行います。 これは、変数の#{code 'myString'}は実体である #{code "'...もう一回言います...'"}を「指し示している」のだと考えると わかりやすいと思います。では、もう少し面白い例を。 END_PARAGRAPH end prog do <<-END_CODE name = 'Patricia Rosanna Jessica Mildred Oppenheimer' puts '私の名前は ' + name + ' よ。' puts 'うわぁ! ' + name + ' ってほんとに長い名前ですね。' END_CODE end para do <<-END_PARAGRAPH 変数に文字列などの物(オブジェクト)を代入する ことができるのと同じように、 別の物を再代入 することもできます。 (これが変数と呼ばれる理由です。指し示している物を変えることが できるからです。) END_PARAGRAPH end prog do <<-END_CODE composer = 'モーツァルト' puts composer + 'は、彼の時代、"衝撃的" だったらしい。' composer = 'ベートーベン' puts 'でも僕は個人的には' + composer + 'が好きだ。' END_CODE end para do <<-END_PARAGRAPH もちろん、変数は文字列以外にもどんな種類の物(オブジェクト)でも指し示すことができます。 END_PARAGRAPH end prog do <<-END_CODE var = 'もひとつ別の' + '文字列' puts var var = 5 * (1+2) puts var END_CODE end para do <<-END_PARAGRAPH 実際、変数は他の変数以外なら何でも指し示すことができます。 変数を代入するとどうなるか試してみましょう。 END_PARAGRAPH end prog do <<-END_CODE var1 = 8 var2 = var1 puts var1 puts var2 puts '' var1 = 'eight' puts var1 puts var2 END_CODE end para do <<-END_PARAGRAPH 最初、#{code 'var2'}に#{code 'var1'}を代入して指し示す先を#{code 'var1'}に しようとすると、実際には#{code '8'}を指し示すようになります。 (これはちょうど、#{code 'var1'}が指していたのと同じです。) そして、#{code 'var1'}に#{code "'eight'"}を代入して#{code 'var1'}の 指し示す先を変えたとしても、#{code 'var2'}は#{code 'var1'}を 指しているわけではないので、#{code '8'}を指し続けます。 END_PARAGRAPH end para do <<-END_PARAGRAPH さて、ここまでで、変数、数、そして文字列が使えるようになりました。 次は、数と文字列を #{makeLink '相互に変換する', :generateConversion}方法を学びましょう。 END_PARAGRAPH end end # # CONVERSION # def generateConversion para do <<-END_PARAGRAPH いままで、2種類のオブジェクトについて見て来ました。 (#{makeLink '数', :generateNumbers}と#{makeLink '文字列', :generateLetters}です。) そして、それらを指し示す#{makeLink '変数', :generateVariables}を作りました。 次にしたいことは、これらを一緒にしてうまいこと出来ないかと言うことです。 END_PARAGRAPH end para do <<-END_PARAGRAPH プログラムを使って画面に#{output '25'}と表示させるつもりで、次のようにしても うまくは行かない というのは分かりますね。 つまり、数と文字列とを加え合わせることはできないということです。 END_PARAGRAPH end prog false do <<-END_CODE var1 = 2 var2 = '5' puts var1 + var2 END_CODE end para do <<-END_PARAGRAPH ここで問題なのは、このプログラムが#{output '7'} (#{code '2 + 5'})を表示させようと しているのか、#{output '25'} (#{code "'2' + '5'"})を表示させようとしているのかを コンピュータが知るすべがないことです。 END_PARAGRAPH end para do <<-END_PARAGRAPH この2つのものを加え合わせる前に、何らかの方法で#{code 'var1'}を文字列にしたものか、 あるいは、#{code 'var2'}を整数にしたものかを得る必要がありそうです。 END_PARAGRAPH end h2 {'変換'} para do <<-END_PARAGRAPH あるオブジェクトを文字列にしたものを得るには、単にその終わりに #{code '.to_s'} を書き足します。 END_PARAGRAPH end prog do <<-END_CODE var1 = 2 var2 = '5' puts var1.to_s + var2 END_CODE end para do <<-END_PARAGRAPH また、同様にオブジェクトを整数にしたものを得るには#{code 'to_i'} を、 浮動小数点数にしたものを得るには#{code 'to_f'} を使います。 では、これら3つのメソッドが何を行うのか、 (あるいは何を行わない のか)をもう少し詳しく見てみましょう。 END_PARAGRAPH end prog do <<-END_CODE var1 = 2 var2 = '5' puts var1.to_s + var2 puts var1 + var2.to_i END_CODE end para do <<-END_PARAGRAPH #{code 'to_s'} を呼んで #{code 'var1'} を文字列にした後でも、 #{code 'var1'} は常に #{code '2'} を指し続けており、決して #{code "'2'"} にはなって いないことに注意してください。#{code 'var1'} は明示的に再代入をしない限り、 (そのためには必ず#{code '='}記号が必要です。)また、プログラムが終了しない限り #{code '2'} を指し続けます。 END_PARAGRAPH end para do <<-END_PARAGRAPH では、いくつかの面白い(そして少し込み入った)変換の例を見てみましょう。 END_PARAGRAPH end prog do <<-END_CODE puts '15'.to_f puts '99.999'.to_f puts '99.999'.to_i puts '' puts '5 は僕の好きな数!'.to_i puts '誰から 5が好きだってきいたの?'.to_i puts '君のママからだよ'.to_f puts '' puts '文字列っぽい'.to_s puts 3.to_i END_CODE end para do <<-END_PARAGRAPH 結果を見て少しびっくりしましたか? 最初の例はまったく問題ないですね。 #{output '15.0'}を返しています。 その次は文字列#{code "'99.999'"}を浮動小数点数と整数に変換しています。 小数に関しては期待通りだと思いますが、整数では例のごとく丸めて切り捨てられ ています。 END_PARAGRAPH end para do <<-END_PARAGRAPH 次は、数に変換しようとする幾分...普通ではない 文字列の例をあげました。 #{code 'to_i'} や #{code 'to_f'} では理解し得ない最初の文字が出た時点で それ以降の文字列を無視します。 ですので、最初の例では#{code '5'}に変換しますが、そのあとの2つの例では、 数字以外の文字から始まっているためすべて無視されて...、コンピュータは ゼロを返しています。 END_PARAGRAPH end para do <<-END_PARAGRAPH 最後の2つの例では、期待通り、変換のメソッドはまったく何も行いません。 END_PARAGRAPH end h2 {(code 'puts') + 'に対するもうひとつの見方'} para do <<-END_PARAGRAPH さて、私たちのお気に入りのメソッドについては何か変なところが あるようです。見てみましょう。 END_PARAGRAPH end prog do <<-END_CODE puts 20 puts 20.to_s puts '20' END_CODE end para do <<-END_PARAGRAPH なぜこの3つは同じ出力なのでしょうか。 後の方の2つに関しては、#{code '20.to_s'} #{code "'20'"}のことなので、 このように表示されるのは当然です。 では、最初の#{code '20'} という整数についてはどうでしょう。それを言うなら、 整数の 20 を表示させるとは何を意味しているのでしょう。 たとえば、あなたが紙片に 2 と書いて、その後に続けて 0と 書いた場合それは文字列を書いたのでしょうか。それとも整数を書いたのでしょうか。 整数の 20というのは、私の手と足にある指の合計の数であって、 20 でできた文字列のことではありません。 END_PARAGRAPH end para do <<-END_PARAGRAPH さてここで、いままで慣れ親しんできた #{code 'puts'} に潜む重大な秘密を披露しましょう。 #{code 'puts'} がオブジェクトを表示する時には、その前に密かに #{code 'to_s'} を使ってそのオブジェクトを文字列に変換していたのでした。 実際、#{code 'puts'} の最後についている s は、string (文字列) の sです。実は、#{code 'puts'}はput string という意味だったのです。 END_PARAGRAPH end para do <<-END_PARAGRAPH 今のところ、このことはそれほどすごいと思うようなことではないかもしれませんが、 Rubyには非常に多種多様な オブジェクトがあって、 (自分でオブジェクトを作る方法もこれから習っていきますよ!) 複雑なオブジェクトを#{code 'puts'}するだけで、それが何なのかわかる...、 たとえば、おばあちゃんの写真だったり、音楽のファイルだったり、が わかるというのは素晴らしいことなのです。この辺に関してはまた後ほど説明します。 END_PARAGRAPH end para do <<-END_PARAGRAPH とりあえずは、いくつかの新しいメソッドを覚えて、楽しいプログラムを書けるように していきましょう。 END_PARAGRAPH end h2 {'メソッド '+(code 'gets')+' と '+(code 'chomp')} para do <<-END_PARAGRAPH もし#{code 'puts'}がput string という意味であるというなら、 #{code 'gets'}は何の略なのか当てられますね。#{code 'puts'}がいつも文字列を 出力するのと同様に#{code 'gets'}は文字列の入力だけをします。といっても、 どこから文字列を受け取るのでしょう。 END_PARAGRAPH end para do <<-END_PARAGRAPH それはあなたからです。もう少し正確に言えばあなたのキーボードから。 キーボードは文字列を打つことしかできませんから、これは十分にうまく働く わけです。実際何が起こるかというと、#{code 'gets'}はその場で止まって あなたがEnterキーを押すまであなたがキーボードから打ったものを 読み続けます。さて、では、試してみましょう。 END_PARAGRAPH end prog ['もしもし聞こえますかぁ?'] do <<-END_CODE puts gets END_CODE end para do <<-END_PARAGRAPH もちろん、何をタイプしようと復唱されてきます。 何度か実行して違うものを入力してみてください。 END_PARAGRAPH end para do <<-END_PARAGRAPH さぁ、これでインタラクティブなプログラムが作れるようになりました! 次に示すのは、名前をタイプしてもらって、それにあいさつを返すプログラムです。 END_PARAGRAPH end prog ['Chris'], 'あれっ! このプログラム実行して—、名前を入れて...、結果を見てください。' do <<-END_CODE puts 'こんにちは! 君の名前は何かな?' name = gets puts '君の名前は ' + name + 'だね. 良い名前じゃないか!' puts 'よろしく, ' + name + '. :)' END_CODE end para do <<-END_PARAGRAPH んー。どうやら C, h, r, i, s, というように文字を入力していって最後にEnterを 押したところ、#{code 'gets'}は私の名前 Enterを すべて取り込んでしまったようです。 でも、幸いなことに、こんなときに使えるメソッドがあるんです。#{code 'chomp'}といいます。 このメソッドは文字列の最後にぶら下がっているどんなEnterも取り去る 働きがあります。 では、今度は#{code 'chomp'}を使ってこのプログラムをもう一度書き直してみましょう。 END_PARAGRAPH end prog ['Chris'] do <<-END_CODE puts 'こんにちは! 君の名前は何かな?' name = gets.chomp puts '君の名前は ' + name + 'だね. 良い名前じゃないか!' puts '会えてうれしいよ, ' + name + '. :)' END_CODE end para do <<-END_PARAGRAPH これでうまくいきました。ここで、#{code 'name'} は #{code 'gets.chomp'} を 指し示すようにしたので、あとから、#{code 'name.chomp'} とする必要がないことに 注意してください。つまり、#{code 'name'} はすでに #{code 'chomp'} されているのです。 END_PARAGRAPH end h2 {'練習問題'} para do <<-END_PARAGRAPH • 最初に姓、次に名前を聞いて、最後にフルネームに対してあいさつを するようなプログラムを書いてみましょう。 END_PARAGRAPH end para do <<-END_PARAGRAPH • 好きな数を入力してもらい、それに1を加えて、その結果を ベターな 数字として薦めるプログラムを書きましょう。 (機転の利いたサジェスチョンに努めましょう。) END_PARAGRAPH end para do <<-END_PARAGRAPH さて、この2つのプログラムを書き終えたら(そしてその他にもいくつか試してみたら) #{makeLink('メソッド', :generateMethods)}について、さらに学んでいきましょう。 END_PARAGRAPH end end # # METHODS # def generateMethods para do <<-END_PARAGRAPH ここまでで結構な数の種類のメソッドを見てきました。 #{code 'puts'} とか #{code 'gets'} など...です。 (突然ですがクイズです。: ここまで出てきたすべてのメソッドを 挙げてみてください。10個あるはずです。回答は後ほど。 ) 今までメソッドが本当は何者なのかについて話してはきませんでした。 というより、何をするかは知っているのですが、何なのかは知りません。 END_PARAGRAPH end para do <<-END_PARAGRAPH しかし、本当は、それこそが メソッドを表している言い方なのです。つまり メソッドとは何かを行うものということです。(文字列や整数や浮動小数点数のような) オブジェクトをRubyの名詞とするなら、メソッドは動詞のようなものです。 そして、英語の文法と同様、その動作を行う 主体の名詞がなくては 動詞を意味あるものにすることはできません。たとえば'tick' (訳註:時計が時を刻むという意味)は何かを起こすものではなくて、時計が しなければならないことです。英語では "The clock ticks."といいます。 Rubyでは、これを#{code 'clock.tick'}と書きます。(訳註:間の#{code '.'}に注意) (もちろん、ここで、#{code 'clock'}をRubyのオブジェクトであると仮定しています。) プログラマはこのことを「#{code 'clock'}の#{code 'tick'}メソッドを呼ぶ。」というような 言い方をします。 END_PARAGRAPH end para do <<-END_PARAGRAPH さっきのクイズやってみましたか? よろしい! では答えです。 まずは、先ほど述べたばかりの命令たち #{code 'puts'}, #{code 'gets'}, そして #{code 'chomp'} は覚えているでしょう。 次に、変換メソッド#{code 'to_i'}, #{code 'to_f'}, そして #{code 'to_s'}についてもおそらく大丈夫 だったのではないでしょうか? ということは残り4つですが、どうでしょう、わかりましたか? 残りのメソッドというのは計算機を作るときに用いた記号、#{code '+'}, #{code '-'}, #{code '*'}, そして #{code '/'}のことです。 これらは演算メソッドと呼ばれます。 END_PARAGRAPH end para do <<-END_PARAGRAPH 先ほど、すべての動詞に名詞が必要なように、すべてのメソッドには オブジェクトが必要であると言いました。 通常、どのオブジェクトがそのメソッドを実行している主体なのか というのはすぐわかります。#{code 'clock.tick'}の例や #{code '101.to_s'}でわかるように、メソッドにはたいていその前に ドットがあってその前にオブジェクトが書いてあるからです。 しかし、演算メソッドのように時々それが明確ではないことがあります。 でも、実は#{code '5 + 5'}というのは、#{code '5.+ 5'}の省略形だったのです。 たとえば次のようにしてみればわかりますね。 END_PARAGRAPH end prog do <<-END_CODE puts 'hello '.+ 'world' puts (10.* 9).+ 9 END_CODE end para do <<-END_PARAGRAPH この書き方はあまりかっこよくはないので、これ以降はもうこんなふうには書かないと 思いますが、演算メソッドを使っているときに実際には 何が 起きてるのかというのを理解するには重要です。 (私の機械では上のような書き方をすると #{output 'warning: parenthesize argument(s) for future version'} のような警告(warning) が表示されます。 これは、コード自体は順調に動作するけれど、コードの意味していることが あいまいだから、将来のバージョンのためにもカッコ(parenthesis)をもっと 使いなさい、と忠告しているのです。) これはまた、なぜ#{code "'pig'*5"}は実行できて#{code "5*'pig'"}がだめなのかに 対するより深い理解を提供します。 #{code "'pig'*5"}は#{code "'pig'"}に掛け算をしろと告げていますが、 #{code "5*'pig'"}では#{code '5'}に対して掛けろと指示しています。 #{code "'pig'"}はそれを#{code '5'}つコピーして、それらをみんな 加え合わせる方法を知っていますが、 #{code '5'}は自分自身を#{code "'pig'"}個複製して足し合わせる 方法を知らないわけです。 END_PARAGRAPH end para do <<-END_PARAGRAPH では#{code 'puts'} や #{code 'gets'} はどうなんだということになりますが、もちろん これについても説明しないといけませんね。 これらのオブジェクトはどこにあるのでしょう。 英語では、主語が取り除かれることがあります。 たとえば、悪漢が「死ね!(Die!)」と叫んだとすると暗黙の主語は叫ばれた人です。 Rubyでは、私が #{code "puts 'to be or not to be'"} と言ったなら、これは正確には #{code "self.puts 'to be or not to be'"} と言っていることを意味しています。 それでは、#{code 'self'}とはいったい何でしょう。自分自身? 実は、これはあなたが「今いる」ところを指し示している特別な変数なのです。 「今いる」とは何でしょう。まだ、オブジェクトの中に入る 方法については 説明してはいないのですが、その話の前にまず、プログラムの実行の際にはいつも必ず ひとつの大きなオブジェクトの中に入っている、ということを理解してください。 そのオブジェクトとは...、そう、プログラム全体です。 ラッキーなことにプログラム全体の中にあるメソッド、たとえば、#{code 'puts'}とか #{code 'gets'}とかですが、はそんなにたくさんはありません。 これを見てください。selfがあってもなくても結果は同じでしょう?(訳注:これは、著者の誤りで、 試してもうまくいかないと思います。理由は#{code 'puts'}が#{code 'Kernel'}の プライベートメソッドだからなのですが、そのことはここで理解できなくても問題ないと思います。) END_PARAGRAPH end prog do <<-END_CODE variable = 3 puts variable self.puts variable END_CODE end para do <<-END_PARAGRAPH これまでメソッドの概念を説明しましたが、完全にフォローできなくても大丈夫です。 大事なことは、すべてのメソッドは必ず何らかのオブジェクトによって実行されるのだ、ということです。 たとえそのメソッドの直前に、ドット(#{code '.'})が見当たらなくてもです。 このことが理解できていれば十分です。 END_PARAGRAPH end h2 {'文字列メソッド特選'} para do <<-END_PARAGRAPH それではここからいろいろなメソッドについて学んでいきましょう。 まずは楽しい文字列のメソッドからです。 とはいっても、これから示すメソッドを全部覚える必要は全くありません。 忘れたらこのページを見直せばいいんですから。 今から文字列にできることのほんの少し をお見せしたいと思います。 事実、私自身文字列メソッドの半分も覚えているわけではありません。 —それでかまわないのです。なぜならインターネット上にすべての 文字列のメソッドのリストと説明を含むすばらしいリファレンスが存在するからです。 (このチュートリアルの終わりで、それをどこで見つければ良いのかを教えます。) 事実、私は全部のメソッドを覚えたいとも 思いません。 それはたとえば辞書のすべての単語を覚えるようなものです。 私は辞書のすべての単語を知らなくても十分英語が話せます...というより、辞書とはそういう ものでしょう?ということで、あなたもメソッド全部を知らなくても大丈夫 です。 END_PARAGRAPH end para do <<-END_PARAGRAPH さて、最初の文字列のメソッドは#{code 'reverse'}です。これはある文字列の逆さ読みの 文字列を返します。(訳註:ASCII文字列に限ります。) END_PARAGRAPH end prog do <<-END_CODE var1 = 'stop' var2 = 'stressed' var3 = 'Can you pronounce this sentence backwards?' puts var1.reverse puts var2.reverse puts var3.reverse puts var1 puts var2 puts var3 END_CODE end para do <<-END_PARAGRAPH これをみるとわかるように #{code 'reverse'} は、オリジナルの文字列を 逆さにはしていません。新しい逆さ文字列を作って返しているだけです。 それが #{code 'var1'} に#{code 'reverse'} のメソッドを呼び出した後でも #{code 'var1'} が #{code "'stop'"} のままである理由です。 END_PARAGRAPH end para do <<-END_PARAGRAPH 次の文字列メソッドは #{code 'length'} です。このメソッドはその文字列の文字数 (スペースを含みます。)を返します。 END_PARAGRAPH end prog ['Christopher David Pine'] do <<-END_CODE puts 'あなたのフルネームは何ですか?' name = gets.chomp puts 'あなたの名前は ' + name.length + ' 文字だったって知っていましたか, ' + name + '?' END_CODE end para do <<-END_PARAGRAPH あれれ! 何かおかしいようです。 #{code 'name = gets.chomp'}の行の後でエラーが起こっているようですね。 何が変だかわかりますか。考えてみましょう。 END_PARAGRAPH end para do <<-END_PARAGRAPH 問題は#{code 'length'}にあります。このメソッドは数を返します。しかし、 ここは文字列が欲しいところです。答えは簡単。これに#{code 'to_s'}を 付け足せばいいんです。(うまくいきますように・・) END_PARAGRAPH end prog ['Christopher David Pine'] do <<-END_CODE puts 'あなたのフルネームは何ですか?' name = gets.chomp puts 'あなたの名前は ' + name.length.to_s + ' 文字だったって知っていましたか, ' + name + '?' END_CODE end para do <<-END_PARAGRAPH いやぁ、知らなかったなぁ。注意: これは私の名前のキャラクタ 数(スペースを含む)であって 文字 数ではありません。(数えてみてください。) (訳註:著者は記号を含む文字を「キャラクタ(character)」、アルファベットの文字を 「文字(letter)」という言葉で使い分けています。日本語ではどちらも「文字」と訳しますが、 今後は、上記のように区別します。) 文字数を計算するプログラムだったら、ファーストネーム、ミドルネーム、ラストネームと 別々に聞いてそれらの文字列の長さ(length)を足し合わせるという手を使えばよいでしょう。 (訳註:日本人の名前なら、ミドルネームは要りませんね。) さぁ、やってみてごらんなさい。待ってますから。 END_PARAGRAPH end para do <<-END_PARAGRAPH できましたか?はいよろしい。プログラムするのは楽しいですよね。 これから何章か進むと、びっくりするくらいいろいろなことができるようになりますよ。 END_PARAGRAPH end para do <<-END_PARAGRAPH 文字列のアルファベットの大小(つまり、大文字とか小文字とか)に関してもそれを変える 様々なメソッドがあります。 #{code 'upcase'}はすべての小文字を大文字に変えます。 逆に#{code 'downcase'}はすべての大文字を小文字にします。 #{code 'swapcase'}は大文字を小文字に小文字を大文字にお互いに逆にします。 最後に、#{code 'capitalize'}は最初のアルファベットの文字を大文字にして、残りを #{code 'downcase'}と同じように小文字にします。 END_PARAGRAPH end prog do <<-END_CODE letters = 'aAbBcCdDeE' puts letters.upcase puts letters.downcase puts letters.swapcase puts letters.capitalize puts ' a'.capitalize puts letters END_CODE end para do <<-END_PARAGRAPH 予想通り。#{code "puts ' a'.capitalize"}の行で見られるように#{code 'capitalize'}メソッドは 最初の文字 ではなく、最初のキャラクタを大文字にします。 また、前に見てきたのと同じようにいろいろなメソッドを読んだ後でも#{code 'letter'}の変数の指し示す 内容は変わっていません。このことをくどくど言うつもりはありませんが、理解しておくことは重要です。 メソッドの中には対応するオブジェクトを変化させてしまう ものも存在します。 ただ、まだ出てきてはいませんし、しばらくは出てこないでしょう。 END_PARAGRAPH end para do <<-END_PARAGRAPH 特選メソッドの最後は視覚フォーマット系のものです。 まずは#{code 'center'}です。このメソッドは文字列の最初と最後にスペースをつけて 元の文字列を中央寄せします。しかし、#{code 'puts'}に対して何を表示したいのかを 告げたように、あるいは#{code '+'}に対して何を足したいのかを示したように、 #{code 'center'}には、文字列をどのくらいの幅でセンタリングしたいのかを告げます。 たとえば、次のような詩を中央よせしたいなら、こんなふうにします。 END_PARAGRAPH end prog do <<-END_CODE lineWidth = 50 puts( 'ハバードのおばさんが'.center(lineWidth)) puts( '食器棚の中に座ったよ'.center(lineWidth)) puts( 'カードとホエーを食べながら'.center(lineWidth)) puts( '蜘蛛がそばにやってきて'.center(lineWidth)) puts( 'おばさんの隣に座ったよ'.center(lineWidth)) puts('かわいそうな犬は一目散に逃げ出した'.center(lineWidth)) END_CODE end para do <<-END_PARAGRAPH ふむ。この詩の続きがこの後どうなるかは思いつきません。私は怠け者ですから。 (#{code '.center(lineWidth)'}を縦に並べたかったので、詩の文字列の前に 余分なスペースを入れました。この方がきれいだと思ったからです。 プログラマはよくプログラムの中で何がきれい(pretty)かというのにこだわっていて、 たまには、意見が分かれたりします。あなたも、プログラムを書くようになればなるほど 自分のスタイルが身についていくと思います。) 怠ける(being lazy)ということに関して言えば、プログラミングには怠け心は 悪いことばかりではないのです。たとえば上のプログラムで、詩の行の長さをどんなふうに 変数#{code 'lineWidth'}に記録したかを見てください。 こうすることで、もし、後で見返してこの詩をより幅広くしたくなったとしても、 センタリングさせたいすべての行の行幅の数字を変える代わりに、プログラムの最初の行だけを ちょっと変えるだけで済んでしまいます。 もし、とても長い詩だったら、ずいぶんの時間が稼げます。こんなふうな怠け癖は プログラミングにとっては美徳なのです。 END_PARAGRAPH end para do <<-END_PARAGRAPH ところで、このセンタリングに関しては...ワープロがするほど美しくはないことに気が 付いたかも知れません。もし完璧なセンタリング(それもより美しいフォントで)を望むなら ワープロを使うべきです。Rubyはすばらしい道具ではあるけれど、どんな道具も すべて の仕事に最適ということはありえません。 END_PARAGRAPH end para do <<-END_PARAGRAPH 残り2つのフォーマットメソッドは#{code 'ljust'}と#{code 'rjust'}です。これは それぞれ左寄せ(left justify)右寄せ(right justify) の意味です。 これらのメソッドはそれぞれ右側と左側だけにスペースを埋めると言うことが違うだけで、 #{code 'center'}と似ています。では、この3つを全部使って見ましょう。 END_PARAGRAPH end prog do <<-END_CODE lineWidth = 40 str = '--> 文章 <--' puts str.ljust lineWidth puts str.center lineWidth puts str.rjust lineWidth puts str.ljust (lineWidth/2) + str.rjust (lineWidth/2) END_CODE end h2 {'練習問題'} para do <<-END_PARAGRAPH • 「怒ったボス」のプログラムを書いてみましょう。 まず、無作法に何が望みか聞いてきます。 で、何を答えようと「怒ったボス」はそれを叫び返して、あなたを首にします。 たとえば、もし#{input '給料上げてください'}とタイプしたとすると #{output 'なにぃ? "給料上げてください" だとー!! おまえは首だ!!'} と、叫び返してきます。 END_PARAGRAPH end para do <<-END_PARAGRAPH • #{code 'center'}, #{code 'ljust'}, そして #{code 'rjust'} を使ってもう少し何かやってみましょう。こんな感じの「目次」を表示する プログラムを書いてみてください。 END_PARAGRAPH end puts '
' +
          '               目  次                       ' + $/ +
          '                                            ' + $/ +
          '1章:  数                                p. 1' + $/ +
          '2章:  文字                             p. 72' + $/ +
          '3章:  変数                            p. 118' + $/ +
          '
' h2 {'すこし程度の高い数学'} para do <<-END_PARAGRAPH (この節は完全にオプショナルです。ある程度の数学の知識を前提としています。 もし興味がなかったら、#{makeLink '制御構造', :generateFlowControl}へ直接 飛んでいっても何の問題もありません。ただ、乱数の節をチラッと 眺めておくと役に立つかもしれません。) END_PARAGRAPH end para do <<-END_PARAGRAPH 数に対するメソッドは文字列ほどは多くありません。(とは言っても、私が全部頭に 入れているわけではありませんが。)さて、ここでは、演算のためのメソッドの残りと 乱数発生メソッド、そして三角関数や超越関数(代数で表せない関数、指数関数など)の メソッドを持っている#{code 'Math'}オブジェクトについてみていきましょう。 END_PARAGRAPH end h2 {'もっと演算メソッド'} para do <<-END_PARAGRAPH 演算メソッドのうち#{code '**'}(べき乗)と#{code '%'}(余り)を 紹介しましょう。Rubyで「5の2乗」を言いたいときは#{code '5**2'}と書きます。 指数のところには、小数を使うこともできるので、5の平方根を求めることもできます。 つまり#{code '5**0.5'}とします。 一方、余りメソッドは数で割り算をしたときの余りを返します。たとえば 7を3で割ると答えは2で余りは1です。では、プログラムを見てみましょう。 END_PARAGRAPH end prog do <<-END_CODE puts 5**2 puts 5**0.5 puts 7/3 puts 7%3 puts 365%7 END_CODE end para do <<-END_PARAGRAPH 最後の行から、(うるう年ではない)1年というのは、何週間かとプラス1日であることが わかりますね。なので、例えば今年の誕生日が火曜日だったら来年は水曜日になると予測 できます。また、余りメソッドは浮動小数点数にも使うことができます。 小数に余りというのはちょっと不思議かもしれませんが、基本的に、 できるだけ道理にかなったやり方で実現されているようです。 とにかく、これに関してはいろいろ試してみることをお勧めします。 END_PARAGRAPH end para do <<-END_PARAGRAPH 乱数発生の節に進む前に述べておく最後のメソッドは#{code 'abs'}です。 これは数の絶対値を返します。 END_PARAGRAPH end prog do <<-END_CODE puts((5-2).abs) puts((2-5).abs) END_CODE end para do <<-END_PARAGRAPH (訳註:括弧の使い方に注意してください。こうしないとエラーが起きてしまいます。) END_PARAGRAPH end h2 {'乱数'} para do <<-END_PARAGRAPH Rubyには、とても素敵な乱数発生機がついています。ランダムに選んだ数字を 得るためのメソッドは#{code 'rand'}です。#{code'rand'}と、後に何もつけずに 呼び出すと、#{code '0.0'}以上#{code '1.0'}未満の浮動小数点数を 得ることができます。また、#{code 'rand'}を(例えば#{code '5'}のような) 整数を後につけて呼び出すと#{code '0'}以上#{code '5'}未満(つまり、 #{code '0'}から#{code '4'}までの5つの中から1つ)の整数が選ばれます。 END_PARAGRAPH end para do <<-END_PARAGRAPH では実際に#{code 'rand'}がどう動くか見てみましょう。(このページをリロードすると、 毎回この数字は変わります。)ここにあるプログラムは、実際に今、実行しているものだ というのを知っていましたか? END_PARAGRAPH end prog do <<-END_CODE puts rand puts rand puts rand puts(rand(100)) puts(rand(100)) puts(rand(100)) puts(rand(1)) puts(rand(1)) puts(rand(1)) puts(rand(99999999999999999999999999999999999999999999999999999999999)) puts('天気予報によると, 今日の降水確率は '+rand(101).to_s+'% です,') puts('でも予想は予想ですから.') END_CODE end para do <<-END_PARAGRAPH ここで、#{code '0'}から#{code '100'}までの間の数字を得るのに#{code 'rand(101)'} という使い方をしているのに注意してください。あるいは、#{code 'rand(1)'}が 常に#{code '0'}を返すというのがわかるでしょうか? このように、返ってくる値の範囲をしっかり理解していないと、 #{code 'rand'}を使った時に非常に気づきにくい大きな間違いを犯してしまう ことになります。 プロのプログラマの、それも店で買うことのできる最終製品のプログラムの中にも このミスが潜んでいることがあります。 私がかつて持っていたCDプレーヤで「ランダムプレイ」を選ぶと、ランダムに曲が 演奏されていくのですが、最後の曲だけは...演奏されなかったのです。 (このプレーヤに1曲だけ入ったCDを入れたとき、何事かと思いました。) END_PARAGRAPH end para do <<-END_PARAGRAPH 時には、プログラムを実行するたびに違う乱数ではなくて、 同じ 乱数が同じ順序で返ってくるような#{code 'rand'}メソッドが欲しい ことも起こるんじゃないでしょうか? (例えば、かつて私は、コンピュータゲームの中で ランダムに構成された世界を作るのにランダムに作られる数値を使っていました。で、 ある1つの世界が非常に気に入ったとしたら、もう一度その世界でプレイしたくなる でしょうし、それを友達に送りたくもなります。)これをするためには、 乱数の種(seed) を#{code 'srand'}を使ってセットする必要があります。 こんなふうに。 END_PARAGRAPH end prog do <<-END_CODE srand 1776 puts(rand(100)) puts(rand(100)) puts(rand(100)) puts(rand(100)) puts(rand(100)) puts '' srand 1776 puts(rand(100)) puts(rand(100)) puts(rand(100)) puts(rand(100)) puts(rand(100)) END_CODE end para do <<-END_PARAGRAPH 同じ数で種を与えると、毎回同じ動作をするようになります。 また、(#{code 'srand'}を使わなかったときのように)毎回異なる数字が 出るようにしたくなったら、#{code 'srand 0'}とすればよいのです。 これにより、種として本当にランダムな数字(コンピュータの現在時刻を ミリセカンドの単位で用いたり)を使うようになります。 END_PARAGRAPH end h2 {"#{code 'Math'} オブジェクト"} para do <<-END_PARAGRAPH 最後に#{code 'Math'}オブジェクトについてみて行きましょう。 とりあえず使っているところを見てください。 END_PARAGRAPH end prog do <<-END_CODE puts(Math::PI) puts(Math::E) puts(Math.cos(Math::PI/3)) puts(Math.tan(Math::PI/4)) puts(Math.log(Math::E**2)) puts((1 + Math.sqrt(5))/2) END_CODE end para do <<-END_PARAGRAPH 最初に気がつくことはおそらく#{code '::'}という表現でしょう。 これは、スコープ解決演算子(scope operator) というもの なのですが、これを説明するのは、んー、このチュートリアルの範囲(scope)を 超えているようです。えーと、これは駄洒落じゃないですよ:)。 ここでは、#{code 'Math::PI'}というのが、まさにあなたがそうだろうと 思っているものだというだけにとどめておきましょう。 END_PARAGRAPH end para do <<-END_PARAGRAPH また、上で見てもらえばわかる様に、#{code 'Math'}はまともな関数電卓 に当然あるべきすべての関数を持っています。そして例のとおり、 浮動小数点数は正確な値に非常に近い近似値 になっています。 END_PARAGRAPH end para do <<-END_PARAGRAPH さて、それでは#{makeLink '制御構造', :generateFlowControl} へと、進みましょう! END_PARAGRAPH end end # # FLOW CONTROL # def generateFlowControl para do <<-END_PARAGRAPH とうとう、「制御構造」の章まで来ましたね。プログラムを組もうとする人は必ずこれを 覚えることになります。 この章が前の#{makeLink 'メソッド', :generateMethods}の章と比べて短くて簡単だとしても これを知ることによってすべてのプログラムの可能性が開けてくるのです。 この章が終わったら、真にインタラクティブなプログラムが書けるようになるはずです。 これまでは、あなたのキーボードからの入力に応じて異なることをしゃべる プログラムを 作ってきましたが、この章からは入力に応じて異なることを実行する ようになります。 でもその前に、前段階として、プログラムの中でオブジェクトを比較することができるようになる 必要があります。つまり、、、 END_PARAGRAPH end h2 {'比較メソッド'} para do <<-END_PARAGRAPH この部分はさっさと進めて次の分岐節に行くようにしましょう。 そこで初めてクールな技が使えるのです。 さて、ひとつのオブジェクトがもうひとつに対して、より大きいかどうか、あるいは、 より小さいかどうかを知るには、#{code '>'} あるいは、#{code '<'} のメソッドを使います。 こんなふうに。 END_PARAGRAPH end prog do <<-END_CODE puts 1 > 2 puts 1 < 2 END_CODE end para do <<-END_PARAGRAPH 問題ないでしょう?(訳註:trueは真、falseは偽を表します。) これと同じように、あるオブジェクトがもうひとつのオブジェクト以上かどうか (あるいは...以下かどうか)は、#{code '>='}メソッド(#{code '<='}メソッド)を 使うとわかります。 END_PARAGRAPH end prog do <<-END_CODE puts 5 >= 5 puts 5 <= 4 END_CODE end para do <<-END_PARAGRAPH そして最後に、2つのオブジェクトが、等しいか否かは#{code '=='}(等しいですか?) あるいは#{code '!='}(異なっていますか?)のメソッドを使って知ることができます。 #{code '='}と#{code '=='}の2つを混同しないようにするのは重要です。 #{code '='}は変数にどのオブジェクトを指し示すかを告げる(代入する)ためにあり、 #{code '=='}は「これら2つのオブジェクトは等しいですか?」と質問をするためにあるのです。 END_PARAGRAPH end prog do <<-END_CODE puts 1 == 1 puts 2 != 1 END_CODE end para do <<-END_PARAGRAPH もちろん文字列を比較することもできます。文字列どうしを比較した場合、 その文字的順序 、つまり辞書順という意味ですが、によって 比べることになります。辞書(アルファベットの順番の辞書)では #{code 'cat'}は#{code 'dog'}の前に来ます。なので、 END_PARAGRAPH end prog do <<-END_CODE puts 'cat' < 'dog' END_CODE end para do <<-END_PARAGRAPH でもこれにはちょっと落とし穴があります。コンピュータの中では 大文字が小文字よりも先に来ます。(これは、文字のフォントをどのように 保存するかにかかっています。たとえば最初に大文字全部、その後小文字が続く という具合です。) つまり、この方法だと#{code "'Zoo'"}が#{code "'ant'"}の前に来ることに なります。なので、本当の辞書において、どちらの単語が先に来るかを はっきりさせたいときには、文字列を比較しようとする前にはどちらの単語についても #{code 'downcase'}(あるいは#{code 'upcase'}か#{code 'capitalize'})を行っておく ことが必要です。 END_PARAGRAPH end para do <<-END_PARAGRAPH 後ひとつ、分岐に進む前に知っておきたいことがあります。それは、 比較メソッドが#{code "'true'"} や#{code "'false'"}のような文字列を返しているのではなく、 特別なオブジェクトである#{code 'true'}と#{code 'false'}を返しているのだということ です。(もちろん、#{code 'true.to_s'}は#{code "'true'"}を返します。 これが#{code 'puts'}メソッドが#{code "'true'"}を表示している理由です。) #{code 'true'}や#{code 'false'}は、以下の節で常に使われています。 END_PARAGRAPH end h2 {'分岐'} para do <<-END_PARAGRAPH 分岐はシンプルな概念ですが強力です。実際、非常に単純なので、まったく説明しなくても いいと思うくらいです。それではお見せしましょう。 END_PARAGRAPH end run1 = {:input => ['Chris']} run2 = {:input => ['Chewbacca'], :remark => 'でも、ほかの名前を入れると・・・'} progN run1, run2 do <<-END_CODE puts 'こんにちは, 君の名前は何かな?' name = gets.chomp puts 'こんにちは, ' + name + '.' if name == 'Chris' puts '良い名前だね!' end END_CODE end para do <<-END_PARAGRAPH これが分岐です。もし#{code 'if'}の後に来るものが#{code 'true'}なら #{code 'if'}と#{code 'end'}の間のコードを実行します。 #{code 'if'}の後に来るものが#{code 'false'}なら実行しません。簡単で単純。 END_PARAGRAPH end para do <<-END_PARAGRAPH 上のプログラムで、私は#{code 'if'}と#{code 'end'}の間を字下げしました。 こうすると、分岐をしているのがどこまでなのかがわかりやすくなります。 ほとんどのプログラマは、使っているプログラミング言語にかかわらずこれをします。 こういう単純なプログラムではありがたみはないように見えますが、 プログラムが複雑になってくると断然違ってきます。 END_PARAGRAPH end para do <<-END_PARAGRAPH しばしば、ある式が#{code 'true'}の時にあることをして、#{code 'false'}のときに 別のことをしてくれるようなプログラムを書きたくなることがあります。 このために#{code 'else'}があります。 END_PARAGRAPH end run1 = {:input => ['Chris']} run2 = {:input => ['Ringo'], :remark => '違う名前で試してみましょう...'} progN run1, run2 do <<-END_CODE puts '私は預言者である. 名を告げなさい:' name = gets.chomp if name == 'Chris' puts 'おー、輝かしい未来が見えるぞよ.' else puts '汝の未来は... おっと待った. いま何時だ!' puts '私は行かねばならない. 失敬する!' end END_CODE end para do <<-END_PARAGRAPH 分岐は、コードをたどっていて分かれ道に到達したようなものです。 #{code "name == 'Chris'"}が成り立つ人の道を行くか、 あるいは(#{code 'else'})、そのほかの道を行くか、です。 END_PARAGRAPH end para do <<-END_PARAGRAPH そして、木の枝(branches)が枝分かれしているように、 分岐した先で、また分岐することも出来ます。 END_PARAGRAPH end run1 = {:input => ['chris', 'yes']} run2 = {:input => ['Chris'], :remark => 'うまくいっていますね。先頭の文字を大文字にして(capitalizeして)試してみます...'} progN run1, run2 do <<-END_CODE puts 'ハロー, 第7学年の英語の授業にようこそ.' puts '私の名前はミセスガバードです. あなたのお名前は...?' name = gets.chomp if name == name.capitalize puts 'はい, お座りなさい, ' + name + '.' else puts name + '? えーと, ' + name.capitalize + ' という意味ですね.' puts '自分の名前の綴り方くらいは知っていますね??' reply = gets.chomp if reply.downcase == 'yes' puts 'ふむ! よろしい、座りなさい!' else puts '出て行きなさい!!' end end END_CODE end para do <<-END_PARAGRAPH 時々、#{code 'if'}、#{code 'else'}、そして#{code 'end'}の全部が どこにあるのか確かめようとして訳がわからなくなることがあります。 これに対して、私がやっているうまい方法は#{code 'if'}を書くときに #{code 'end'}を同時に 書いてしまうというやり方です。 上のプログラムはそうやって書いたのですが、ここに、その途中段階 を示します。 END_PARAGRAPH end prog false do <<-END_CODE puts 'ハロー, 第7学年の英語の授業にようこそ.' puts '私の名前はミセスガバードです. あなたのお名前は...?' name = gets.chomp if name == name.capitalize else end END_CODE end para do <<-END_PARAGRAPH その後、間をコメント で埋めます。コメントというのは、 コード中にあって、コンピュータが完全に無視する文字です。 END_PARAGRAPH end prog false do <<-END_CODE puts 'ハロー, 第7学年の英語の授業にようこそ.' puts '私の名前はミセスガバードです. あなたのお名前は...?' name = gets.chomp if name == name.capitalize # 礼儀正しく. else # 怒り出す. end END_CODE end para do <<-END_PARAGRAPH #{code '#'}の後に来るものすべてはコメントとみなされます。 (もちろん文字列中でない限りですが。) で、私はその後、これらのコメントを実際に動くコードに置き換えていきます。 このようなコメントを残しておくのが好きな人もいますが、個人的には 「良く書かれたコードはそれ自身を語る」と思っています。 以前は私ももっとコメントを使っていたのですが、Rubyを「すらすらと」と使う ようになればなるほどコメントは使わなくなってきました。 実際、コメントを書くと結構時間を食われてしまいます。 これは個人的な選択です。 あなたも自分なりの(そして常に進化していく)スタイルを見つけることでしょう。 さて、私が採っている方法を続けると、次のステップはこんな感じになります。 END_PARAGRAPH end prog false do <<-END_CODE puts 'ハロー, 第7学年の英語の授業にようこそ.' puts '私の名前はミセスガバードです. あなたのお名前は...?' name = gets.chomp if name == name.capitalize puts 'はい、お座りなさい, ' + name + '.' else puts name + '? えーと, ' + name.capitalize + ' という意味ですね.' puts '自分の名前の綴り方くらいは知っていますね??' reply = gets.chomp if reply.downcase == 'yes' else end end END_CODE end para do <<-END_PARAGRAPH またしても#{code 'if'}、#{code 'else'}、そして#{code 'end'} を同時に書きました。この方法はコードの中で「今どこにいるのか」を 覚えておくのにかなり役に立ちます。その上、#{code 'if'}と #{code 'else'}の間のような小さい場所に意識を集中できるので、 プログラムを書くのをよりやさしくさせてくれます。 もうひとつ、この方法をとる利点はコンピュータがどの段階でもプログラムを 理解できるということです。どの未完成バージョンのプログラムでも 実行させることが出来ます。未完成ではありながら実行可能なのです。 プログラムを書いている時点で、このようにテストを行い、 どんなふうに出来たのかと、これから何が必要なのかを確認するのに役立てて います。すべてのテストがうまくいったら、ここまで出来たというのが わかります。 END_PARAGRAPH end para do <<-END_PARAGRAPH このようなコツは、分岐を含むプログラムを書くときに役に立ちますが、 それだけではなくて、他にもある主な制御構造に対しても役立ちます。 END_PARAGRAPH end h2 {'ループ'} para do <<-END_PARAGRAPH プログラミングではしばしば、コンピュータに同じことを何度も何度も繰り返し 行わせたくなることがあると思います。これこそがコンピュータの得意技 ですよね。 END_PARAGRAPH end para do <<-END_PARAGRAPH コンピュータに同じことを繰り返し続けるように指示する時は、それを どうやって止めるのかも必ず指示する必要があります。 コンピュータは飽きることを知らないので、もし止まる方法を教えないと、 自動的に止まることはありません。ということで、無限の繰り返しが 起こらないように、ある特定の条件が正しい(true)の間(#{code 'while'})だけ、 プログラムのある部分を繰り返すように、コンピュータに告げるようにします。 END_PARAGRAPH end prog ['Hello?', 'こんにちは!', 'はじめまして.', 'あぁ...なんて素敵なんでしょう.', 'bye'] do <<-END_CODE command = '' while command != 'bye' puts command command = gets.chomp end puts 'また来て下さいね!' END_CODE end para do <<-END_PARAGRAPH これがループです。(出力の最初に空の行が1行あるのに気が付きましたか? これは最初の#{code 'gets'}の前にある最初の#{code 'puts'}から出ています。 この最初の行をなくすにはどう変えたら良いでしょうか? やってみましょう。 それは上のプログラムと比べて最初の空行が消えている以外、まったく 同じ動作をしますか?) END_PARAGRAPH end para do <<-END_PARAGRAPH ループを使えばあらゆる面白そうなことができそうでしょう? でも、ミスったときはちょっと問題を起こします。 コンピュータが無限ループに陥ってしまったらまずいですよね。 これが起こったなと思ったら、落ち着いて、 Ctrlキーを押しながらCを押しましょう。 END_PARAGRAPH end para do <<-END_PARAGRAPH ループを使っていろいろ遊んでみる前に、仕事を楽にするための、 2,3のことを学んでおきましょう。 END_PARAGRAPH end h2 {'論理を少し'} para do <<-END_PARAGRAPH もう一度、分岐を使った最初のプログラムを見てみましょう。 今度は、私の妻が来てプログラムを試してみたとします。 このままだと、プログラムは彼女に「良い名前だね!」とは言わないわけです。 私は彼女の気持ちを損ねたくはありません。(でないと、カウチで寝ることに・・) ということで、直しましょう。 END_PARAGRAPH end prog ['Katy'] do <<-END_CODE puts 'こんにちは, 君の名前は何かな?' name = gets.chomp puts 'こんにちは, ' + name + '.' if name == 'Chris' puts '良い名前だね!' else if name == 'Katy' puts '良い名前だね!' end end END_CODE end para do <<-END_PARAGRAPH はい、これで動きます... でもあまりかっこいいプログラムとはいえませんね。なぜでしょう? プログラミングに関して私が覚えたルールの中でベストなものは、 DRY ルールと呼ばれるものです。これはDon't Repeat Yourself(あなた自身を 繰り返しちゃいけない) の頭文字です。 多分、なぜそれがそんなに良いルールなのかについて、私は小さな本くらい書けると思います。 先ほどの場合だと、#{code "puts '良い名前だね!'"}の行を2回繰り返して書いています。 なぜこれがそんなにオオゴトなんでしょうか?たとえば、ここを書き直すときにスペルミスを してしまったとしたらどうでしょうか? あるいはこの両方の行で#{code "'良い名前'"} を #{code "'素敵な名前'"}に変えたくなったとしたらどうでしょう? 私は怠け者です。覚えてました? 基本的に、#{code "'Chris'"} か #{code "'Katy'"}の文字を入力されたときに プログラムに同じことをして欲しいなら、私は本当に同一のこと をしてもらいたい のです。つまり、こうです。 END_PARAGRAPH end prog ['Katy'] do <<-END_CODE puts 'こんにちは, 君の名前は何かな?' name = gets.chomp puts 'こんにちは, ' + name + '.' if (name == 'Chris' or name == 'Katy') puts '良い名前だね!' end END_CODE end para do <<-END_PARAGRAPH だいぶ良くなりました。上のことを実現するために、私は#{code 'or'}を使いました。 このような論理演算子 にはこの他に#{code 'and'}や#{code 'not'}があります。 このような演算子を使うときには、いつも一緒に括弧を使うというのは、良い考えです。 END_PARAGRAPH end prog do <<-END_CODE i_am_Chris = true i_am_Purple = false i_like_food = true i_eat_rocks = false puts (i_am_Chris and i_like_food) puts (i_like_food and i_eat_rocks) puts (i_am_Purple and i_like_food) puts (i_am_Purple and i_eat_rocks) puts puts (i_am_Chris or i_like_food) puts (i_like_food or i_eat_rocks) puts (i_am_Purple or i_like_food) puts (i_am_Purple or i_eat_rocks) puts puts (not i_am_Purple) puts (not i_am_Chris ) END_CODE end para do <<-END_PARAGRAPH この中で、変だと思うものがあるとしたら、#{code 'or'}(または)でしょう。 英語では(訳註:日本語でも)、私たちは"or"という言葉をよく 「これかあれかのどちらかで、両方ではない」という意味で使います。 たとえば、あなたのママが「パイかケーキをデザートに食べていいわよ。」 と言ったとしたら、両方とも食べてもいいということにはならない ですよね。でも、コンピュータは#{code 'or'}を「あれかこれか両方か」の 意味に使うのです。(別の言い方で言うと、「少なくとも一つは正しい」 ということです。)これがコンピュータがママより楽しい理由です。 END_PARAGRAPH end h2 {'練習問題'} para do <<-END_PARAGRAPH • "99本のビールが壁に..." 遠足などでよく歌われる古典的な童謡の歌詞、"99 Bottles of Beer on the Wall" を 出力するプログラムを書いてみましょう。 (訳註:マザーグースの歌なのですが、日本ではあまりメジャーではないので、 出力例をあげておきます。) END_PARAGRAPH end prog false do <<-END_CODE 99 Bottles of beer on the wall 99 Bottles of beer Take one down and pass it around 98 Bottles of beer on the wall 98 Bottles of beer on the wall 98 Bottles of beer Take one down and pass it around 97 Bottles of beer on the wall … 3 Bottles of beer on the wall 3 Bottles of beer Take one down and pass it around 2 Bottles of beer on the wall 2 Bottles of beer on the wall 2 Bottles of beer Take one down and pass it around 1 Bottle of beer on the wall 1 Bottle of beer on the wall 1 Bottle of beer Take one down and pass it around 0 Bottles of beer on the wall END_CODE end para do <<-END_PARAGRAPH • 耳の遠いおばあちゃんのプログラムを書いてみましょう。 おばあちゃんに何を言っても(何をタイプしても)、叫ばない限り (つまり、全部大文字でタイプしない限り)、 #{output 'は?! もっと大きな声で話しておくれ、坊や!'} と返事をします。もし叫んだときは、彼女はあなたの言葉を聞いて (少なくとも聞いた気がして)、 #{output 'いやー、1938年以来ないねー!'} と大声で返事をします。 プログラムにちょっと真実味 を持たせるため、 1930年から1950年のランダムな数字で毎回違う年を叫ぶようにしましょう。 (この部分はオプションです。#{makeLink 'メソッド', :generateMethods}の 章で、Rubyの乱数発生法の節を読んでいたら簡単でしょう。) あなたは#{input 'BYE'}と叫ぶまでおばあちゃんとの会話から逃れられません。
ヒント: #{code 'chomp'}をお忘れなく! Enter付きの #{code "'BYE'"}はついていない #{code "'BYE'"}とは 違うものです。
ヒント2:あなたの書いたプログラムのどの部分が 何度も何度も繰り返し実行されるべきなのかを良く考えてみましょう。 そのすべてを #{code 'while'}ループの中に入れます。 END_PARAGRAPH end para do <<-END_PARAGRAPH • 上で作った、「耳の遠いおばあちゃんのプログラム」を拡張しましょう。 おばあちゃんはあなたに行って欲しくないのです。 あなたが#{input 'BYE'}と叫んでもおばあちゃんは聞こえないふりをします。 #{input 'BYE'}を3回連続で 叫ばないといけないように変更してみてください。 #{input 'BYE'}と3回叫んでも 連続していない限り、おばあちゃんとの会話は続くようになっているか プログラムをテストして確認してみましょう。 END_PARAGRAPH end para do <<-END_PARAGRAPH • うるう年。 開始の年と終わりの年を聞いて、その間にあるすべてのうるう年を (もし開始や終了の年がうるう年だったらそれも含めて)表示するプログラムを書きましょう。 うるう年は(1984年とか2004年のように)4で割り切れる数の年です。ただし、100で割り切れる年は うるう年ではなくて (たとえば1800年や1900年)、さらに、それは 400で割り切れない限り です。(つまり、1600年や2000年はうるう年です。) (確かに、このルールは紛らわしいです。でも7月が冬の真ん中に来てしまうのはもっと紛らわしいと 思いませんか?このルールがなかったらそうなったかもしれないのです。) END_PARAGRAPH end para do <<-END_PARAGRAPH ここまでやったら少し休憩しましょう。すでにたくさんのことを学んできました。 おめでとう!! コンピュータにこんなにたくさんのことを指示できるようになったな んて、驚きでしょう? 後少しの章でどんなことでもプログラムができるようになりますよ!! んー、ちょっとこれは言い過ぎかもしれませんけれど、ループや分岐を覚える前には できなかったことで、新しくできるようになったこと全部を振り返ってみることは 良いことだと思います。 END_PARAGRAPH end para do <<-END_PARAGRAPH さて。一段落したところで、次の新しい種類のオブジェクトについて学んでいきましょう。 それは他のオブジェクト(物)の並びを保持しているオブジェクト、つまり #{makeLink '配列', :generateArrays}です。 END_PARAGRAPH end end # # ARRAYS AND ITERATORS # def generateArrays para do <<-END_PARAGRAPH 好きな数だけ単語の入力をしてもらい(1行に1単語、最後はEnterだけの空行)、 アルファベット順に並べ変えて出力するようなプログラムを書いてみましょう?いいですか? END_PARAGRAPH end para do <<-END_PARAGRAPH さて、最初に・・—えーと、あー、ふむふむ。 何を使えばいいのかというと— あーと、んーと。 END_PARAGRAPH end para do <<-END_PARAGRAPH まだ出来ないようです。まずは、いくつあるかわからない数の単語を保存する方法と、 その全部をどうやってまとめるのかを知らないとだめですね。 これが分かれば他の変数とはごっちゃにならないですみます。 何か一覧表(list)のようなものに単語を入れないといけないようです。 つまり配列 が必要です。 END_PARAGRAPH end para do <<-END_PARAGRAPH 配列とはコンピュータの中の一覧リストようなものです。 リストの中のすべての項目は変数のように振舞います。 特定の項目が何のオブジェクトを指し示しているかを知ることが出来るし、 それを別のオブジェクトにポイントしなおすことも出来ます。 では、配列について見てみることにしましょう。 END_PARAGRAPH end prog false do <<-END_CODE [] [5] ['Hello', 'Goodbye'] flavor = 'バニラ' # もちろんこれは配列ではありません。 [89.9, flavor, [true, false]] # ...でもこれは配列です。 END_CODE end para do <<-END_PARAGRAPH 最初は空の配列です。次はひとつの数字を含む配列で、 その次は2つの文字列を保持している配列です。 その次の行は普通の代入文です。そしてその次は3つのオブジェクトを保持している 配列を示しています。その中の最後の要素であるオブジェクト #{code '[true, false]'}もまた、配列です。 変数がオブジェクトではないことを思い出してください。ここで、 最後の配列は順に、浮動小数点数、文字列 、そして、配列を 指し示しているわけです。 たとえ、あとから#{code 'flavor'}を他のものを指し示すようにセット しなおしたとしても、もとの配列に変更が加わることはありません。 END_PARAGRAPH end para do <<-END_PARAGRAPH 配列の中の特定のオブジェクトを見つけるために、それぞれの項目には インデックスとなる数が振ってあります。プログラマたちは(それと、 偶然にも多くの数学者たちも)ゼロから数える習慣があります。たとえ、その結果、 1番目の項目が項目0と呼ぶことになったとしてもです。 では、この番号を使って配列のオブジェクトをどう呼び出すかを見てみましょう。 END_PARAGRAPH end prog do <<-END_CODE names = ['Ada', 'Belle', 'Chris'] puts names puts names[0] puts names[1] puts names[2] puts names[3] # これは範囲外です。 END_CODE end para do <<-END_PARAGRAPH 最初、#{code 'puts names'}によって、#{code 'names'}配列に入っている それぞれの名前が表示されるのがわかります。その後で、#{code 'puts names[0]'} を使って「1番目の」名前がプリントされます。その次は、#{code 'puts names[1]'} で、「2番目」...以下同様です。はい、確かに紛らわしいのは私も認めます。 しかし、これには慣れるしか ないようです。とにかく物を数える時には 頭の中で 0から数えるようにして、「1番目」とか「2番目」とか言う言葉は 使わないようにしたほうが良いかもしれません。 たとえば、外食して5コースの料理を食べるとき、1番目のコース料理といわずに コース0と言いましょう。(そして、頭の中では#{code 'course[0]'}と思うわけです。) あなたの右手には5本の指があると思います。その番号は、0, 1, 2, 3, そして4です。 妻と私はジャグラーなんですが、2人で6本のクラブをジャグリングしているとき、 それらはクラブ0-5です。数ヵ月後にはクラブ6をジャグリングできるようにしたいと 思っています。(それができたときは、2人の間には7本のクラブがあることになります。) そのうち、「その配列の4番目の要素」といって間違えることなく#{code 'myArray[3]'}の ことを指すようになっていると思います。そのころには、「0番目」という単語を 使うようになってるかもしれません。実際にこの言葉は使われているんですよ。 プログラマか数学者の誰かに聞いてごらんなさい。 END_PARAGRAPH end para do <<-END_PARAGRAPH プログラムでは最後に、#{code 'puts names[3]'}を試しています。 何が起こるのでしょう。エラーが起こるのを期待しますか? コンピュータに質問をしたとき、(コンピュータにとって) その質問が理解できない時には、エラーになります。 しかし、質問には意味があって、その答えが何もない という時もあります。項目3には何があるのでしょう? 何もありません。 #{code 'names[3]'}はなんでしょう。#{code 'nil'}です。 #{code 'nil'}とは、Rubyの言い方で言う「なんにもなし」です。 #{code 'nil'}は基本的には「他のどのオブジェクトでもない」という意味の特別の オブジェクトなのです。 END_PARAGRAPH end para do <<-END_PARAGRAPH この配列の項目に関する奇妙な番号付けの方法をマスターしたなら、 もう恐れることはありません。 さらに、いろいろな配列のメソッドを使えば、この奇妙な番号付けにさえでくわさないで すんでしまいます。こんな具合。 END_PARAGRAPH end h2 {"メソッド#{code 'each'}"} para do <<-END_PARAGRAPH #{code 'each'}を使うと、その配列が指し示しているそれぞれすべて(#{code 'each'})の オブジェクトに対して何か(なんでも好きなこと)をすることができます。 たとえば下のように、配列にいくつかの言語が入っているとして、そのそれぞれに対して なにか素敵なことを言いたい時にはこんなふうにします。 END_PARAGRAPH end prog do <<-END_CODE languages = ['English', '日本語', 'Ruby'] languages.each do |lang| puts '私は ' + lang + ' が好きだー!' puts 'あなたは?' end puts 'では、C++についても聞かせてください!' puts '...' END_CODE end para do <<-END_PARAGRAPH さて、何が起こったのでしょう。このやり方を使えば、配列の中の全部の要素を まったく番号を使わずに扱うことができます。これはナイスです。 上のプログラムを普通の言葉に翻訳するなら、「配列#{code 'languages'}の 中にあるオブジェクトそれぞれ(#{code 'each'})に対し、#{code 'lang'}という変数に 代入したうえで、#{code 'end'}までの間に書いてあるすべてのことを 実行(#{code 'do'})しなさい。」ということになります。 (知ってのとおり、C++というのはプログラミング言語の1つです。 この言語はRubyに比べると非常に覚えるのが難しくて、たいていの場合、C++のプログラムは 同じことをするRubyのプログラムの何倍もの長さになります。 (訳註:でも実行のスピードはずっと速いですが。)) END_PARAGRAPH end para do <<-END_PARAGRAPH あなたはこれを見て、「これはなんとなく前に習ったループに似てるなぁ」と、 思ったかもしれません。はい、確かに似ています。 重要な違いは#{code 'each'}がメソッドである、ということです。 #{code 'while'}と#{code 'end'}は、(そして、 #{code 'do'}, #{code 'if'}, #{code 'else'}や、その他 #{@@KEYWORD_COLOR[1]}色 で書いてあるすべての命令語も)メソッドではありません。 これらはRuby言語の基本的な文法の一部であって、たとえば #{code '='}とか、括弧とか、普通の言語で言ったら句読点のようなものと 同じです。 END_PARAGRAPH end para do <<-END_PARAGRAPH でも、#{code 'each'}は違います。#{code 'each'}は配列のメソッドのひとつなのです。 #{code 'each'}のようにループと「同じように振舞う」メソッド はしばしばイテレータ という名前で呼ばれます。 END_PARAGRAPH end para do <<-END_PARAGRAPH イテレータに関して気をつけておくべきことは、必ず #{code 'do'}...#{code 'end'}という形が後に続くということです。 #{code 'while'}や#{code 'if'}のそばには#{code 'do'}は用いられません。 #{code 'do'}は主にイテレータとともに使われます。 (訳註:#{code 'while'}の後に#{code 'do'}を用いることもできます。 この場合の#{code 'do'}は省略されることが多いので、 #{code 'do'}がイテレータと共に使われるというのはたいていの場合あっています。) END_PARAGRAPH end para do <<-END_PARAGRAPH さて、もうひとつのかわいいイテレータを紹介しましょう。ただし、これは 配列のメソッドではなく、整数のメソッドです。 END_PARAGRAPH end prog do <<-END_CODE 3.times do puts 'ヒップ ヒップ フレーー' # ばんざーい end END_CODE end h2 {'練習問題'} para do <<-END_PARAGRAPH • では、この章の最初で述べたプログラムを書いてみましょう。
ヒント: 配列を順番に並び替える(ソートする)には 素敵なメソッド #{code 'sort'}があります。 これを使いましょう。
(訳注:配列 #{code 'ary'} の最後に要素 #{code 'elem'} を追加するには、#{code 'ary << elem'} と 記述します。) END_PARAGRAPH end para do <<-END_PARAGRAPH • 上のプログラムを#{code 'sort'}メソッドなしで 書けますか。プログラミングの大部分は、問題解決にあります。 これはいい練習になります。 END_PARAGRAPH end h2 {'もっと配列メソッド'} para do <<-END_PARAGRAPH ここまでで、#{code 'each'}と#{code 'sort'}を覚えました。 しかし、配列にはその他にたくさんのメソッドが存在します。その数は文字列のメソッドと 同じくらいです。実際、そのいくつか(#{code 'length'}, #{code 'reverse'}, #{code '+'}, そして#{code '*'}など)は文字列と同じような動作をします。ただし、 文字列で文字に当たるものが配列では項目になりますが。 その他にも、配列特有のメソッドとして#{code 'last'}や#{code 'join'}などもあります。 さらに、#{code 'push'}や#{code 'pop'}のようなメソッドはそれを呼んだ配列のほうを 変化させます。 そして、文字列のメソッドのときと同じように、それについてどこを探せばいいかを 知っている限り(つまりこれから説明していく場所ですが)、すべてを覚える必要は 全くありません。 END_PARAGRAPH end para do <<-END_PARAGRAPH さて、では最初に#{code 'to_s'}と#{code 'join'}を見ていきましょう。 #{code 'join'}は#{code 'to_s'}とほとんど同じように働きます。 違いは、#{code 'join'}のほうが配列のそれぞれのオブジェクトの間に 文字列を挿入することです。 見てみましょう。 END_PARAGRAPH end prog do <<-END_CODE foods = ['アーティチョーク', 'ブリオッシュ', 'キャラメル'] puts foods puts puts foods.to_s puts puts foods.join(', ') puts puts foods.join(' :) ') + ' 8)' 200.times do puts [] end END_CODE end para do <<-END_PARAGRAPH 見てのとおり、#{code 'puts'}は配列を他のオブジェクトとは違ったふうに 扱っています。つまり、(訳註:ただ変換したりするだけではなくて)配列の中にある オブジェクトそれぞれに対して別々に#{code 'puts'}を呼び出しています。 これが、最後の例で、空の配列に対して#{code 'puts'}を200回呼び出していても 何も出力さてていない理由です。空の配列には何のオブジェクトも指し示されていない ので、#{code 'puts'}に対しては何もすることがありません。 (そして、何もしないのを200回繰り返してもやっぱり何もしません。) では、中に別の配列が入っている配列を#{code 'puts'}して見ましょう。 期待通り実行されますか。 END_PARAGRAPH end para do <<-END_PARAGRAPH いままで空の行を#{code 'puts'}しようとして、空文字列を 残していたのに気づいていましたか。#{code 'puts'} は #{code "puts ''"}と 同じことをします。 END_PARAGRAPH end para do <<-END_PARAGRAPH さて今度は、#{code 'push'}, #{code 'pop'}それから#{code 'last'}というメソッドについて 見てみましょう。#{code 'push'}と#{code 'pop'}は、#{code '+'}と#{code '-'}のように、 互いに反対の意味を持つペアのメソッドです。 #{code 'push'}は配列の最後尾にオブジェクトを追加します。反対に、#{code 'pop'}は 最後尾の要素となるオブジェクトを取り除きます。(そしてそれがなんだったかを返します。) #{code 'last'}は#{code 'pop'}に似ていて、最後尾の要素がなにかを告げますが、 その要素を取り除くことはしません。 繰り返しますが、#{code 'push'}と#{code 'pop'}は配列そのものを変化させてしまいます。 END_PARAGRAPH end prog do <<-END_CODE favorites = [] favorites.push 'バラの上の雨粒' favorites.push '子猫の上のウィスキー' puts favorites[0] puts favorites.last puts favorites.length puts favorites.pop puts favorites puts favorites.length END_CODE end h2 {'練習問題'} para do <<-END_PARAGRAPH • 以前、#{makeLink 'メソッド', :generateMethods}の章で書いた 目次を表示するプログラムを修正してみましょう。その際、プログラムの最初で 目次の情報(つまり、章の名前やページ番号など)をすべてひとつの配列にしまいます。 その後、その配列から情報を取り出して美しくフォーマットされた目次を出力します。 END_PARAGRAPH end para do <<-END_PARAGRAPH ここまでたくさんの数のメソッドを習ってきました。 さて次は、いよいよ#{makeLink '自分でメソッドを作る', :generateDefMethod}方法を覚えます。 END_PARAGRAPH end end # # WRITING METHODS # def generateDefMethod para do <<-END_PARAGRAPH 今まで見てきたように、ループやイテレータを使えば、同じことを (同じコードを)何度も何度も実行させることが出来ます。 しかし、時には同じ事を何度も行いたいけれどプログラムの中で違う場所から それを行いたいこともあるでしょう。 たとえば、心理学の学生のためにアンケートプログラムを書いてるとしましょう。 知り合いの心理学コースの学生たちからもらったアンケートの内容は 次のようなものでした。 END_PARAGRAPH end prog ['yes','yes','no way!','NO','yes','yes'] do <<-END_CODE puts 'こんにちは, 私の実験のためにお時間をとって' puts 'いただきありがとうございます. この実験は, ' puts '人々がメキシコ料理に対してどう感じているか' puts 'に関するものです. メキシコ料理について考え' puts 'すべての質問に対して, yes か no かで正直に' puts '答えてください. この実験はおねしょとは関係' puts 'ありません.' # いくつかの質問をしますが、答えは無視してしまいます。 goodAnswer = false while (not goodAnswer) puts 'タコスは好きですか?' # タコス《トルティーヤに肉・ answer = gets.chomp.downcase # チーズ・野菜などをくるんだ if (answer == 'yes' or answer == 'no') # メキシコ料理》 goodAnswer = true else puts '"yes" か "no" かでお答えください.' end end goodAnswer = false while (not goodAnswer) puts 'ブリートは好きですか?' # ブリート《肉・チーズなどを answer = gets.chomp.downcase # トルティーヤで包んで焼いた if (answer == 'yes' or answer == 'no') # メキシコの料理》 goodAnswer = true else puts '"yes" か "no" かでお答えください.' end end # しかし、「この」回答には注目します。 goodAnswer = false while (not goodAnswer) puts 'ベッドを濡らしますか?' answer = gets.chomp.downcase if (answer == 'yes' or answer == 'no') goodAnswer = true if answer == 'yes' wetsBed = true else wetsBed = false end else puts '"yes" か "no" かでお答えください.' end end goodAnswer = false while (not goodAnswer) puts 'チミチャンガは好きですか?' # チミチャンガ《スパイスを効かせた answer = gets.chomp.downcase # 肉などをトルティーヤで包んで if (answer == 'yes' or answer == 'no') # 揚げたメキシコ料理》 goodAnswer = true else puts '"yes" か "no" かでお答えください.' end end puts 'さらにいくつか質問を重ねます...' goodAnswer = false while (not goodAnswer) puts 'ソパイピーヤは好きですか?' # ソパイピーヤ《四角い形をした answer = gets.chomp.downcase # 練り粉の揚げ物;蜜をかけたり if (answer == 'yes' or answer == 'no') # してデザートにする》 goodAnswer = true else puts '"yes" か "no" かでお答えください.' end end # メキシコ料理に関して他にもたくさんの質問をします。 puts puts 'デブリーフィング' #(訳註: 実験後、被験者にその目的・理由を明かすこと) puts 'この実験へのご協力感謝します. 実は, この実' puts '験はメキシコ料理とは何の関係もありません. ' puts 'この実験は夜尿症(おねしょ)に関する実験だっ' puts 'たのです。メキシコ料理は、より正直に答えら' puts 'れるようあなたのガードをはずすために聞いた' puts 'に過ぎません。本当にありがとうございました.' puts puts wetsBed END_CODE end para do <<-END_PARAGRAPH これは繰り返しのたくさん入った非常に長いプログラムです。 (メキシコ料理に関する質問のところのコードはみな同じ物で、夜尿症 の質問が少し異なるだけです。) コードの繰り返しは悪いことです。たとえ、質問と質問の間に何か させたいことがあったりして、その繰り返しを大きなループや イテレータに組み込むことができなかったとしてもです。 こういった場合、メソッドを書くのがベストです。 まずは簡単な例でどうするのか示しましょう。 END_PARAGRAPH end prog do <<-END_CODE def sayMoo # (訳註)'moo'は、牛の鳴き声 puts 'モォーーー ' end END_CODE end para do <<-END_PARAGRAPH んー。このプログラムは#{code 'sayMoo'}を実行しません。 なぜでしょう。それは、実行するように告げなかったからです。 ここに書いてあるのは、どうやって #{code 'sayMoo'} するかであって、実際にしろ と命令しているわけでは ないのです。ではこれにもう少し追加します。 END_PARAGRAPH end prog do <<-END_CODE def sayMoo puts 'モォーーー ' end sayMoo sayMoo puts 'コイン-コイン' sayMoo sayMoo END_CODE end para do <<-END_PARAGRAPH これで、実行できましたね。(フランス語を話さない人のために説明 しておきますと、プログラムの中央で鳴いているのはフランスの アヒルです。フランスではアヒルは"コイン-コイン" と鳴きます。) END_PARAGRAPH end para do <<-END_PARAGRAPH こうして、#{code 'sayMoo'}というメソッドを 定義(#{code 'def'}ine) することができました。(メソッド名は、ほとんどの場合、 変数名と同じように小文字のアルファベットで始まります。 #{code '+'}や#{code '=='}と言ったわずかな例外もありますが。) しかし、メソッドは常にオブジェクトに付随していなければ ならないものだったはずです。実は、こうして定義した場合、 (#{code 'puts'}や#{code 'gets'}のときと同様に)、メソッドは プログラム全体というオブジェクトに付随するようになります。 次の章で、プログラム全体ではなく、特定のオブジェクトに付随する メソッドを定義する方法を覚えます。 でもその前にまずは覚えなければならないことがあります。 END_PARAGRAPH end h2 {'メソッド引数(ひきすう)'} para do <<-END_PARAGRAPH すでに気が付いていると思いますが、メソッドには(#{code 'gets'}, #{code 'to_s'}や#{code 'reverse'}など)、ひとつのオブジェクトに 対して単独で用いられるものと、(#{code '+'}や#{code '-'}、あるいは #{code 'puts'}などのように)、引数(ひきすう) (parameter) を取って、オブジェクトがそのメソッドをどのように実行するかを指示する 必要があるものがあります。 たとえば、#{code '5+'}とだけ言っても意味が通じませんね。 これは、#{code '5'}に対して加算するということを告げていますが、 何を 足すのかということは告げていません。 END_PARAGRAPH end para do <<-END_PARAGRAPH #{code 'sayMoo'}の定義に引数を付け加えるには(たとえば、モーと鳴く 回数を付け加えてみましょう。)、こんなふうにやります。 END_PARAGRAPH end prog do <<-END_CODE def sayMoo numberOfMoos puts 'モォーーー ' * numberOfMoos end sayMoo 3 puts 'オインク-オインク' sayMoo # この行は引数がないためエラーが出てしまいます。 END_CODE end para do <<-END_PARAGRAPH #{code 'numberOfMoos'}というのは、与えられた引数を指し示す変数です。 ちょっと紛らわしいので、もう一度言いましょう。 #{code 'numberOfMoos'}は、(メソッドを実行するときに)与えられた引数を (メソッドの中から)指し示すための変数です。 もし、#{code 'sayMoo 3'} というコードがあればその引数は#{code '3'}です。 そして、変数 #{code 'numberOfMoos'} は#{code '3'}を指し示します。 END_PARAGRAPH end para do <<-END_PARAGRAPH さて、こうしてこのプログラムではこのように引数が 必要 となりました。 では、もし、引数をつけなかったら#{code 'sayMoo'}は #{code "'モォーーー '"}に対して何を掛けるのでしょう。 コンピュータにはどうにもなりません。 END_PARAGRAPH end para do <<-END_PARAGRAPH もし、Rubyのオブジェクトが英語の名詞で、メソッドが動詞に相当するのであれば、 引数は副詞(たとえば、#{code 'sayMoo'}では、引数は、どのように #{code 'sayMoo'}するかを指示)か、または、 直接目的語(たとえば、#{code 'puts'}では、引数は何を #{code 'puts'} するのかを指示)に当たるとみなせます。 END_PARAGRAPH end h2 {'局所変数(local variable)'} para do <<-END_PARAGRAPH 以下のプログラムでは、2つの変数があります。 END_PARAGRAPH end prog do <<-END_CODE def doubleThis num numTimes2 = num * 2 puts num.to_s+' の2倍は '+numTimes2.to_s end doubleThis 44 END_CODE end para do <<-END_PARAGRAPH その2つの変数とは#{code 'num'}と#{code 'numTimes2'}です。 そのどちらも#{code 'doubleThis'}というメソッドの中にあります。 このような変数は(そして、ここまでで出てきた変数全部がそうなのですが) 局所変数 です。この意味は、これらの変数が メソッドの内部にのみ存在できて、そこから出ることができない、 ということです。実際やってみるとエラーが返ってきます。 END_PARAGRAPH end prog do <<-END_CODE def doubleThis num numTimes2 = num * 2 puts num.to_s+' の2倍は '+numTimes2.to_s end doubleThis 44 puts numTimes2.to_s END_CODE end para do <<-END_PARAGRAPH Undefined local variable... そんな局所変数は定義されていませんよ、ということですね。 #{code 'numTimes2'}という局所変数はメソッドの中で定義した はずなのですが、その変数を使おうとしている所が「局所」では なかった、ということのようです。 局所というのはメソッドの中のみという意味です。 END_PARAGRAPH end para do <<-END_PARAGRAPH このことは不便なように見えますが、さにあらず。 実際には非常にうまい機能なのです。 メソッドの中の変数が外からアクセスできないということは、 メソッドの外側であなたが定義した 変数も、メソッドの中から アクセスできない、つまり、消されたりすることがないということです。 END_PARAGRAPH end prog do <<-END_CODE def littlePest var var = nil puts 'ハハ! お前の変数は破壊したぜ!' end var = '君はこの変数に手を触れることはできない!' littlePest var puts var END_CODE end para do <<-END_PARAGRAPH この小さなプログラムの中には実際には#{code 'var'}という名前の 2つの 変数があります。ひとつは#{code 'littlePest'}の内側、 もうひとつは外側です。で、#{code 'littlePest var'}というふうに メソッドを呼ぶと、文字列がひとつの#{code 'var'}からもうひとつの #{code 'var'}へと渡されます。その結果、どちらもが同じ文字列を指し示す ようになります。そのあと、#{code 'littlePest'}がそれ自身の局所 変数である#{code 'var'}の指し示す先を#{code 'nil'}に替えます。 しかし、このときメソッドの外にある#{code 'var'}は何もいじられることはありません。 END_PARAGRAPH end h2 {'返り値'} para do <<-END_PARAGRAPH メソッドの中には、呼ばれたとき何か値を返すものがあることに 気が付いていると思います。たとえば、#{code 'gets'}は (キーボードから入力された)文字列を返し ますし、 #{code '5+3'}(これは、実は#{code '5.+(3)'}のことなんですが) の中の#{code '+'}というメソッドは#{code '8'}を返します。 数に対する算術メソッドは数を返しますし、文字列に対する 算術メソッドは文字列を返します。 END_PARAGRAPH end para do <<-END_PARAGRAPH メソッドが呼ばれたところへ値を返すのと、プログラムが画面に対して 出力するの(#{code 'puts'}の動作)との違いを理解しておくことは重要です。 #{code '5+3'}は#{code '8'}を返しますが、#{output '8'}を出力する わけではありません。 END_PARAGRAPH end para do <<-END_PARAGRAPH それでは、#{code 'puts'}は何を返し ているのでしょう? 今までは気にしてきませんでしたが、ここで見てみることにしましょう。 END_PARAGRAPH end prog do <<-END_CODE returnVal = puts 'この puts が返しているのは:' puts returnVal END_CODE end para do <<-END_PARAGRAPH この結果、最初の#{code 'puts'}は#{code 'nil'}を返していることがわかります。 ここではチェックしていませんが、2つ目の#{code 'puts'}も同様です。 つまり、#{code 'puts'}は常に#{code 'nil'}を返します。 すべてのメソッドは、何かを返さなければなりません。たとえそれが#{code 'nil'}で あってもです。 END_PARAGRAPH end para do <<-END_PARAGRAPH ではここでちょっと読むのを中断して、 先ほどのプログラムで#{code 'sayMoo'}が何を返しているのか 調べるプログラムを書いてみてください。 END_PARAGRAPH end para do <<-END_PARAGRAPH びっくりしましたか? どうなったのか説明しましょう。 メソッドからの返り値は単純にメソッドの最後の行の値です。 #{code 'sayMoo'}の場合、これから行くと #{code "puts 'モォーーー '*numberOfMoos"}が返ることになり、すなわち #{code 'nil'}が返ります。なぜなら#{code 'puts'}はいつも#{code 'nil'}を 返すからです。 もし、このメソッドが#{code "'黄色い潜水艦'"}という文字列を返すように したければ、それを メソッドの終わりに追加するだけでよいわけです。 つまり、 END_PARAGRAPH end prog do <<-END_CODE def sayMoo numberOfMoos puts 'モォーーー '*numberOfMoos '黄色い潜水艦' end x = sayMoo 2 puts x END_CODE end para do <<-END_PARAGRAPH さて、それではようやく最初の心理学の実験に戻ってみましょう。 今度は質問の繰り返しのために、自分で定義するメソッドを使います。 質問は引数を取って、#{input 'yes'}と答えたときには#{code 'true'}を #{input 'no'}と答えたときには#{code 'false'}と答えるようにしましょう。 (たとえ、大部分の答えを無視する場合でも、答えを返すというのは良いアイデアです。 このやり方は夜尿症の質問でも使えるわけですから。) それと、今回は挨拶とデブリーフィングのところは省略します。 これで少しは読みやすくなるでしょう。 END_PARAGRAPH end prog ['yes','yes','no way!','NO','yes','yes','yes','yes','yes'] do <<-END_CODE def ask question goodAnswer = false while (not goodAnswer) puts question reply = gets.chomp.downcase if (reply == 'yes' or reply == 'no') goodAnswer = true if reply == 'yes' answer = true else answer = false end else puts '"yes" か "no" かでお答えください.' end end answer # これが返す値(true または false)です。 end puts 'こんにちは, 私の実験のために....' puts ask 'タコスは好きですか?' # この返り値は無視します。 ask 'ブリートは好きですか?' wetsBed = ask 'ベッドを濡らしますか?' # この返り値は保存します。 ask 'チミチャンガは好きですか?' ask 'ソパイピーヤは好きですか?' ask 'タマーレは好きですか?' puts 'さらにいくつか質問を重ねます...' ask 'オルチャタを飲むのは好きですか?' ask 'フラウタスは好きですか?' puts puts 'デブリーフィング:' puts 'ご協力感謝します...' puts puts wetsBed END_CODE end para do <<-END_PARAGRAPH どうです、悪くないですねぇ。(訳註:同じコードの繰り返しがだいぶ減りました。) 質問を追加するのも簡単 です。追加したとしても、プログラムは小さい ままです。これは大きな進歩— 怠け者プログラマにとっては夢のようです。 END_PARAGRAPH end h2 {'大きな例をもうひとつ'} para do <<-END_PARAGRAPH ここで、もうひとつのメソッドの例を出しましょう。 #{code 'englishNumber'}です。 これは、たとえば#{code '22'}のようなひとつの数をもらって、 その英語読みの文字列(この場合#{code "'twenty-two'"}のような)を返します。 とりあえず、#{code '0'}から#{code '100'}までの整数についてだけ 働くようにしましょう。 END_PARAGRAPH end para do <<-END_PARAGRAPH (注意: このメソッドは、 さっさとメソッドから返る #{code 'return'} というキーワードと、新しい種類の分岐法 #{code 'elsif'} を使っています。これらがどんなふうに働くのかはプログラムの文脈から 明らかでしょう。) END_PARAGRAPH end prog do <<-END_CODE def englishNumber number # 0から100だけの数を受け取るようにします。 if number < 0 return 'ゼロ以上の数を入力してください.' end if number > 100 return '100以下の数を入力してください.' end numString = '' # これが最終的に返す文字列です。 # 上のほうの桁から表示させていって、"left" には何の数がまだ残っているかを入れます。 # "write" にはこれからすぐに出力させるための一桁分の数を入れます。 # write と left... いいですか? :) left = number write = left/100 # 百の桁をまず書き出しましょう。 left = left - write*100 # leftからは百の桁を取り除きます。 if write > 0 return 'one hundred' end write = left/10 # 今度は十の桁です。 left = left - write*10 # そして十の桁を引きます。 if write > 0 if write == 1 # 参りますねぇ。 # 英語では12は"twelve"であって、"tenty-two"と言うわけには行かないので、 # 十の桁が1のときは特別扱いしなければなりません。 if left == 0 numString = numString + 'ten' elsif left == 1 numString = numString + 'eleven' elsif left == 2 numString = numString + 'twelve' elsif left == 3 numString = numString + 'thirteen' elsif left == 4 numString = numString + 'fourteen' elsif left == 5 numString = numString + 'fifteen' elsif left == 6 numString = numString + 'sixteen' elsif left == 7 numString = numString + 'seventeen' elsif left == 8 numString = numString + 'eighteen' elsif left == 9 numString = numString + 'nineteen' end # writeが1の場合、一の桁に関してはもう処理してしまっていますので、 # もう出力する必要はありません。 left = 0 elsif write == 2 numString = numString + 'twenty' elsif write == 3 numString = numString + 'thirty' elsif write == 4 numString = numString + 'forty' elsif write == 5 numString = numString + 'fifty' elsif write == 6 numString = numString + 'sixty' elsif write == 7 numString = numString + 'seventy' elsif write == 8 numString = numString + 'eighty' elsif write == 9 numString = numString + 'ninety' end if left > 0 numString = numString + '-' end end write = left # で、一の桁を書き出します。 left = 0 # leftから一の桁を引きます。(0になりますね。) if write > 0 if write == 1 numString = numString + 'one' elsif write == 2 numString = numString + 'two' elsif write == 3 numString = numString + 'three' elsif write == 4 numString = numString + 'four' elsif write == 5 numString = numString + 'five' elsif write == 6 numString = numString + 'six' elsif write == 7 numString = numString + 'seven' elsif write == 8 numString = numString + 'eight' elsif write == 9 numString = numString + 'nine' end end if numString == '' # この時点で"numString"が空文字列ということは、 # もともとの数 "number" は0です。 return 'zero' end # ここまできたら、0から100までの数の英語バージョンがどこかに # しまわれています。そう、"numString"を返す必要があります。 numString end puts englishNumber( 0) puts englishNumber( 9) puts englishNumber( 10) puts englishNumber( 11) puts englishNumber( 17) puts englishNumber( 32) puts englishNumber( 88) puts englishNumber( 99) puts englishNumber(100) END_CODE end para do <<-END_PARAGRAPH このプログラムに関しては、私が気に入らない点が3つほどあります。 第1に、繰り返しが多すぎです。第2に、100以上の数は扱えません。 第3に、特別扱いが沢山ありすぎます。つまり#{code 'return'}が多すぎなのです。 ここで、配列を使ってもう少しきれいに仕上げてみましょう。 END_PARAGRAPH end prog do <<-END_CODE def englishNumber number if number < 0 # 負の数は不可です。 return '負でない数を入力してください.' end if number == 0 return 'zero' end # もう、上限として特別の場合は設けません。余分なreturnもなし。 numString = '' # これが最終的に返す文字列です。 onesPlace = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine'] tensPlace = ['ten', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety'] teenagers = ['eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen'] # 上のほうの桁から表示させていって、"left" には # まだ残っている数を入れます。 # "write" にはこれからすぐに出力させるための # 一桁分の数を入れます。 # write と left... 良いですか? :) left = number write = left/100 # 百の桁以上の桁をwriteに入れます。 left = left - write*100 # 下2桁をleftに残します。 if write > 0 # さて、ここでちょっとしたトリックを使います。 hundreds = englishNumber write numString = numString + hundreds + ' hundred' # これは「再帰法」と呼ばれる技です。さて何をやって # いるのでしょう? 上の行ではメソッドの中で自分自身 # (訳註:つまり"englishNumber")を呼ぶように指示して # います。ただし、引数として"number"の代わりに # "write"を指定しています。 # ここで、"write"が(この時点では)、最終的に英語で # 出力すべき数の百の位以上の数であるということを # 思い出してください。 # 2行目では、"hundreds"を"numString"の後に追加して、 # さらに、' hundred'という文字列を追加しています。 # 具体的に言うと、たとえば、最初に引数を1999とする # と(つまり、"number" = 1999)、englishNumberを呼ぶ # ところでは、"write"に19が入っており、"left"には99が # 入っています。ここで最も怠惰な考えとしては、 # englishNumberがまず'nineteen'を出力して、その後に # ' hundred'を出力し、最後に残りの'ninety-nine'を # englishNumberが出力してくれれば良いわけです。 if left > 0 # この時、'two hundredfifty-one'のように # ならないためにスペースを入れます。 numString = numString + ' ' end end write = left/10 # 今度は十の桁です。 left = left - write*10 # そして十の桁をひきます。 if write > 0 if ((write == 1) and (left > 0)) # 英語では12は"twelve"であって、"tenty-two"と # 言うわけには行きません。そこで、十の桁が1の # ときは特別扱いしなければなりません。 numString = numString + teenagers[left-1] # この"-1" は、teenagers[3] が 'fourteen'で # あって、'thirteen'ではないため必要です。 # writeが1の場合、一の桁に関してはもう処理して # しまっていますので、もう出力する必要は # ありません。 left = 0 else numString = numString + tensPlace[write-1] # この"-1" も、tensPlace[3] が'forty'で'thirty'ではない # ため必要。 end if left > 0 # 'sixtyfour'とは書かずに、途中にハイフンを入れます。 numString = numString + '-' end end write = left # ここで、一の桁を書き出します。 left = 0 # この桁を引きます。(0になります。) if write > 0 numString = numString + onesPlace[write-1] # この"-1"はonesPlace[3]が'four'であって # 'three'ではないから必要。 end # 最後に"numString"を返します。 numString end puts englishNumber( 0) puts englishNumber( 9) puts englishNumber( 10) puts englishNumber( 11) puts englishNumber( 17) puts englishNumber( 32) puts englishNumber( 88) puts englishNumber( 99) puts englishNumber(100) puts englishNumber(101) puts englishNumber(234) puts englishNumber(3211) puts englishNumber(999999) puts englishNumber(1000000000000) END_CODE end para do <<-END_PARAGRAPH いやー 、とても良くなりました。プログラムはかなり密度が高いので 多くのコメントを追加してあります。このプログラムは、大きな数に対しても 望み通りとはいかないまでも、とりあえず動きます。例えば最後の例の数では、 #{code "'one trillion'"}(訳註:1兆)と出れば理想的で、そうででなくても せめて#{code "'one million million'"}と出て欲しいです。(3つの例とも 間違えとはいえないのですけれど。) 実際、やろうと思えばすぐにでも 出来ますよね。 END_PARAGRAPH end h2 {'練習問題'} para do <<-END_PARAGRAPH • 上の#{code 'englishNumber'}を拡張してみましょう。 最初にthousand(千の桁)を導入してください。上のプログラムでは #{code "'ten hundred'"}となっているところを#{code "'one thousand'"}を返すように、 あるいは、#{code "'one hundred hundred'"}の代わりに#{code "'ten thousand'"}を 返すようにします。 END_PARAGRAPH end para do <<-END_PARAGRAPH • #{code 'englishNumber'}にさらに改良を加えましょう。 milion(百万)を加えます。その結果、#{code "'one thousand thousand'"}の代わりに #{code "'one million'"}が得られるようにします。 その後、billion(十億)とかtrillion(兆)とかを追加していってみましょう。 どこまでいけるでしょうか? END_PARAGRAPH end para do <<-END_PARAGRAPH • #{code 'weddingNumber'}はどうでしょう? このプログラムは#{code 'englishNumber'} とほとんど同じように動作しますが、"and" という接続詞をやたらめったら挿入します。 たとえば #{code "'nineteen hundred and seventy and two'"} という具合に、結婚式の 招待状のような感じで。もう少し例を挙げたいと思いましたが、私自身詳しくありません。 結婚のコーディネータに訊いてみるのも良いでしょう。 END_PARAGRAPH end para do <<-END_PARAGRAPH • "99本のビールが壁に..." #{code 'englishNumber'}と以前作ったプログラムを使って、今度は正しい 方法で この歌の詩を出力させなさい。 その後は、9999から初めてコンピュータをこらしめましょう。 (ただ、あまり大きな数字を使うと、コンピュータといえども全部出力するのにかなりの時間を 必要とします。10万本のボトルには、かなりの時間がかかります。もし、100万本のボトルなどと するとあなた自身がひどい目に会いますよ。) END_PARAGRAPH end para do <<-END_PARAGRAPH おめでとう! この時点であなたは本物のプログラマと言えます! あなたは大きなプログラムを1から作成するのに必要なすべての知識を学んできました。 もし、自分のために書きたいプログラムのアイデアがあるとしたら、やってみましょう。 END_PARAGRAPH end para do <<-END_PARAGRAPH もちろん、すべてを1から作るというのは時間のかかる作業です。 果たして、誰かがすでに書いたプログラムをもう一度書くのに時間をかける 意味があるでしょうか? あなたの書いたプログラムがe-mailを送ったり、コンピュータ上のファイルを保存したり 読み出したりできたら良いと思いませんか? あるいは、サンプルコードがリロードするたびに実際に実行されるようなチュートリアルの Webページを作ったりはどうでしょう :)。 こういったプログラムを、より良く早く書くために、Rubyには多くの異なる #{makeLink 'オブジェクトの種類', :generateClasses}があります。 END_PARAGRAPH end end # # CLASSES # def generateClasses para do <<-END_PARAGRAPH ここまで、私たちは、数々のオブジェクトを異なる種類、あるいは別の言い方を すればクラス ごとに分けて見てきました。 たとえば、文字列、整数、浮動小数点数、配列、そして後で説明する 特別なオブジェクト(#{code 'true'}, #{code 'false'}, そして #{code 'nil'})などです。 Rubyでは、これらのクラスはいつも先頭文字を大文字で書かれます。つまり、 #{code 'String'}(文字列), #{code 'Integer'}(整数), #{code 'Float'}(浮動小数点数), #{code 'Array'}(配列)... などなどです。 一般的にはある特定のクラスの新しいオブジェクトを作りたいときには、#{code 'new'} を使います。 END_PARAGRAPH end prog do <<-END_CODE a = Array.new + [12345] # Array の足し算。 b = String.new + 'hello' # String の足し算。 c = Time.new puts 'a = '+a.to_s puts 'b = '+b.to_s puts 'c = '+c.to_s END_CODE end para do <<-END_PARAGRAPH 配列や文字列は、それぞれ#{code '[...]'}とか#{code "'...'"}のように 作ることができるので、#{code 'new'}を使って作ることはほとんどありません。 (上の例からはあまりはっきりしないかもしれませんが、#{code 'String.new'}は 空の文字列を、#{code 'Array.new'}は空の配列を作ります。) 数字もまた例外です。整数は#{code 'Integer.new'}では作ることができません。 整数が欲しければそのまま整数を書かなければなりません。 END_PARAGRAPH end h2 {"#{code 'Time'}クラス"} para do <<-END_PARAGRAPH それでは、#{code 'Time'}クラスに関してはどうでしょう? #{code 'Time'}オブジェクトはある瞬間の時刻を表します。 ある時刻に数を足したり引いたりして新しい時刻を作ることが出来ます。 ある時刻に#{code '1.5'}を加えると、1.5秒後の新しい時刻が作られます。 END_PARAGRAPH end prog do <<-END_CODE time = Time.new # このWebページをゲットした瞬間。 time2 = time + 60 # 1分後。 puts time puts time2 END_CODE end para do <<-END_PARAGRAPH また、ある特定の瞬間の時刻を#{code 'Time.mktime'}を使って 作ることも出来ます。 END_PARAGRAPH end prog do <<-END_CODE puts Time.mktime(2000, 1, 1) # Y2K. puts Time.mktime(1976, 8, 3, 10, 11) # 私の生まれたとき。 END_CODE end para do <<-END_PARAGRAPH 注意:この誕生時刻は、太平洋夏時間(Pacific Daylight Time; PDT)で 表しています。Y2Kが問題になったのは、少なくとも西海岸の人にとっては、 太平洋標準時(Pacific Standard Time; PST)です。 (訳註;日本国内では時刻は日本標準時(JST)で表され、グリニッジ標準時(GMT) あるいは協定世界時(UT)より9時間進んでいます。そのため、GMT+9:00と表現されることも あります。PDTはGMTより7時間、PSTは8時間遅れています。 #{code 'mktime'}はコンピュータに設定されたタイムゾーンにあわせて時刻を設定するので、 実行するコンピュータによりこの結果は異なります。) カッコは#{code 'mktime'}の引数を一つに束ねる働きをしています。 引数を加えていくほど、時刻は正確になります。 END_PARAGRAPH end para do <<-END_PARAGRAPH 時刻を比較メソッドを使って比較することも出来ます。 (早い時刻は遅い時刻より小さく なります。) そして、ある時刻を別の時刻から引き算をするとその2つの時刻の間の 秒数を得ることが出来ます。これらを使っていろいろ遊んでみましょう。 END_PARAGRAPH end h2 {'練習問題'} para do <<-END_PARAGRAPH • 10億(One billion)秒... (もし記録が残っているなら)あなたの生まれた 正確な時刻を見つけ、いつ10億秒歳になる(あるいはなった)のかを 計算してみなさい。そしてカレンダーに印をつけましょう。 END_PARAGRAPH end para do <<-END_PARAGRAPH • ハッピーバースデー!  生まれた年、月、そして日を順に訊いて そこから年齢を計算します。そして、過ごしてきた誕生日それぞれに対して 大きな#{output 'おめでとう!'}をプレゼントしましょう。 END_PARAGRAPH end h2 {"#{code 'Hash'}クラス"} para do <<-END_PARAGRAPH もう1つ、便利なクラス#{code 'Hash'}があります。 ハッシュは配列に非常に似ていて、いろいろな種類のオブジェクトを 指し示すことの出来る項目のようなものをまとめています。ただし、 配列ではそれらの項目は1列に並んでおり、それらの1つ1つには (0から)数字が振られているのに対して、ハッシュではその項目が 並んではおらず(ある意味ごちゃ混ぜになっていて)、それぞれの 項目を参照するのに、数ではなくどんな オブジェクトも 使うことが出来ます。まとめて処理したいひとまとまりのデータがあって、 それが序列をつけたリストとしてはそぐわない時などが、ハッシュ の出番です。例えば、このチュートリアルを作るときのコードの異なる部分 に対して使う色などです。 END_PARAGRAPH end prog do <<-END_CODE colorArray = [] # Array.new と同じ colorHash = {} # Hash.new と同じ colorArray[0] = '#{@@STRING_COLOR[1]}' colorArray[1] = '#{@@NUMBER_COLOR[1]}' colorArray[2] = '#{@@KEYWORD_COLOR[1]}' colorHash['strings'] = '#{@@STRING_COLOR[1]}' colorHash['numbers'] = '#{@@NUMBER_COLOR[1]}' colorHash['keywords'] = '#{@@KEYWORD_COLOR[1]}' colorArray.each do |color| puts color end colorHash.each do |codeType, color| puts codeType + ': ' + color end END_CODE end para do <<-END_PARAGRAPH 配列を使った場合は、#{code '0'}の項目が文字列を表し、 #{code '1'}が数字を...というように覚えていなければなりません。 でも、もしハッシュを使えば簡単になります。項目の名前を#{code "'string'"}にして、 もちろんここに文字列の色を入れるのです。面倒な対応などは覚える必要がありません。 ここで注意しておいたほうが良いのは、#{code 'each'}を使った時、 ハッシュに入っているオブジェクトがハッシュに入れた時と同じ順序で 取り出されるとは限らないことです。 (少なくとも、私がこれを書いたときは違いました。次は順番どおり出てくるかもしれません。 どちらにしても、ハッシュの順序に関してはわからないのです。) 配列はものの順序を保持しますが、ハッシュはしません。 END_PARAGRAPH end para do <<-END_PARAGRAPH ハッシュの項目の名前としては文字列が使われることが普通ですが、やろうと思えば どんな種類のオブジェクトでも使うことができます。(それが必要になるとは あまり思えませんが)、配列や他のハッシュだってOKです。 END_PARAGRAPH end prog false do <<-END_CODE weirdHash = Hash.new weirdHash[12] = 'モンキーズ' weirdHash[[]] = 'からっぽ' weirdHash[Time.new] = 'するなら今だ' END_CODE end para do <<-END_PARAGRAPH ハッシュと配列はそれぞれ適材適所があります。ある特定の問題に 対し、どちらが最適かを決めるのはあなたの責任です。 END_PARAGRAPH end h2 {'クラスの拡張'} para do <<-END_PARAGRAPH 前の章の最後で、与えられた整数を英語のスペリングに変換するメソッドを 書きました。しかし、それは整数が持つメソッドではなく、単に 一般的な「プログラム」が持っているメソッドでした。たとえば #{code 'englishNumber 22'}と書く代わりに、#{code '22.to_eng'}のように 書くことができたらナイスだと思いませんか? こうすればそれができます。 END_PARAGRAPH end # HACK ALERT!!! (I can't get to the global namespace transparently # from inside the StringIO object in a mod_ruby script.) integerClassHack = <<-END_CODE def to_eng if self == 5 english = 'five' else english = 'fifty-eight' end english end END_CODE Integer.module_eval integerClassHack # This is the real method definition. # The following defines a method in "another" integer class: # END HACK ALERT!!! prog do <<-END_CODE class Integer #{integerClassHack} end # 2つの数字についてチェックしましょう。 puts 5.to_eng puts 58.to_eng END_CODE end para do <<-END_PARAGRAPH はい、テスト完了。動いているようです。 :) END_PARAGRAPH end para do <<-END_PARAGRAPH さて、これで整数メソッドを定義したことになります。 #{code 'Integer'}クラスに飛び込み、そこでメソッドを定義し、 そしてそこから飛び出してきました。 その結果、すべての整数はこの(ある意味不完全な)メソッドを持つように なります。実際、もし#{code 'to_s'}のようなビルトインメソッド (最初から定義されているメソッド)の振る舞いが気に入らないとしたら、 このやり方を使って再定義ができてしまいます...しかしお勧めはしませんが。 何か新しいことをしたくなったときは、古いメソッドをそのままにして 新しい物を作るほうが良いようです。 END_PARAGRAPH end para do <<-END_PARAGRAPH さて、まだ混乱していますか? このプログラムをもう少し見ていきましょう。 ここまで、どんなコードを実行するにしてもメソッドを作るにしても、それは デフォルトの「プログラム」オブジェクトの中で行ってきました。 でも、今のプログラムでは、最初にそのオブジェクトから離れて、 #{code 'Integer'}というクラスの中に入っています。 そして、そこでメソッドを定義し(これによって整数メソッドができます。)、 すべての整数がそれを使うことができるようになりました。 メソッドの内側では、そのメソッドを使っているオブジェクト(つまり整数)を 参照するのに#{code 'self'}を使います。 END_PARAGRAPH end h2 {'クラスを作る'} para do <<-END_PARAGRAPH 今までにたくさんのオブジェクトの種類(クラス)を見てきました。 しかし、Rubyが持っていないオブジェクトの種類が欲しくなることは良く あります。 ラッキーなことに、新しいクラスを作るのは古いクラスを拡張するのと 同じくらい簡単です。たとえば、Rubyの中にさいころが 欲しくなったとしましょう。これがDieクラスの作り方です。 END_PARAGRAPH end prog do <<-END_CODE class Die def roll 1 + rand(6) end end # 2つのさいころを作ってみましょう。(訳註:die(さいころ)の複数がdice) dice = [Die.new, Die.new] # そして、転がします。 dice.each do |die| puts die.roll end END_CODE end para do <<-END_PARAGRAPH (もし、乱数の節を飛ばしていたとしたら、#{code 'rand(6)'}というのは、 #{code '0'}から#{code '5'}までの乱数を与えるというふうに理解してください。) END_PARAGRAPH end para do <<-END_PARAGRAPH はい、私たちのオブジェクトが出来ました。 さいころを(リロードボタンを使って)何度か転がしてみて、何が出るか みてみましょう。 END_PARAGRAPH end para do <<-END_PARAGRAPH これで、オブジェクトに対してどんな種類のメソッドも定義できるようになりました...。 しかし何かまだ足りないようです。 こんなふうにオブジェクトを作ったりしていると、何か変数を覚える前にしていた プログラミングのような感覚になりませんか? たとえば、今作ったさいころのプログラムを見てみましょう。 さいころを振ることはできます。そして、毎回違った数字が現れています。 でも、その数字をとどめておきたいとしたら、その数字を指し示す変数が欲しくなります。 まともなさいころなら、数をひとつ保持 することができて、 さいころを振ったときに、その数が変化するようにすべきだと思います。 もし、さいころ自体を変数に入れているなら、出ている目を別の変数にいれる というのは良くないでしょう。 END_PARAGRAPH end para do <<-END_PARAGRAPH でも、もし出た目を#{code 'roll'}の中で(局所)変数に保存しようとすると、 #{code 'roll'}が終わるやいなや、その値はどこかに行ってしまいます。 この数は別の種類の変数に保存する必要があるようです。 END_PARAGRAPH end h2 {'インスタンス変数'} para do <<-END_PARAGRAPH 普通、ある文字列について話をしたい時には、それを文字列 と呼びます。 でも、文字列オブジェクト と呼ぶこともできます。 時々、プログラマはこれを#{code 'String'}(文字列)クラスの インスタンス と呼んだりします。でもこれは、文字列 の やや気取った(そして、ちょっと長たらしい)言い方に過ぎません。 要するに、クラスのインスタンス とは、クラスのオブジェクトのことです。 END_PARAGRAPH end para do <<-END_PARAGRAPH さてそれで、インスタンス変数とはオブジェクトの変数のことです。 メソッドの局所変数はメソッドが終わるまでの命です。 一方、オブジェクトのインスタンス変数は、オブジェクトが生き残っている限り 生き残ります。インスタンス変数を普通の局所変数と区別するために、 その名前の先頭には#{code '@'}をつけます。 END_PARAGRAPH end prog do <<-END_CODE class Die def roll @numberShowing = 1 + rand(6) end def showing @numberShowing end end die = Die.new die.roll puts die.showing puts die.showing die.roll puts die.showing puts die.showing END_CODE end para do <<-END_PARAGRAPH いいですねぇ。これで、#{code 'roll'}はさいころを振って#{code 'showing'}は 何の目が出ているかを知らせるようになりました。 でも、もし、さいころを振る前(つまり、#{code '@numberShowing'}の変数を セットする前)に何の目が出るかを見ようとしたら、いったいどうなるでしょう。 END_PARAGRAPH end prog do <<-END_CODE class Die def roll @numberShowing = 1 + rand(6) end def showing @numberShowing end end # このさいころをもう一度使うつもりはないので、 # 変数に保存しておく必要はありません。 puts Die.new.showing END_CODE end para do <<-END_PARAGRAPH ふむふむ。少なくともエラーにはならないようです。でもこの、 #{output 'nil'}という出力の意味を推し量ったとしても、 「振られていない」さいころに出た目というのにはやはり違和感があります。 これを避けるには、さいころオブジェクトが作られた瞬間に出る目を 決めてしまえば良いわけです。これをするのが#{code 'initialize'}です。 END_PARAGRAPH end prog do <<-END_CODE class Die def initialize # ここでは、さいころを振るだけにします。 # 最初から6にセットしておくとか、 # 他にやりたい放題できることは確かですけど。 roll end def roll @numberShowing = 1 + rand(6) end def showing @numberShowing end end puts Die.new.showing END_CODE end para do <<-END_PARAGRAPH オブジェクトが生成されるときには、(それが定義されてれば) 必ず#{code 'initialize'}メソッドが実行されます。 END_PARAGRAPH end para do <<-END_PARAGRAPH 私たちのさいころはほとんど完璧です。あと、出る目をセットする方法が あったらいいなと思いませんか? さぁ、この#{code 'cheat'}メソッドを 書いてみましょう。プログラムを書いたら(そして、もちろん書いた メソッドがきちんと動くかテストしたら)この文章に戻ってきましょう。 #{code '7'}の目をセットできてしまうなんてことがないように 確認してくださいね。 END_PARAGRAPH end para do <<-END_PARAGRAPH ちょっとトリッキーでしたが、これでかなりクールなことができるように なりましたね。 さて、それでは、また別のもっと楽しい例をあげてみましょう。 たとえば、簡単な仮想ペットを作ってみるのはどうでしょう。 赤ちゃんドラゴンです。 たいがいの赤ちゃんと同じく、そのドラゴンは、食べ、寝、うんちをします。 ということは、食べさせたり、寝かしつけたり、散歩させたり、もできるように しないといけません。内部では、ドラゴンはおなかがすいているのか、 疲れているのか、出かけたいのかなどを記憶している必要があるのですが、 赤ちゃんドラゴンにそれを聞いてみてもそれはわかりません。 人間の赤ちゃんに「おなかすいた?」と訊いてもしかたがないのと同じです。 その代わり、赤ちゃんドラゴンと会話するための別の方法を2,3提供しましょう。 そして、彼が生まれる時には名前を付けられるようにします。 (#{code 'new'}メソッドに何を渡しても、それは#{code 'initialize'}メソッドに引き継がれます。) OK, では、はじめます。 END_PARAGRAPH end prog do <<-END_CODE class Dragon def initialize name @name = name @asleep = false @stuffInBelly = 10 # 最初彼はおなかいっぱいです。 @stuffInIntestine = 0 # トイレはまだ大丈夫。(Bellyは胃、Intestineは腸です。) puts @name + ' は生まれました.' end def feed puts 'あなたは ' + @name + 'に食べ物を与えます.' @stuffInBelly = 10 passageOfTime end def walk puts 'あなたは ' + @name + 'をトイレに連れて行きます.' @stuffInIntestine = 0 passageOfTime end def putToBed puts 'あなたは ' + @name + ' を寝かしつけます.' @asleep = true 3.times do if @asleep passageOfTime end if @asleep puts @name + ' はいびきをかいて, 部屋中煙だらけ.' end end if @asleep @asleep = false puts @name + ' はゆっくり目を覚ます.' end end def toss puts 'あなたは ' + @name + ' を空中に投げ上げます.' puts '彼はキャハキャハ笑い, あなたの眉毛は焼け焦げた.' passageOfTime end def rock puts 'あなたは ' + @name + ' を優しく揺すります.' @asleep = true puts '彼は短くうとうと...' passageOfTime if @asleep @asleep = false puts '...でもやめるとすぐ起きちゃう.' end end private # "private"というのは、ここで定義されているメソッドが # オブジェクト内部のものであるという意味です。 # (あなたはドラゴンにえさを与えることはできますが、 # おなかがすいているかどうかを訊くことはできません。) def hungry? # メソッドの名前は"?"で終わることができます。 # 通常、メソッドがtrueかfalseのどちらかを返すときだけ、 # この名前を使います。このように: @stuffInBelly <= 2 end def poopy? #うんちが出そう? @stuffInIntestine >= 8 end def passageOfTime if @stuffInBelly > 0 # 食べたものは、胃から腸へ移動 @stuffInBelly = @stuffInBelly - 1 @stuffInIntestine = @stuffInIntestine + 1 else # 私たちのドラゴンは飢えました! if @asleep @asleep = false puts '彼は突然跳び起きます!' end puts @name + ' は飢え死にしそう! 死に物狂いの彼は"あなた"を食べてしまいました!' exit # プログラムを終了します。 end if @stuffInIntestine >= 10 @stuffInIntestine = 0 puts 'おっと! ' + @name + ' はやっちゃったようです...' end if hungry? if @asleep @asleep = false puts '彼は突然起きます!' end puts @name + 'のおなかはゴロゴロ言ってます...' end if poopy? if @asleep @asleep = false puts '彼は突然起きます!' end puts @name + ' はうんちをしたくて踊っています...' end end end pet = Dragon.new 'Norbert' pet.feed pet.toss pet.walk pet.putToBed pet.rock pet.putToBed pet.putToBed pet.putToBed pet.putToBed END_CODE end para do <<-END_PARAGRAPH ふー! もちろん、会話的にプログラムが動けばなお良いのですが、 その部分は後であなたが作ることができるでしょう。 ここでは、新しいドラゴンクラスを作るのに直接関係する部分を示してみました。 END_PARAGRAPH end para do <<-END_PARAGRAPH この例では、2つの新しいものが出てきています。 1つ目は#{code 'exit'}です。 これは非常に単純で、プログラムをその場で終了させるメソッドです。 2つ目は#{code 'private'}という言葉です。 これはクラス定義の真ん中に挿入されています。 プログラムはこれがなくても動くのですが、 ドラゴンに対してある特定のメソッドだけを行うことができて、 それ以外はドラゴン内部で起こるようにする、 という制限をつけたい時に必要です。 これは、「フードの下に覆い隠す」というふうに思えば分かるのではないかと思います。 別の例を挙げましょう。運転する時、あなたが知っていなければならない操作は、 アクセルペダルとブレーキペダルの踏み方、ハンドルの回し方などです (自動車のメカニックでない限り)。 プログラマはこれを車に対するパブリック インターフェイス と呼びます。 エアバッグがいつ開くかというのは車の内部の問題です。 普通のユーザ(ドライバー)はこのことを知る必要はありません。 END_PARAGRAPH end para do <<-END_PARAGRAPH これを、もう少し具体的にプログラムの行と関連させるため、 (たまたま私の仕事と関係あるのですが)、ビデオゲームの中の車をどんなふうに 作るかについて考えてみましょう。 最初に、パブリックインターフェイスをどうするかを決めたくなると思います。 別の言い方をすれば、あなたの車オブジェクトに対して、人々がどのメソッドを 呼ぶことが出来るようにするかということです。 おそらく、アクセルペダルとブレーキペダルを踏めるようにするのは 必要だし、どのくらい強くペダルを踏んでいるのかを特定するのも 必要でしょう。(べた踏みと軽くたたくのでは大きな違いです。) それから、ハンドルを切るのも必要でしょうし、どのくらい回すのかを 言う必要だってあるでしょう。それと、クラッチをつけたくなったり、 ウインカーをつけたくなったり、ロケットランチャー、アフターバーナー、 キャパシタ、などなど・・・。要はどんなゲームを作っているかによります。 END_PARAGRAPH end para do <<-END_PARAGRAPH で、車オブジェクトの内部ではずっとたくさんのことが行われている必要が ありますが、それだけではなくて、(最も基本的なものとして) 車のスピード、方向、位置などが必要です。 こういった属性はアクセルペダルやブレーキペダルを踏むことと、 ハンドルを回すことで変化するでしょうが、もちろん、ユーザーが直接 位置を指定できたりしてはまずいです(まるで、ワープのようになって しまいますから)。あるいは、スリップ、ダメージ、空気抵抗など を記録したくなるかもしれません。これら全ては車オブジェクトの内部 のものです。 END_PARAGRAPH end h2 {'練習問題'} para do <<-END_PARAGRAPH • #{code 'OrangeTree'}クラスを作ってみなさい。 このクラスには、木の高さを返す#{code 'height'}メソッドと、 メソッドを呼ぶことで、1年分時間を進める#{code 'oneYearPasses'}メソッドがあります。 毎年、この木は成長し大きくなります(オレンジの木が1年に伸びる分だけ)。 そして、ある年限が来たら(これもメソッド呼び出しによります) その木は死んでしまいます。最初の2,3年は実をつけませんが、その後は 実がなる様にします。で、成長した木は若い木よりたくさん実をつけます。 このあたりはあなたが納得するよう調節してみましょう。 そして、もちろん#{code 'countTheOranges'}(木になっているオレンジの数を返す)メソッドと、 #{code 'pichAnOrange'}(オレンジをひとつ摘むメソッド。このメソッドで #{code '@orangeCount'}が、1だけ小さくなり、いかにおいしいオレンジだったかを告げる文字列か あるいは、もう木にオレンジがなっていないことを告げる文字列かを返します。)を 実行できるようにしなければいけません。 それと、ある年に取り残したオレンジは次の年には落ちてしまうようにしましょう。 END_PARAGRAPH end para do <<-END_PARAGRAPH • 赤ちゃんドラゴンと会話が出来るようなプログラムを書いてみましょう。 #{input 'feed'}や#{input 'walk'}のようなコマンドを入力できるようにして ドラゴンに対してそれらのメソッドが呼ばれるようにします。 もちろん、入力されたものは文字列なので、ある種のメソッドに転送する処理 をしなくてはならないでしょう。つまり、何の文字が入力されたかをチェックして適切な メソッドを呼び出すという処理です。 END_PARAGRAPH end para do <<-END_PARAGRAPH これでプログラムに関してはすべて完了です!! でもちょっと待ってください。 まだ、Eメールを送ったり、ファイルをコンピュータにしまったり読み出したり、 あるいは、ウインドウズやボタンを作ったり、はたまた3次元の世界を作ったり、 のようないろいろなことをするクラスについてまだ何も説明をしていませんでしたね。 はい、ここですべてを示すことが不可能な程たくさんの 使用可能な クラスがあります。そのほとんどは私でさえ知りません。 私が言えるのは その大部分がどこで見つけることが出来るかということです。 そうすればプログラムで使いたいクラスについて、ひとつずつ学ぶことが出来るはずです。 でも、あなたを送り出す前に、後ひとつの章を授けたいと思います。 それは、あなたが知っておくべきRubyのもうひとつの特徴で、ほかのほとんどの言語には 無いのだけれど、それ無しでは私は生きていけないくらいのものです。 それは、#{makeLink 'ブロックと手続きオブジェクト', :generateBlocksProcs}です。 END_PARAGRAPH end end # # BLOCKS AND PROCS # def generateBlocksProcs para do <<-END_PARAGRAPH これは、Rubyに数あるクールな特色のなかでもその際たるものです。 ほかの言語の中にもこの特徴を持っているものがありますが、そこでは (クロージャー などの)他の名前がついています。 でも、ほとんどの有名なプログラミング言語にはないものです。 END_PARAGRAPH end para do <<-END_PARAGRAPH では、このクールな新しいこととはいったい何でしょう。 それは、コードのブロック を作って(#{code 'do'}と#{code 'end'}の 間のコードをこういいます。)、それをオブジェクトとしてくるんで (手続きオブジェクト(proc) と呼びます。)、変数に保存したり メソッドに渡したりして、それでもって、そのブロックの中のコードは好きな時に (何度でも)実行させることが出来るという能力です。 なので、この特徴はメソッドと似ているのですが、これがオブジェクトに付属しているものでは ないところが異なります(それ自身 がオブジェクトなのです)。 そして、それを他のオブジェクトと同じように保存したり受け渡したり出来るのです。 そろそろ例をお見せしたほうが良いですね。 END_PARAGRAPH end prog do <<-END_CODE toast = Proc.new do puts 'かんぱーい!' end toast.call toast.call toast.call END_CODE end para do <<-END_PARAGRAPH これで、手続きオブジェクトを作りました。(この#{code 'Proc'}というのは、 手続きという意味の"procedure"の短縮語だと思いますが、より重要なのは、 "proc"というのが、"block"と韻を踏んでいるということではないかと思います。) この手続きオブジェクトにはコードのブロックが含まれていて、それを3回 呼び出し(#{code 'call'}し)ました。これで見る限りメソッドとよく似てますね。 END_PARAGRAPH end para do <<-END_PARAGRAPH 実際、手続きオブジェクトはメソッドに本当に近いものと言えるかもしれません。 というのは、ブロックは引数をとることが出来るからです。 END_PARAGRAPH end prog do <<-END_CODE doYouLike = Proc.new do |aGoodThing| puts '私は *ほんとうに* '+aGoodThing+' が好きだ!' end doYouLike.call 'チョコレート' doYouLike.call 'ruby' END_CODE end para do <<-END_PARAGRAPH OK。ブロックと手続きオブジェクトが何たるかは分かりましたし、どう使うのかも 分かりました。でも、何がポイントなんでしょうか? 何でメソッドを使うだけではいけないのでしょうか? それは、メソッドでは出来ないことがあるからです。 特に、ひとつのメソッドを他のメソッドに渡すことは出来ません(手続きオブジェクトなら可能です)。 そして、メソッドは他のメソッドを返すことは出来ません(手続きオブジェクトを返すことは可能です)。 これは単純に、手続きオブジェクト(proc)がオブジェクトであるからで、 メソッドはそうでないからなのです。 END_PARAGRAPH end para do <<-END_PARAGRAPH (ところで、これはどこかで見たことがあることなのではないでしょうか? はい、すでにブロックは見たことがありますね。そう、イテレータを学ぶときに。 でも、ここではもう少し話を進めていきましょう。) END_PARAGRAPH end h2 { '手続きオブジェクトを受け取るメソッド' } para do <<-END_PARAGRAPH メソッドに手続きオブジェクトを渡すと、その手続きを、どうやって、どんな時に、何度、 呼ぶかのコントロールが可能になります。たとえば、あるコードが実行される前後に何かやりたいことが あるとしましょう。 END_PARAGRAPH end prog do <<-END_CODE def doSelfImportantly someProc puts 'みなさん、ちょっとよろしいですか! 私はしたいことがあります...' someProc.call puts 'Ok みなさん, 終わりました. していたことを続けてください.' end sayHello = Proc.new do puts 'コンニチハ!' end sayGoodbye = Proc.new do puts 'サヨウナラ' end doSelfImportantly sayHello doSelfImportantly sayGoodbye END_CODE end para do <<-END_PARAGRAPH おそらくこれを見ても、特別すばらしいとは思えないかもしれませんが、 実はすばらしいのです :)。 プログラムでは、何をいつやらなければならいかということに関して厳密に決められている ということはよくあることなのです。たとえば、もしファイルを保存したいなら、 ファイルを開き、保存したい情報を書き出し、ファイルを閉じます。 もし、ファイルを閉じ忘れたとしたら、良からぬこと(tm)が起こるかも 知れません。しかし、ファイルを保存したり読み出したりするたび毎に、 同じこと、つまり、ファイルを開き、ほんとに やりたいことを行い、 そしてファイルを閉じる、、をしなければならないのです。これは、退屈で忘れやすいことですね。 Rubyでは、ファイルの保存(あるいは読み出し)は上で示したようなコードになっています。 従って、実際に保存し(あるいは読み出し)たいこと以外に気を回す必要がなくなります。 (ファイルを保存したり読み出したりする方法に関しては、それらをどこで調べれば良いのか、 次の章で示しますね。) END_PARAGRAPH end para do <<-END_PARAGRAPH 手続きオブジェクトを何回呼び出すのか、あるいはそもそも呼び出すかどうか を決定するメソッドを書くことも可能です。 ここに、渡された手続きを半分の回数だけ行うメソッドと、 2回呼び出すメソッドを示します。 END_PARAGRAPH end prog do <<-END_CODE def maybeDo someProc if rand(2) == 0 someProc.call end end def twiceDo someProc someProc.call someProc.call end wink = Proc.new do puts '<ウインク>' end glance = Proc.new do puts '<目くばせ>' end maybeDo wink maybeDo glance twiceDo wink twiceDo glance END_CODE end para do <<-END_PARAGRAPH (何度かこのページをリロードすると、結果が毎回変わっているのが分かると思います。) 手続きオブジェクトのもう少し普通の使い方として、メソッドだけではできなかったものの例を 示しましょう。つまり、これまでに、ウインクを2回するメソッドを書く方法は覚えましたが 何か を2回するメソッドというのは書けなかったのではないでしょうか。 END_PARAGRAPH end para do <<-END_PARAGRAPH 先に行く前に、もうひとつの例を見てみましょう。 ここまでは、渡している手続きオブジェクトはお互い大体似たようなものでした。 今度は、まったく違ったものです。この例で、このようなメソッドが、 渡された手続きオブジェクトにどれほど依存しているかが見て取れると思います。 ここでのメソッドは、あるオブジェクトと手続きオブジェクトを受け取って そのオブジェクトを引数とした手続きを実行しています。 手続きがfalseを返すとプログラムを終了させます。でなければ、返ってきた値を引数として また手続きを呼び出します。これを、手続きがfalseを返すまで続けます。 (必ず最終的にはfalseを返すようにするべきです。でないとプログラムがクラッシュします。) メソッドは、手続きから返ってきたfalseではない最後の値を返します。 END_PARAGRAPH end prog do <<-END_CODE def doUntilFalse firstInput, someProc input = firstInput output = firstInput while output input = output output = someProc.call input end input end buildArrayOfSquares = Proc.new do |array| lastNumber = array.last if lastNumber <= 0 false else array.pop # 末尾の数字を取り出して... array.push lastNumber*lastNumber # ...その2乗の数と置き換え... array.push lastNumber-1 # ...1つ小さい数を後につける。 end end alwaysFalse = Proc.new do |justIgnoreMe| false end puts doUntilFalse([5], buildArrayOfSquares).inspect puts doUntilFalse('私はこれを午前3:00に書いています; 私はもうノックアウト!', alwaysFalse) END_CODE end para do <<-END_PARAGRAPH OK, これはかなり凝った例と言うことは私も認めます。 でも、この#{code 'doUntilFalse'}というメソッドが、渡された手続きオブジェクトによって まったく違う動作をするということはわかるのではないでしょうか。 END_PARAGRAPH end para do <<-END_PARAGRAPH #{code 'inspect'}メソッドはかなり#{code 'to_s'}と似ているのですが、 #{code 'to_s'}が人間が読めるようにするための文字列に変換するのと違って、 返す文字列が、渡したオブジェクトを作るためのRubyのコードを表現している ところが違います。ここでは、#{code 'doUntilFalse'}を最初に呼んだ時に、 返ってきた配列をまとめて表示するのに使っています。 それと、その配列の最後にある#{code '0'}は、実際には2乗されていないということ に気が付いたかも知れませんね。でも、#{code '0'}の2乗はやはり#{code '0'}なので、 その必要はなかったわけです。 そして、#{code 'alwaysFalse'}は見てのとおりいつも#{code 'false'}を返します。 なので、#{code 'doUntilFalse'}は2回目に呼ばれた時はまったく何もしていません。 呼ばれたものを単に返しているだけです。 END_PARAGRAPH end h2 { '手続きオブジェクトを返すメソッド' } para do <<-END_PARAGRAPH もうひとつ、手続きオブジェクトを使ってできるとてもクールな技は、 それをメソッド内で作成して返すということです。これによって、 (遅延評価 とか、無限データ構造 とか、 カリー化 とか)といったあらゆるクレイジーなプログラミングパワーを 得ることができます。 でも実のところ、私はほとんどこの技を実務で使わないですし、 誰かがこれをコードの中で使っているのを見た覚えはありません。 私が思うに、この技は普通Rubyでしなければならないたぐいのものではないし、 Rubyでは他の解決方法があるような気がします(詳しくはわかりませんが)。 とにかく、ここでは、簡単にこの技法について触れるだけにしましょう。 END_PARAGRAPH end para do <<-END_PARAGRAPH この例では、#{code 'compose'}は2つの手続きオブジェクトを受け取って、 ひとつの新しい手続きオブジェクトを返しています。その返す手続きオブジェクトは 呼ばれたら、最初の手続きをまず行って、その結果を2つ目の手続きに渡すということを します。 END_PARAGRAPH end prog do <<-END_CODE def compose proc1, proc2 Proc.new do |x| proc2.call(proc1.call(x)) end end squareIt = Proc.new do |x| x * x end doubleIt = Proc.new do |x| x + x end doubleThenSquare = compose doubleIt, squareIt squareThenDouble = compose squareIt, doubleIt puts doubleThenSquare.call(5) # (5+5) * (5+5) puts squareThenDouble.call(5) # (5*5) + (5*5) END_CODE end para do <<-END_PARAGRAPH ここで、#{code 'proc1'}の呼び出しは、最初に実行されなければならないため、 #{code 'proc2'}のカッコの中になければならなかったことに注意してください。 END_PARAGRAPH end h2 { 'メソッドに対して(手続きオブジェクトではなく)ブロックを渡す'} para do <<-END_PARAGRAPH 以上のことは、学問的に興味深い内容ではありますが、使うかどうかは議論の 分かれるところです。ここで問題なのは、3つのステップ(メソッドを定義し、 (ブロックを使って)手続きオブジェクト作り、 それを引数としてそのメソッドを呼ぶ。)を踏んでいるということです。しかし、 これは2つのステップ(メソッドを定義し、ブロック を、手続きオブジェクトを 使わずそのまま渡す。)でもよさそうに思えます。 なぜなら、メソッドに渡した後はその手続き(ブロック)を使う必要はほとんどないでしょうから。 さて、気が付いていたかもしれませんが、Rubyはそのことをすべて解決している のです。実際、イテレータを使うたびにその2段階のステップを使っているわけです。 END_PARAGRAPH end para do <<-END_PARAGRAPH 最初に手短な例からお見せしましょう。後でそれについて説明します。 END_PARAGRAPH end # HACK ALERT!!! (I can't get to the global namespace transparently # from inside the StringIO object in a mod_ruby script.) arrayClassHack = <<-END_CODE def eachEven(&wasABlock_nowAProc) isEven = true # 配列は0という偶数から始まるので、最初は"true"です。 self.each do |object| if isEven wasABlock_nowAProc.call object end isEven = (not isEven) # 偶数から奇数へ、あるいは奇数から偶数へとトグルします。 end end END_CODE Array.module_eval arrayClassHack # This is the real method definition. # The following defines a method in "another" array class: # END HACK ALERT!!! prog do <<-END_CODE class Array #{arrayClassHack} end ['アップル', '悪いアップル', 'チェリー', 'ドリアン'].eachEven do |fruit| puts 'んー, おいしい! 僕は '+fruit+'パイが大好きさ.' end # 配列の偶数番目の要素を取っていることを思い出してください。 # それらはすべて奇数なわけです。 # 私はこの手の問題を起こすのが結構好きなんですよ。 [1, 2, 3, 4, 5].eachEven do |oddBall| puts oddBall.to_s+' は偶数 で は な い!' end END_CODE end para do <<-END_PARAGRAPH ブロックを#{code 'eachEven'}に渡すには、ブロックをメソッドの後につければ よいだけです。どのメソッドにもこのようにブロックを渡すことができますが、 多くのメソッドはそれをただ無視するだけです。もし、あなたのつくるメソッドが、 これを無視せず、 捕まえて、手続きオブジェクトに変身させるようにするためには、 メソッドの引数の並びの最後に、アンド記号(#{code '&'})を頭につけて、 手続きオブジェクトの名前を並べてください。 そうすればそのメソッドを、#{code 'each'}や#{code 'times'}のような、ブロックを取る 組み込みメソッドと同じように、何度でも使えるようになります。 (#{code '5.times do'}を思い出してください。) END_PARAGRAPH end para do <<-END_PARAGRAPH もし、混乱するようなら、#{code 'eachEven'}の振る舞いを思い出してみましょう: 配列の要素に対してひとつ飛びに、渡されたブロックを呼び出しています。 一度これを書いてしまい、それが動くなら、(どのブロックがいつ呼ばれるかなど) その中で実際に行われていることに関して考える必要はなくなります。実のところ、 このようなメソッドを書く正確な理由 とはこういうことです。なので、 中でどのように動いているのかを再度考える必要はないわけです。使うだけです。 END_PARAGRAPH end para do <<-END_PARAGRAPH 私は以前、プログラムの異なる部分がそれぞれどのくらいの時間を取るのかを 知りたいと思いました。(これは、コードをプロファイル するともいいます。) そこで、コードを実行する前に時間を計り、実行させ、 その後もう一度時間を計り、その差を計算する、メソッドを書きました。 今すぐにはそのコードを見つけることはできませんが、おそらくその必要ありません。 それはおそらくこんな感じです。 END_PARAGRAPH end prog do <<-END_CODE def profile descriptionOfBlock, &block startTime = Time.now block.call duration = Time.now - startTime puts descriptionOfBlock+': '+duration.to_s+' 秒' end profile '25000回同じ数を足し合わせる' do number = 1 25000.times do number = number + number end puts number.to_s.length.to_s+' 桁' # これは、この巨大な数の桁数です。 end profile '100万まで数える' do number = 0 1000000.times do number = number + 1 end end END_CODE end para do <<-END_PARAGRAPH なんて簡単なんでしょう。そして、なんてエレガントな。いまや、こんな小さなメソッドで どんなプログラムのどんな一部分でも好きなだけ時間を計測できます。 単に、コードをブロックに入れて#{code 'profile'}に送れば良いのです。 これより簡単になり得る事はないと思います。他のほとんどの言語だったら、明示的に 時間計測用のコードを付け加えなければならないでしょう。 それに比べてRubyでは、そういったコードをすべて一つの場所、しかも(より重要なことですが) 邪魔にならない場所に、一まとめにしておくことができるのです。 END_PARAGRAPH end h2 {'練習問題'} para do <<-END_PARAGRAPH • おじいさんの時計 。 ブロックを取って、今日過ぎた時間の回数だけ、 ブロックを呼ぶメソッドを書きなさい。それで、もし、ブロックとして #{code "do puts 'DONG!' end"}を渡したとしたら、それはおじいさんの時計のような (そんなたぐいの)チャイムを打つことになります。できたメソッドを、 (先ほどの例を含めて)2,3の違うブロックを使ってテストしてみなさい。
ヒント:いまの時間を得るには #{code 'Time.now.hour'} が使えます。でも、この時間というのは、#{code '0'}から #{code '23'}の 数字を返しますので、それを普通の時計の前面にある数字 (#{code '1'}から #{code '12'})へと 変換しなければなりません。 END_PARAGRAPH end para do <<-END_PARAGRAPH • プログラムロガー。 ブロックを説明する文字列と ブロックを受け取る、#{code 'log'}というメソッドを書きなさい。 #{code 'doSelfImportantly'}の例と似ているかもしれませんが、このメソッドでは、 渡されたブロックの開始を告げる文字列、修了を告げるまた別の文字列、 そしてブロックが何を返したかを告げる文字列を#{code 'puts'}するようにします。 これにコードブロックを送って、作ったメソッドをテストしなさい。ブロックの中に 別の #{code 'log'}への呼び出しを入れて、それをもうひとつのブロックに 渡しなさい。(これは入れ子構造(nesting) と呼ばれます。) 別の言い方をすると、次のような出力が得られるようにしなさい。 END_PARAGRAPH end puts '
' +
          '開始 "外ブロック"...' + $/ +
          '開始 "ある小さなブロック"...' + $/ +
          '..."ある小さなブロック" 終了, 返り値は:  5' + $/ +
          '開始 "もうひとつのブロック"...' + $/ +
          '..."もうひとつのブロック" 終了, 返り値は:  I like Thai food!' + $/ +
          '..."外ブロック" 終了, 返り値は:  false' + $/ +
          '
' para do <<-END_PARAGRAPH • ロガー、改良版。 前のロガーの出力は少し読みにくく、多く使うほど より悪くなっていくようです。もし、内部のブロックで行が字下げ(インデント)されていれば 断然読みやすくなると思われます。それをするには、ロガーが、何か出力するたびに 何段階の入れ子になっているか保存しておく必要があるでしょう。その際、 コードのどの場所からも見ることができる変数である、グローバル変数 を使いなさい。この、グローバル変数を作るためには、#{code '$'}で始まる名前を 使えば良いです。たとえば、#{code '$global'}, #{code '$nestingDepth'}あるいは、 #{code '$bigTopPeeWee'}はグローバル変数です。 そして、最後、ロガーは、こんなふうな出力をするようにします。 END_PARAGRAPH end puts '
' +
          '開始 "外ブロック"...' + $/ +
          '  開始 "ある小さなブロック"...' + $/ +
          '    開始 "ちっちゃなブロック"...' + $/ +
          '    ..."ちっちゃなブロック" 終了, 返り値は:  lots of love' + $/ +
          '  ..."ある小さなブロック" 終了, 返り値は:  42' + $/ +
          '  開始 "もうひとつのブロック"...' + $/ +
          '  ..."もうひとつのブロック" 終了, 返り値は:  I love Indian food!' + $/ +
          '..."外ブロック" 終了, 返り値は:  true' + $/ +
          '
' para do <<-END_PARAGRAPH さて、これで、このチュートリアルから学ぶことはほとんどおしまいです。 おめでとう! あなたはたくさん のことを学びました。 おそらくは、すべてを覚えていると言う気持ちにはなっていないでしょうし、あるいは、 ある部分を飛ばしているかもしれません。でもそれでよいのです。 プログラミングは知る物ではなくて、理解するものだからです。 忘れたものを思い出す場所を知っている限り順調です。 私がこのチュートリアルすべてを何も見ないで書いたなどとは思わないで下さい。 なぜなら、しょっちゅう何かを参考にしていたからです。 それと、このチュートリアルの例のコードについても、多くの助けを得ています。 では、はどこでそれを見ていたのでしょう。そしては誰に 助けを請うていたのでしょう。次は、 #{makeLink 'それをお示ししましょう。', :generateBeyond} END_PARAGRAPH end end # # BEYOND THIS TUTORIAL # def generateBeyond para do <<-END_PARAGRAPH さて、それではこれからどこへ行きましょうか。もし質問が出てきたら、 誰に聞けばいいのでしょう。あるいは、プログラムでWebページを開いたり、 メールを送ったり、デジタル画像の大きさを変えたりしたくなったとしたら どうしたらいいでしょう。Rubyのヘルプを見つける場所は非常にたくさんあります。 不運にも、このことはあまり助けにはなりませんね。 END_PARAGRAPH end para do <<-END_PARAGRAPH 私についていえば、Rubyのヘルプを探す場所は、実際たった3個所です。 それが小さな疑問で、自分で実験して答えを見つけることができそうなときは、 irb を使います。そしてもう少し大きな疑問のときは、 ピッケル本(pickaxe)を開きます。 そして、もし自分で理解できないときにはruby-talk に助けを 求めます。 END_PARAGRAPH end h2 {'IRB: インタラクティブRuby'} para do <<-END_PARAGRAPH Rubyをインストールすると、irbが使えるようになっているはずです。 これを使う方法は、コマンドプロンプトから、#{input 'irb'}とタイプするだけです。 irbの中では、望みのRubyの式をタイプすると、その値が表示されます。 たとえば、#{input '1 + 2'}と打つと、#{output '3'}が得られます。 (ここで、#{code 'puts'}を使う必要が無いことに注意してください。) たとえていえば、これは大きなRuby計算機のようなものです。 終了するには、#{input 'exit'}とタイプしてください。 END_PARAGRAPH end para do <<-END_PARAGRAPH これ以外にirbにはもっとたくさんのことができるのですが、それはすべて ピッケル本で学ぶことができます。 END_PARAGRAPH end h2 {'ピッケル本: 「プログラミングRuby―達人プログラマガイド」'} para do <<-END_PARAGRAPH Rubyに関する手に入れるべき本と言えば、David Thomas と Andrew Hunt (達人プログラマ) 著の「プログラミングRuby, 達人プログラマガイド」は絶対にはずせません。 (訳註:現在、Ruby1.8に対応した第2版が出版されています。この版の翻訳本は言語編とライブラリ編とに 分かれて二分冊で発売されています。) このすばらしい本の書籍バージョンを買うことを強くお勧めはしますが、 この本は、オンラインで無償で手に入れることもできます。 (実際、あなたが、WindowsバージョンのRubyをインストールしたなら、 すでに、それは中に入っていたと思います。Andrew HuntはこのWindowsの Rubyインストーラーを作っている人物でもあります。) END_PARAGRAPH end para do <<-END_PARAGRAPH この本には、Rubyに関しての基礎から高度な内容まで、ほとんどすべてを見つけることが できます。読みやすく、広範囲にわたった記述があり、ほとんど完璧に近い出来です。 すべての言語に、このくらいの質の本があったらと思います。 この本の後半では、多くのページを割いて、すべてのクラスのすべてのメソッドについて 例を挙げながらの詳しい解説が載っています。 私はこの本が大好きです。 END_PARAGRAPH end para do <<-END_PARAGRAPH これを手に入れるには、(達人プログラマ自身のページを含め)多くの 場所がありますが、私のお気に入りのサイトは、 ここです。 このサイトのバージョンは素敵な目次と索引が左横にありますが、 特にすばらしいと思えるのは、画面下部のフレームです。 ピッケル本が出版されてから、Rubyは幾分か改良を加えられてきています。 Rubyのコミュニティはその変化を記録しオンライン上に投稿しつづけています。 この画面下部のフレームでは、オリジナル本に関する追加や訂正が 記述されているのです。 END_PARAGRAPH end para do <<-END_PARAGRAPH そして、なぜ「ピッケル(the pickaxe)」と呼ばれるかなのですが、それは、 この本の表紙カバーにピッケルの絵が描かれているからです。 変な名前ですが、すでに定着してしまいました。 END_PARAGRAPH end h2 {'Ruby-Talk: Rubyのメーリングリスト'} para do <<-END_PARAGRAPH irbやピッケル本をもってしても理解できないことはあるでしょう。 あるいは、今あなたの行っていることがなんにせよ、すでに行われているかどうかとか、 その結果を作る代わりに使わせてもらえるかどうかなど、知りたい時もあるでしょう。 こういった場合、行くべき場所はRuby-talkというRubyのメーリングリストです。(訳註: Ruby-talkは英語のメーリングリストです。日本語のメーリングリストは、Ruby-listです。) そこは、親切で、賢くて、助けになる人々であふれています。 これに関して、もっと知りたい、あるいは登録したいと言う場合は、 ここを見てください。 END_PARAGRAPH end para do <<-END_PARAGRAPH 警告: 毎日、非常に多くの メールがメーリングリスト上に 流れています。私は、自動的に他のメールフォルダーに移動するようにして、普段の メールとは別にしています。このメーリングリストのメール全部を読みたいので なければメーリングリストに参加する必要はありません。ruby-talkメーリングリストは ニュースグループのcomp.lang.rubyにお互いにミラーされていますので、こちらでも同じ メッセージを見ることが出来ます。どちらにしても、少し異なったフォーマットで、 同じものが読めます。 END_PARAGRAPH end h2 {'ティムトアディ'} para do <<-END_PARAGRAPH ここまで、私があなたから遠ざけていて、多分今後すぐに遭遇すること。 それは、TMTOWTDI(ティムトアディと読みます)という概念です。これは、 There's More Than One Way To Do It.(やり方はひとつではない。) の頭文字です。 END_PARAGRAPH end para do <<-END_PARAGRAPH ある人は、TMTOWTDIがいかにすばらしいかを語るかもしれません。またある人は全く 違った考えを持っているかもしれません。私は、これに関して特に強い気持ちを持って はいませんが、プログラムの方法を教えるという立場では、望ましくはない ものだろうと思っています。(何かをやるのに、ひとつの方法を覚えることでも、 かなりの挑戦を伴い、十分に混乱してしまうというのを否定しているように感じます。) END_PARAGRAPH end para do <<-END_PARAGRAPH しかし、このチュートリアルを離れていくあなたは、もっともっといろいろなコード を見ることになるでしょう。たとえば、私は、文字列を作るのに(ある文字の並びを シングルクオーテーションマークで囲うという方法の他に)、少なくとも5つの方法が あリ、それぞれ少しずつ違っているということを思いつきます。 私はその6種類の方法の中で最も簡単な方法しか見せませんでした。 END_PARAGRAPH end para do <<-END_PARAGRAPH そして、分岐の説明をしたときに、私は、#{code 'if'}を示しましたが、 #{code 'unless'}については述べませんでした。これについては、irbで 動作を確かめることを勧めます。 END_PARAGRAPH end para do <<-END_PARAGRAPH もうひとつ、#{code 'if'}や#{code 'unless'}や#{code 'while'}を使うときに うまい、ちょっとしたショートカットの方法があるのですが、それは 1行バージョンというやつです。 END_PARAGRAPH end prog do <<-END_CODE # 次の単語たちは普通の英語の文章のように書ける、プログラムの一部です。 # クールでしょ? puts 'あいうえお かきくけこ' if 5 == 2**2 + 1**1 puts 'さしすせそ たちつてと' unless 'Chris'.length == 5 END_CODE end para do <<-END_PARAGRAPH そして最後に、(手続きオブジェクトではなく)ブロックをとるメソッドを書くもうひとつの 方法を示します。ブロックを受けてこれを手続きオブジェクトに変換するときには、 関数定義の引数リストの最後に#{code '&block'}をつけるという方法を見てきました。 そして、ブロックを呼び出すときには、#{code 'block.call'}としました。 でも、これには別のもっと短い方法があります(私個人としては、より混乱するように 思えるのですが)。次のようなブロックを受ける方法があったとして、 END_PARAGRAPH end prog do <<-END_CODE def doItTwice(&block) block.call block.call end doItTwice do puts 'いろはにほてと ちりぬるを' end END_CODE end para do <<-END_PARAGRAPH その代わりに、次のようにも出来ます。 END_PARAGRAPH end prog do <<-END_CODE def doItTwice yield yield end doItTwice do puts 'わかよたれそ つねならむ' end END_CODE end para do <<-END_PARAGRAPH 私には分かりませんが、どう思いますか?私だけが感じることかもしれませんが、 #{code 'yield'}ですって? もしたとえば、#{code 'call_the_hidden_block'}のような 名前なら、ずっと 受け入れられるのですが。 多くの人が、#{code 'yield'}は分かりやすいというのです。(訳註:訳者もyieldの方が良いと思います。) どちらにしても、これがTMTOWTDIがある理由なのかもしれません。好きなようにやれば良いのです。 私も私のやり方でやります。 END_PARAGRAPH end h2 {'おしまい'} para do <<-END_PARAGRAPH これを良い目的に使ってくださいね。 :) もし興味があったら、 #{makeLink 'about this tutorial', :generateAbout} で、もう少し学ぶことが出来るでしょう。で、もし使えるなと(あるいは、 分かりにくいなとか、間違ってるよとか)思ったら、 知らせてください! (訳註:pineさんには、原文をあたってから 質問してください。日本語訳に関する誤りやサジェスチョンは、 こちらまでお知らせください。) END_PARAGRAPH end end # # ABOUT THIS TUTORIAL # def generateAbout para do <<-END_PARAGRAPH このチュートリアルは私、 Chris Pine が書き下ろしました。使用、変更、書き換え、あなたのサイトへのコピー など、どうぞご自由になさってください。そのとき、ひとつだけお願い したいのは、このオリジナルのチュートリアルへ、はっきりとした リンクを張っていただくことです。 END_PARAGRAPH end para do <<-END_PARAGRAPH あなたが見ているこのページ(すべてXHTML 1.1に則っていますが、)は このRubyプログラム. から生成したものです。これは見事なコードであるとは言えないかも しれませんが、ちょっとうまい特徴を持っています。 たとえば、すべてのコードのサンプルは、そのページを眺めるたびに 実際に実行されていて、表示される出力はそのプログラムが出力した結果 そのものです。私は、このやり方が、提供したコードのすべてが説明どおり 正確に 動作しているのを確認するための、最も良く、最も簡単で、 おそらく最もクールな方法であると思っています。 私がある例の出力を間違えてコピーしていたり、コードのどれかをテスト し忘れていたりすることの心配は要りません。見るたびごとにすべてその場で テストされているのですから。(なので、乱数発生の節ではページをリロード すると、毎回数値が変わっているのが分かります。.....ナイス ですね。) END_PARAGRAPH end para do <<-END_PARAGRAPH このチュートリアルでは、できるだけ概念を分けるようにしました。 その結果、学習者は一時にひとつの内容だけを学べばよいことになります。 最初これは難しいかもしれませんが、いくつかの例題を行った後では ちょっと簡単過ぎて しまうようです。ほかの事を教える前に、 教えておかなければならないこともあります。しかし、実際には、その 順序構造がほとんどないことにびっくりしました。結局のところ、 ある順序に沿って話を進めなければならなくて、それぞれの新しい章が 前の章から興味を起こさせるように配列することにしました。 この配列が常にうまく行っているかどうかは自信はありませんが。 END_PARAGRAPH end para do <<-END_PARAGRAPH このチュートリアルの、もうひとつの原則は、あることをするために、 必ずひとつのやり方のみを教えるということです。これはいままで プログラムをしなかった人に対するチュートリアルでは明らかな利点です。 あることをするのに、やり方をひとつ覚えることは2つ覚えるより明らかに 簡単です。おそらく、より重要な利点は、新しいプログラマに教えることが 少ないほど、彼らはプログラミングにおいて、より創造的でより賢くならねば ならないということです。プログラミングの大多数は問題解決のためにある ので、すべての段階で可能な限りそれを奨励するのは非常に重要なのです。 END_PARAGRAPH end para do <<-END_PARAGRAPH 私は、プログラミングに関する概念を説明するのに、新しいプログラマが すでに知っているはずの概念に便乗するように心がけました。つまり、 教え込むというより学習者の直感がうまく働くような形で、アイデアを 提示したわけです。オブジェクト指向プログラミングはこの方法に 非常に向いています。このチュートリアルでは、「オブジェクト」や、 異なる「種類のオブジェクト」といった言葉を、最も自然な感じで 文中に滑り込ませることによって、非常に簡単に言い始めることが 出来ました。それから、「Rubyでは、すべてのものがオブジェクトである。」 とか、「数や文字列ですらオブジェクトの種類だ。」とかいった表現は 使いませんでした。なぜなら、これらの文は新しいプログラマには実際、 全く意味を成さないからです。その代わり、文字列(「文字列オブジェクト」 ではなく)について説明しましたし、時には、「オブジェクト」という言葉を 単純に、「これらのプログラムの中のもの」という 意味で言及したりしました。 (訳註:日本語では多少ぎこちなくなっている気もします。) Rubyにおけるすべてのもの がオブジェクトである という 事実は、私のこっそり忍び込ませる作戦を非常にやりやすくしてくれました。 END_PARAGRAPH end para do <<-END_PARAGRAPH 必要のないOO(訳注:オブジェクト指向)用語を使うのは避けたかったのですが、 言葉を習う必要があるとしたら、正しいものを習うべきだということを確実にしたいと 思いました。(初学者が同じ物を2度習わなければならないようには したくなかったのです。)なので、私は"文字列(string)"を"テキスト(text)"とは 呼びませんでした。メソッドには何か呼び名をつける必要がありましたので、 「メソッド(method)」と呼ぶようにしました。 END_PARAGRAPH end para do <<-END_PARAGRAPH 練習問題に関しては、良い出来のものを作ったと思っていますが、数は 多くはありません。正直、楽しくて興味深い練習問題を作り出そうと 半分の時間を使ったんですよ。(ちょっと悲しいですか?) (多くのチュートリアルで見られるような)退屈な練習問題はプログラム しようとする欲求を確実に摘んでしまいます。 完璧な練習問題というのは、新しいプログラマがどうしてもやりたくなると むずむずしてくるようなものです。 でも、この世に完璧なものなどありませんね。 END_PARAGRAPH end para do <<-END_PARAGRAPH 概して言えば、このチュートリアルはとてもうまくいったと思います。 もし、間違えやスペリングミスに気づいたり、あるいはコメントや サジェスチョンがあったり、あるいは、新たに含めることが出来そうな 練習問題を思いついたりしたら、ぜひ、 お知らせ ください。 END_PARAGRAPH end para do <<-END_PARAGRAPH 最後に、考えと激励をくれたruby-talkメーリングリストのすべての人々に、 モルモットになってもらった妻に、すばらしい言語を作ったMatzに、 そして、それを私に伝えてくれた達人プログラマに、 感謝したいと思います。 END_PARAGRAPH end para do ''+ 'powered by Ruby'+ '' end end # # MAIN PAGE GENERATION # def generate srand tocString = '目次' tutTitle = 'プログラミング入門 - Rubyを使って -' puts '' puts '' html(:xmlns=>'http://www.w3.org/1999/xhtml', 'xml:lang'=>'ja') do head do link(:rel=>'stylesheet', :type=>'text/css', :href=>LINKADDR.sub('index.rb', 'tutorial.css')) link(:rel=>'shortcut icon', :href=>'favicon.ico') title do tutTitle + ', by Chris Pine, 日本語ver. by S. Nishiyama' end end # head body do chapNum = @cgi.params['Chapter'][0] chapter = @chapters[chapNum] if chapter para { makeLink tocString, :generateTOC } # Method generateTOC is intentionally not defined. chapTitle = '' chapTitle += chapNum + '.  ' if chapNum < 'A' chapTitle.sub! /^0/, '' chapTitle += chapter[0] h1 {chapTitle} puts @@HLINE method(chapter[1]).call puts @@HLINE para { makeLink tocString, :generateTOC } # Method generateTOC is intentionally not defined. else # TOC h1 {tutTitle} para do <<-END_PARAGRAPH 未来のプログラマのためのチュートリアル END_PARAGRAPH end puts @@HLINE h2(:style => 'padding-top: 0px;') {tocString} @chapters.sort{|a,b|a[0]<=>b[0]}.each do |chapNum, chapter| chapTitle = chapNum + '.  ' chapTitle.sub! /^0/, '0' if chapNum != 'format' && chapNum != 'about' para { chapTitle + makeLink(chapter[0],chapter[1]) } end end puts @@HLINE para { makeLink@chapters['about'][0], @chapters['about'][1] } end end # body end # html end end cgi = CGI.new if cgi.params['ShowTutorialCode'][0] tutorialSource = nil open ('index.rb') { |file| tutorialSource = file.read } cgi.out('text/plain') { tutorialSource } else page = TutorialWebPage.new cgi page.generate page.out end