読者です 読者をやめる 読者になる 読者になる

ピッケル本を読む(1)第2章 Ruby.new

夏休みに突入したので、本格的にピッケル本(プログラミングRuby 第2版 言語編)を読んでみようと思う。なお、式を評価した値を矢印の右側に示すことにする。

# sample
"Hello, ruby!".length    #=>    12    

メモ

  • Rubyは純粋なオブジェクト指向言語
    • 操作の対象、結果もオブジェクト
  • 実世界の概念をコード内にモデル化した(モノ)=エンティティ(?)
  • 標準コンストラクタは「new」 例:Song.new("Ruby Lullaby")
  • 全てのオブジェクトには一意なオブジェクト識別子(オブジェクトID)がある
# オブジェクトIDを表示してみる。(p349)
num = 1
p num.object_id    #=>    3
# 小文字又はアンダースコア(_)が先頭:ローカル変数
# ただし、$, @, @@が先頭の場合は異なる意味の変数になるので注意
instance = "foo"

# $が先頭:グローバル変数
$global = "hoge"

# @が先頭:インスタンス変数
@instance = "foo"

# @@が先頭:クラス変数
@@class = "foo"

# 大文字が先頭:定数又はクラス名・モジュール名
# PI, String, etc.
  • Rubyの文法規則上、定数、クラス名・モジュール名は先頭文字を大文字にする
  • 習慣上、複数の単語からなるインスタンス変数は、単語の間にアンダースコアを挿入する(例:@point_1)
  • 習慣上、複数の単語からなるクラス名は、各単語の先頭文字だけを大文字にする(最初の単語の先頭文字は大文字じゃないとダメ。これはRubyの文法規則。)
  • メッセージ=メソッド名+パラメータ
    • メッセージという呼び名はSmalltalkからきているらしい
  • Rubyでは数値もオブジェクトなので、数値自自体に色々と処理をさせることが出来る
# RubyとJavaの対比(絶対値を求める処理)

# Rubyではこう書く(数値自体が仕事をやってくれる)
number = -3.abs    #=>    3

