#! /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] == '#')
''
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 ''+methodName+'>'
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というのは、私の手と足にある指の合計の数であって、
2 と 0 でできた文字列のことではありません。
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
''+
''+
''
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