# Javaではこう書く(別のクラスのメソッドを呼び出す必要がある)
number = Math.abs(-3)
  • メソッドの引数の括弧は省略可能
  • 文字列リテラルのダブルクォートとシングルクォートの違い
    • シングルクォート≒文字列リテラルそのもの
    • ダブルクォート=エスケープシーケンスの置換(特定の2進値で置き換える)、式の展開( #{expression}というシーケンスをその式の値で置換する)
# エスケープシーケンスに対する処理の違い
puts 'And good night, \nGrandma'    #=>    And good night, \nGrandma
puts "And good night, \nGrandma"    #=>    \nのところで改行される

# 式の展開に対する処理の違い
name = 'Pa'
puts 'Good night, #{name}'    #=>    Good night, #{name}
puts "Good night, #{name}"    #=>    Good night, Pa

# 式が単純なグローバル変数・インスタンス変数・クラス変数の場合はブレースを省略しても良い
$global = "Hello"
@instance = "bigfatcat"
puts "#$global, #@name"    #=>    Hello, bigfatcat

# ローカル変数はブレースを省略できないので注意
local = "good cat"
puts "#local"      #=>    #local
puts "#{local}"    #=>    good cat
  • メソッドの戻り値は、そのメソッド内で最後に評価された式の値になる
    • 一時変数とreturn文が省略できるので便利かも
def say_goodnight (name)
  "Good night, #{name}"
# return文がいらない
end
puts say_goodnight ('Ma')    #=>    Good night, Ma
  • ハッシュと配列
    • Rubyの配列とハッシュは、添え字つきコレクション。
    • 配列のキーは整数
    • ハッシュのキーは任意のオブジェクト
# 新しい配列を作成・初期化するには、配列リテラル(角括弧で囲まれた一連の要素)を使う
a = [ 1, 'cat', 3.14]
puts a[0]    #=>    1
puts a[2]    #=>    nil
p a    #=>    [1, 'cat', 3.14] # Rubyではnilもオブジェクト。ただ、何も表さないだけ。

# 文字列の配列を作りたいときは、次のようにすればクォートやカンマを使う必要がなくて便利
p %w{ a b c 3.14 }    #=>    ["a", "b", "c", "3.14"]

# 新しいハッシュを作成・初期化するには、ハッシュリテラル(中括弧で囲まれた一連の要素)を使う
# hash_var = {key => valuable}
inst_section = {
  'cello' => 'string',
  'clarinet' => 'woodwind',
  'drum' => 'percussion'
}
p inst_section['cello']    #=>    "string"

# ハッシュで存在しないキーを指定すると、デフォルトではnilが返される(配列でも同じ)
# nilは条件式で使うとfalseと同じ意味なので、この仕様は好都合
p inst_section['bassoon']    #=>    nil

# 存在しないキーを指定したときのデフォルト値は、変更できる
histogram = Hash.new(0)
p histogram['key1']    #=>    0
p histogram['key1'] = histogram['key1'] + 1
p histogram['key1']    #=>    1
  • Rubyの文修飾子(statement modifier)
    • Rubyの大半の文は値を返すらしい
    • if文って呼んでいいのかな?if式な気がする…
if radiation > 3000
  puts "Danger, Will Robinson"
end
# 次のように、文修飾子としてif文を使っても同じ
puts "Danger, Will Robinson" if radiation > 3000

square = 2
while square < 1000
  square = square * square
end
# 次のように、文修飾子としてwhile文を使っても同じ
square = 2
square = square * square while square < 1000
/Perl|Python/  # PerlまたはPythonとマッチ
/P(erl|ython)/ # 同上

/ab+c/ # +は直前の1文字の1回以上の繰り返しを表す
/ab*c/ # *は直前の1文字の0回以上の繰り返しを表す

# 文字クラス(文字グループ中の1文字にマッチするパターンのこと)
\s # 空白文字(スペース・タブ・改行など)にマッチ
\d # 数字にマッチ
\w # 英数字と_にマッチ

/\d\d:\d\d:\d\d/ # 例えば、12:34:56などの時刻にマッチ
if line =~ /Perl|Python/
  puts "Scripting language mentioned: #{line}"
end
  • 正規表現にマッチした文字列の一部を他のテキストで置き換える
line.sub(/Perl/, 'Ruby')    # 最初の Perl を Ruby で置き換える
line.gsub(/Python/, 'Ruby') # 全ての Python を Ruby で置き換える
  • コードブロックとイテレータ(すごい強力な機能らしい)
    • コードブロックとは、ブレースまたはdo...endで囲まれた単なるコードのかたまり
{puts "Hello"}         # これはブロック

do                     ###
  club.enroll(person)    # これもブロック
  person.socialize       #
end                    ###
    • 一行のブロックにはブレースを、複数行のブロックには do/end を使うのが習慣らしい
    • ブロックはメソッド呼び出しに関連付けることが出来るらしい(最初聞いたときは軽く混乱した)
# 次のようにすると、puts "Hi"を含むブロックがgreetメソッドの呼び出しに関連付けられる
greet {puts "Hi"}
# メソッドの引数がある場合は、ブロックの前に指定する
verbose_greet("Dave", "loyal customer") {puts "Hi"}

# 次の例を見れば一目瞭然
# メソッドは、自分に関連付けられたブロックを、yield文を使って呼び出すことが出来る
def call_block
  puts "Start of method"
  yield
  yield
  puts "End of method"
end
call_block {puts "In the block"}
# 出力結果
Start of method
In the block
In the block
End of method

# yieldにパラメータを指定すると、それがブロックに渡される
# ブロック内では、これらのパラメータを受け取るための引数名のリストを縦棒の(|)間に指定する
def call_block
  yield("hello", 99)
end
call_block {|str, num| ...}
    • コードブロックはRubyのライブラリのあちこちで、イテレータを実装するために使われてる(イテレータとは、配列などのコレクションから連続する要素を返すメソッドのこと)
animals = %w(ant bee cat dog elk)   # 配列を作成する
animals.each {|animal| puts animal} # 配列の要素を繰り返し処理する
# 出力結果
bee
cat
dog
elk

# 上の例で使った Array クラスの each イテレータの擬似コード
# eachイテレータは配列の各要素をループ処理する。その際、要素ごとにyieldを呼び出す。
# Array クラス内では…
def each
  for each element # Ruby の文法では間違い
    yield(element)
  end
end
    • つまり、Rubyのコードブロックを利用したイテレータを使えば、CやJavaだとfor文などを使わなくちゃならないところを、Rubyでは単なるメソッド呼び出しとして実現できる
['cat', 'dog', 'horse'].each {|name| print name, " "}
5.times {print "*"}
3.upto(6) {|i| print i}
('a'..'e').each {|char| print char}
# 出力結果
cat dog horse *****3456abcde
printf("Number: %5.2f,\nString: %s\n", 1.23, "hello")
# 出力結果
Number:  1.23,
String: hello
  • プログラムに入力を読み込む方法で一般的なのは、getsを使う方法
line = gets
print line
  • Perl流からの脱却を図るRubyPerlRubyの対比)
    • -wフラグを指定してプログラムを実行すると、RubyインタプリタPerl的なコーディングに対して警告を出すらしい
Perl
# あるファイルから、Rubyというテキストを含む全ての行を検索する
# Perlでは、マジックを総動員するとこう書けるらしい
while gets
  if /Ruby/
    print
  end
end
Ruby
# あるファイルから、Rubyというテキストを含む全ての行を検索する
# Rubyでは、イテレータと事前定義のオブジェクトARGF(プログラムの入力ファイルを表す)を使う
ARGF.each {|line| print line  if line =~ /Ruby/}

#次のように、もっとコンパクトに書くこともできる
print ARGF.grep(/Ruby/)

まとめ

オブジェクト・メソッド・文字列・正規表現・ブロックとイテレータについてざっと勉強した。
それにしてもこの本、全くの初心者(プログラミング未経験者)が読もうとしても恐らく挫折すると思う。他の言語との対比は経験者には嬉しいけど、未経験者は混乱するだろうし、オブジェクト指向の用語(クラス、オブジェクト、メソッドなど)に関してはほとんど説明が無い。逆に、CやJavaPerlなどで多少なりともプログラミング経験(できればオブジェクト指向の簡単な知識)があれば、自分に必要なところだけ説明してくれてわかり易い本だと思う。
とりあえず、今日はここまで。通してメモするのきつそうだから、次回以降はもっと要点に的を絞ってメモしないとかな。