yunomuのブログ

プログラミング関係

Rust yew の function_component が動かない

 yewで遊ぶぞーとなって一番手こずったところ。

 yew 0.20.0ではyew::start_appが無くなっているので、yewの(古い)チュートリアルにあるコードはそもそも動かない。

 かといってdocsにあるExampleもそのままでは動かない。具体的にはyew::Renderer:<App>::new().render();の部分で could not find `Renderer` in `yew`と言われる。

docs.rs

 そのものズバリなissueで答えが出てた。

github.com

 Cargo.tomlファイルのdependenciesのyewの項目にfeaturesを指定するとよい。

yew = { version = "0.20.0", features = ["csr"] }

 よく見るとyewのdocsのRendercsrというfeaturesのタグが貼ってある。これそういう意味だったのか。つまりCargoのマニュアルをあまり真面目に読んでいなかったのが敗因。

 おまけ。function_componentとwasm_bindgenで動かす時のHelloWorldのサンプル:

src/lib.rs

use wasm_bindgen::prelude::wasm_bindgen;
use yew::prelude::{function_component, html, Html};

#[function_component(App)]
fn app() -> Html {
    html! {
        <div><h1>{"Hello, World!"}</h1></div>
    }
}

#[wasm_bindgen(start)]
pub fn run_app() {
    yew::Renderer::<App>::new().render();
}

Cargo.toml

[package]
name = "yew-app-fn"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
wasm-bindgen = "0.2.83"
yew = { version = "0.20.0", features = ["csr"] }

MSYS2でRuby開発環境を作る

 ウィルスバスターその他いろいろに阻まれてrubyinstaller+DevKit(MSYS2)のインストールに失敗したみなさん。いっそMSYS2そのものをインストールしてしまいましょう。64bitのWindowsの7以降を対象にしています。10ならだいたいOKだと思います。

www.msys2.org

 英語だけどがんばって。Installationの節を順番に実行していきます。

  1. ダウンロードします。ダウンロードしましょう。
  2. ダウンロードした"msys2-x86_64-XXXXXXXX.exe"を実行します。XXXXXXXXのところはなんか数字が入ります。1でダウンロードしたファイルです。
  3. 画像のとおり、インストール先を選びましょう。だいたいそのままでOKです。
  4. インストールしたら"Run MSYS2 now."をチェックして終了しましょう。
  5. なんかウィンドウが出ますよね。こういうコマンド入力できる画面を「ターミナル」と呼びます。ターミナルエミュレーターの略です。まずコマンドpacman -Syuを実行します。pacmanは、パッケージ(ソフトウェアのに必要なファイル一式のようなもの)を管理するソフトで、だいたいスマホのAppStoreやGoogle Play Storeと似たようなやつです。無料なので安心してください。pacman -Syuは、全てのパッケージを更新チェックして更新するという意味です。例で出ている最後のエラーはだいたい「再実行してください」という意味です。これが出ると一旦ターミナルが終了します。
  6. スタートメニューか、見つからなければインストールしたフォルダの中から、"MSYS2"を実行してください。またターミナルが開くと思います。先程のようなエラーが出た場合はここでpacmanを再実行します。pacman -Suです。前のやつから"y"が一文字消えているのは、今回は更新チェックをしなくてよいからです。さっきしたからね。
  7. これでMSYS2使えるようになったよ(以下略)

 とりあえずここまでで良いでしょう。次はRubyをインストールします。

$ pacman -S ruby

 これでおわりです。今日の時点ではRuby 3.0.0がリリースされていますが、まだパッケージが更新されていないのでRuby 2.7.2-1が入るのではないかと思います。

$ ruby --version

 このように今どのバージョンが入っているかを確認して、適切なバージョンのマニュアルを見るようにしましょう。マニュアルはここでバージョンを選ぶことができます。

docs.ruby-lang.org

 一応、whichコマンドで、Rubyがどこにインストールされているかを見ておいてください。

$ which ruby
/usr/bin/ruby

 みたいになっていたら成功です。パスが/c/で始まっている場合は、CGIの例題が動かない可能性があるのでPATHなどを見なおす必要があります。くわしい人に相談してください。

CGIを書こう

 タイトル「インターネットになろう」にしようと思ったけどやめた。

 今回はちょっとWebをやってみます。「そろそろ何をやりたいか考えてみよう」と言いたいところですが、そんな簡単に見つかるなら幸せなことで、それはそれで良いのですが、普通はとりあえずできることを増やしながら「これ面白いな」と思うことを見つけていくというのが良いでしょう。

準備(Windows使いのプログラマ向け)

 まずはサンプルを動かしてみてほしかったのですが、Windowsだと肝心な部分がどうしても動かなかったのでちょっと下準備をします。

 WindowsのPCを使っていて、Rubyのインストールで「RubyInstaller」を使っていて、現在はコマンドプロンプト(Rubyといっしょにインストールされている"Start Command Prompt for Ruby"みたいなやつを含む)を使ってRubyを書いている人向けの情報です。

 おそらく最初に以下のようなページからrubyinstaller-devkit-2.7.2-1-x64.exerubyinstaller-2.7.2-1-x64.exeをダウンロードしてインストールしたのではないでしょうか。

rubyinstaller.org

 名前に"devkit"と入っている方のファイルがある人はそのまま、無い方のファイルの人はdevkitのファイルをダウンロードしてください。ファイルをなくした人もダウンロードしなおしてください。

 そして再度インストールしてください。ただし、今度は途中のモジュール選択の部分で"MSYS2"をインストールするようにチェックを入れてください。最初にインストールした時と同様にコマンドプロンプトのウィンドウが開いて色々質問されると思いますが、全部そのままEnterを押しておけば大丈夫です。

 インストールが終わったら、今までと同じようにコマンドプロンプトを立ち上げてください。そのあと、bashコマンドを実行してください。ちょっと見た目が変りましたね。bashは、おおむね今までのコマンドプロンプトと似たようなものですが、色んな機能があったりなかったりします。でも今までどおりcddirも使えるので安心してください。dirの代わりにlsも使えますし、もちろんrubyirbも使えます。

 では、ここからはbashを使っているという前提でいきます。

WEBrickを使う

 ここではWEBrickというWebサーバを使ってみます。Webサーバとはなんぞやというのはまあやってみればなんとなくわかるでしょう。マニュアルはこれまでとは違い「標準添付ライブラリ All libraries」のページにあります。ページには大量のライブラリがありますが、ネットワークの節の"webrick"のページを見てください。

library webrick (Ruby 2.7.0 リファレンスマニュアル)

 概要にサンプルコードが載っています。「以下は Web サーバとして完全に動作するスクリプトです。」とのことなので、とりあえずこれを適当なファイル名(ここではweb.rbとします)のファイルに打ち込むかコピペしてください。

require 'webrick'
srv = WEBrick::HTTPServer.new({ :DocumentRoot => './',
                                :BindAddress => '127.0.0.1',
                                :Port => 20080})
srv.mount('/view.cgi', WEBrick::HTTPServlet::CGIHandler, 'view.rb')
srv.mount('/foo.html', WEBrick::HTTPServlet::FileHandler, 'hoge.html')
trap("INT"){ srv.shutdown }
srv.start

 これを実行します。

% ruby web.rb
[2021-02-05 19:57:11] INFO  WEBrick 1.6.0
[2021-02-05 19:57:11] INFO  ruby 2.7.0 (2019-12-25) [x86_64-linux]
[2021-02-05 19:57:11] INFO  WEBrick::HTTPServer#start: pid=111395 port=20080

 このように、なにかメッセージが出てそのままプログラムが停止すると思います(細かい数字は違うかもしれません)。エラーが出たり終了してしまったら何か書き間違いがあるかもしれません。書き間違いは熟練プログラマでもよくあることなので注意深く探してみましょう。

 この状態で、Webブラウザを開いてURL http://localhost:20080 にアクセスしてみてください。ファイル一覧みたいなものが見えたら成功です。たぶんさきほど作ったファイル(web.rb)が見えているんじゃないでしょうか。ファイル名をクリックすると中身が見られます。インターネットっぽくなってきましたね。

 終了する時はコマンドの画面に戻ってC-Cです。サンプルコードの中の最後から2行目trap("INT"){ srv.shutdown }の部分が、C-Cを押したら終了するという意味になっています。書かないと終了できなくて不便です。ウィンドウ閉じたら終了したりするんだろうか? よくわかりません。別に試さなくてもよいでしょう。最悪PCを再起動すれば終了します。

ファイルにアクセスする

 まず、/hoge.htmlにアクセスします。URLは http://localhost:20080/hoge.html です。"Not Found"のような文字が表示されたと思います。それで正常です。つまり「"hoge.html"というファイルはありません」ということです。実際に無いので、これはこれで正しい。

 では"hoge.html"というファイルを作ってみましょう。何か適当なことを書いて、現在ソースを置いているのと同じフォルダに"hoge.html"という名前で保存します。内容が思いつかない人はこうしましょう。

You just don't have enough fight in you!

 ファイルを保存して再度、http://localhost:20080/hoge.html にアクセスしてみましょう。「You just don't have enough fight in you!」または適当に書いた文字が表示されていれば成功です。文字化けした人は、アルファベット以外を書きましたね。このように、Webサーバはファイルをブラウザに配信することができます。HTMLが書けるならHTMLを書くともうちょっとそれらしくなります。文字化けした人は、HTMLでエンコードを指定するか、ブラウザの設定でエンコードをいじるか、なんかやってみてください。

 これだけでも何かと便利なので覚えておくと良いでしょう。

 次に、/foo.htmlにアクセスします。URLは http://localhost:20080/foo.html です。/hoge.htmlにアクセスした時と同じ結果になったと思います。なっていない人は、コードのどこかを間違えていると思います。がんばって間違いを探しましょう。この間違いさがしを専門用語でデバッグとも言います(デバッグはもっと広い意味での間違いさがしですが)。

 同じ結果になるのは、コードの中のsrv.mount('/foo.html', WEBrick::HTTPServlet::FileHandler, 'hoge.html')の行の効果です。これで「"/foo.html"にアクセスした時は"hoge.html"のファイルを返す」という意味になります。詳しくはWEBrickのマニュアルにありますが、難しいのでまた別に解説してもいいと思っています。話せば長くなります(たのしい)。

プログラムにアクセスする(CGI)

 本題です。ファイルにアクセスしてファイルが返る、または適当なパス(ここでは"/foo.html")にアクセスしてファイルが返るように、プログラムを実行して実行結果をブラウザに返すといったようなことができます。ものすごく簡単に言うと、このような仕組みをCGIと言います。

 まだ解説していない以下の行に注目してください。

srv.mount('/view.cgi', WEBrick::HTTPServlet::CGIHandler, 'view.rb')

 これは日本語訳すると「"/view.cgi"にアクセスされたら"view.rb"を起動してその結果を返す」という意味です。ここで出てくる"view.rb"はRubyのプログラムです。そんなものは存在しないので、現時点で http://localhost:20080/view.cgi にアクセスすると「Internal Server Error」みたいなのが出ると思います。Webサービスがエラーになった時にこのような画面を見たことがある人もいるかもしれません。つまりそういうことです。

 ということで、ファイル"view.rb"を用意します。内容は以下のようにしてください。

#!/usr/bin/env ruby

print "Content-Type: text/plain\r\n\r\n"

print "Ciao!"

 保存して http://localhost:20080/view.cgi にアクセスして、"Ciao!"と表示されれば成功です。print "Ciao!"の部分を好きなように改造して遊んでみるといいと思います。これまで使っていたputsも使えます。

 うまくいかなかった人は以下を試してみてください。うまくいった人にはすみませんが、ここまで含めてプログラミングです。

  • view.cgiのコードに間違いが無いか確認する
  • ブラウザの画面に出るエラーメッセージを確認して手掛かりを探す
  • ruby web.rbを実行した方のウィンドウで、最近出ているメッセージから原因を探す
  • 1行目に間違いが無いか確認する(1行目は絶対に#!で始まらないといけない)
  • 1行目の書き方を変える。C-Cbashに戻って、which rubyコマンドを実行する。実行した結果の文字列を1行目の"#!"の後に入れる。例えばwhich rubyの結果が/usr/bin/rubyだったら1行目は#!/usr/bin/rubyにする

 それでもわからなかった場合や、動いたけどなんで動いたのか気になった場合は、何をしたかも含めて文章でもスクリーンショットでも良いので添えて詳しい人に聞いてみてください。

 動いた人は、それがつまりWebサービスにつながる第一歩です。Twitterやブログなんかもここから始まっています。

倫理的なやつ

 ここで作ったプログラムには何の危険もありませんし、自分以外の誰にもアクセスできません。安全です。というのをわかった上で。

 これでなんらかのプログラムを使ったサービスをインターネットで公開できるようになりました。大抵はそのままではファイアウォールだったりネットワーク設定だったりに阻まれてそのままインターネットに公開というわけにはいきませんが、理論的にはこれで全世界に公開できるサービスを作ることができるようになったということになります。きっとそのために勉強をすればすぐにできるでしょう。

 ただし、ここから先、本当にインターネットに公開しようとすると色々な現実的な問題が発生します。今回はまだ外のシステムにアクセスするようなことはしていませんが、単に公開するだけでも、例えばどこか知らない誰かからの攻撃の危険にさらされます。プログラムに脆弱性があった場合、もしくはプログラムに非は無くとも利用しているWEBrickRubyそのものに脆弱性があった場合、そこを突かれてシステムが乗っ取られたり、不正アクセスのための踏み台にされることがあります。つまり直接的に誰かに迷惑をかけるだけでなく、知らないうちに悪事に加担してしまう恐れまである。それは非常にマズい。いわゆるハッキングとかクラッキングとかいうやつです。

 ということで、何か作って公開したいと思った時は、詳しい人に相談するか、インターネットセキュリティや倫理についてしっかり勉強するなどしてからやるようにしてください。

 実際のところ、悪事を働くための技術や守るための技術の理屈はすごく難しいですが、対策する方法自体はそんなに難しくないことが多いので、「これやっとけば大丈夫よ」くらいのアドバイスで済むことが多いです。

 ただそれはそれとして、クラッキングというやつの仕組みはそこそこ楽しいので、悪用しない前提で勉強するのは楽しいと思います。死ぬほど難しいけどそれなりの需要はあります。

それから

 今回はここで終わりです。今回はこれまでと違って説明を省いた部分が大量にあります。ここから先は、これまでのどこがおもしろかったかで進む方向を決めると良いでしょう。どの方向に行っても1歩目までは大抵サポートできると思います。

 ざっと思いつくのはこんな感じです。

  • もっと色々できるWebサービスを作りたい
  • ここで出てきたURLの意味がわからん(localhostとか:20080とか)
  • コードの中のWEBrick::HTTPServer.newのオプションは一体何(:DocumentRoot => './',とか:BindAddress => '127.0.0.1',とか:Port => 20080)
  • 文字化け直したい
  • trap("INT")の詳細が知りたい
  • WEBrickが何をしているのかを知りたい
  • そもそもrubyみたいなのを作りたい

 これ以外にも色々なことを思うかもしれません。ほとんどの共通部分は同じではありますが、特にどこが気になるかによってかなり分野が違っていたりするので、おすすめする本なども変わってくると思います。私も挙げた分についてはそこそこ知っているものもあるので聞いてみてもいいかもしれません。半分くらいRubyじゃないですけども。

2時間でわかる繰り返し

 今回は繰り返しの話です。

 繰り返しというのは要するに同じことや似たようなことを何回もやる、ということですが、そのやり方はたくさんあります。これはRubyだからということではなく、様々なプログラミング言語で様々な繰り返しの方法があります。その多数の中から状況によって適切なやり方を選択できるということが、プログラミングの上達につながるといって良いでしょう。

 ただしこれは非常に難しい。一流と呼ばれるプログラマでも間違えることが多々ありますし、正解が存在しないということもあります。

 なので、ここではとりあえずどういうパターンがあるのかを見て「そういうものもあるんだな」と思っておく、というところから始めましょう。

無限ループ

 まずは無限ループからです。無限ループというのはそもそもプログラミング用語であり、その名の通り、特定の処理を無限に繰り返します。最も一般的な書き方はこうです。

while true
  puts "ainachan"
end

 これを実行すると"ainachan"と連呼しつづける迷惑なやつになります。C-Cで止めてあげましょう。

 原理としては、マニュアルの「制御構造」のページに書いてあります。whileは、その後に続く式がfalseになるまで永遠に繰り返す文ですが、今回の場合は式の部分にtrueと書き込んでいるので、falseになることはありません。つまり無限に繰り返すということです。

 無限に繰り返して終わらないというのは困ったようにも思えますが、普通のプログラムならまあC-Cを入力すれば終わりますし、終わらないということがそれなりに便利なともあります。

 余談ですが、例えば今まさに使っているブラウザとかエディタのようなGUIアプリケーションは、これまでに作ってきたHelloWorldやMegaGreeter(20分ではじめるRuby)などのようにやることやったらすぐ終了してしまうというのでは困るので、どこかしらで無限ループのようなことをしています。OSとかもそうです。

 ただ、普通は終わらないと困るので、終わらせる方法をC-C以外で用意しておくのが普通です。

 例えば、ループするかどうかを判断する変数つづけるを使います。(変数は実は日本語も使えます)

つづける = true
while つづける
  puts "ainachan"
  つづける = false
end

 これを実行するとどうなるでしょうか。while文は、つづけるtrueの間は無限に繰り返しますが、ainachanと言った後でつづけるfalseになるので、そのままwhile文は終了し、1回しか"ainachan"は出力されません。これだとループの意味は無いので、普通はif文をからめてこういう感じになります。

つづける = true
while つづける
  puts "ainachan"
  if 終了条件
    つづける = false
  end
end

 この終了条件の部分には、「どういう時に終わらせたいか」という条件式が入ります。例はちょっと後で。

無限ループ2

 無限ループの書き方についてもいくつかあります。普通は先ほど説明した通りに書くのが良いのですが、こういうパターンもあります。

puts "ainachan" while true

 今度は1行です。これは「while装飾子」というパターンです。先ほどと同じくマニュアルの制御構造のページのwhileの節の次に書いてあります。これはwhileの後の条件がtrueになるまでwhileの前の文を繰り返すという意味です。基本的には1行を繰り返すのですが、複数の行を1行として扱うbeginendと組み合わせると以下のようになります。

begin
  puts "ainachan"
  puts "anchan"
end while true

 ほとんど最初のwhile true 〜 endと同じように動くように見えますが、while装飾子とbegin endを組み合わせた場合だけちょっと違う動きをします。while装飾子のtruefalseに変えてみてください。

begin
  puts "ainachan"
  puts "anchan"
end while false

 条件式がfalseなのでループは実行されないように見えますが、1回だけ実行されます。「1回は少なくとも実行したい」という時はこのような書き方をします。滅多にありませんが、稀にそう思うことがあります。なにかスッキリしない時に思い出すと良いでしょう。

無限ループ3

 untilというのもあります。これは単にwhileの条件のtruefalseが逆になっただけで、ほとんど同じです。until 式の部分はwhile not 式と書けば全く同じなので実のところあまり見かけませんが、でもなにかそれでは気持ち悪い時に使います。

 Rubyにはそういう「同じだけど名前が違う」とか「ほとんど同じだけどすごく細かいところが違う」という機能がたくさんあります。一番気持ちいいやりかたでやると良いでしょう。

4回繰り返す

 4回繰り返します。まずはこうです。

puts "ainachan"
puts "ainachan"
puts "ainachan"
puts "ainachan"

 4回繰り返しました。あまりにも単純ですが、これはこれで正解になることがあります。開発初期のとりあえずやってみようという段階や動作確認ではこういう感じでもよいことがあります。コピペも有効に使っていきましょう。

4回繰り返す (n回繰り返す)

 「4回」というように、回数が決まっている場合はIntegerクラスのtimesメソッドを使うのが簡単です。

4.times {
  puts "ainachan"
}

 これなら100回にしたくなった時にも4の部分を書き換えればよいので簡単です。

 4の部分を変数にするのも良いでしょう。n回繰り返すパターンは以下です。

n = 4 # 今回は4回繰り返します

n.times {
  puts "ainachan"
}

 nをメソッドのパラメータにすると、指定した回数だけ繰り返すメソッドも作ることができます。今日は4回、明日は10回繰り返したい、という場合などに便利です。

def renko(n)
  n.times {
    puts "ainachan"
  }
end

renko(4)

for文を使って4回繰り返す (timesの別の使い方)

 timesにはブロック({ ... })を指定する以外にも使い方があります。

 例えば、以下のようにfor文といっしょに使えます。

for i in 4.times
  puts "ainachan"
end

 ここで、iは、何回目かという情報が入っています。for文の中でputs iなどして確かめてみるとよいでしょう。0から始まり、1, 2, 3と表示されると思います。このように回数は0から始まりn - 1でおわります。そしてこのfor文のコードは以下と同じです。

4.times {|i|
  puts "ainachan"
}

 timesのブロックは、このようにカウンタiを受け取ることができます。ブロックの中でputs iするとまた同じようになるはずです。

 また、for文のパターンは以下のコードとも全く同じです。そもそも実は、このコードの省略形がfor文です。Rubyにはこのような同じ処理の書き換えパターンがよく登場します。くわしくは「制御構造」のマニュアルに書いてあります。ここではeachが出てきます。

4.times.each {|i|
  puts "ainachan"
}

 IntegerクラスのtimesはブロックをつけないとEnumeratorを返すとマニュアルに書いてあります。Enumeratorというのは「なんか数えられるやつ」くらいの意味です。数字だったりリストだったり、そういう何かです。そしてマニュアルで言うとEnumeratorクラスの中にeachメソッドがあります。eachなのでつまり、「それぞれの要素に対してブロックを実行する」という意味で、これが数字やリストに対して実行すると繰り返しになります。

4回繰り返す (カウンタを使う)

 ここで、現在の繰り返し回数iがわかるということは、iを使って以下のように書くこともできます。

100.times {|i|
  if i < 4
    puts "ainachan"
  end
}

 日本語で説明すると、「100回繰り返し、繰り返し回数が4回未満なら"ainachan"と表示する」ということになります。これはこれでいいのですが、普通は100回も繰り返さずに、4回以上になった時点でやめればよいのです。というパターンが以下のコードです。

100.times {|i|
  if i >= 4
    break
  end
  puts "ainachan"
}

 これが「100回繰り返すが、4回以上になったらやめる」です。ループの中でbreakと書くと、breakが実行された時点でループそのものが終了します。「ループを抜ける」とも表現します。このようにループは途中でやめることもできます。

4回繰り返す (whileとカウンタを使う)

 最初に出てきたwhile文を使って以下のように書くこともできます。

i = 0
while i < 4
  puts "ainachan"
  i += 1
end

1. while文にはカウンタが無いので自分でi = 0と定義します
2. iが4未満の場合に繰り返します
3. "ainachan"って表示
4. iに1を足します
5. 2に戻る

 大事なのは2と4です。4で自分でちゃんと数えてあげる必要があります。4が無いと無限ループします。

 また、無限ループとbreakを使って以下のように書くこともできます。

i = 0
while true
  if i >= 4
    break
  end

  puts "ainachan"
  i += 1
end

 このように、whileの条件式をループ内のif文で代用することもできます。

 ただ、これだと条件がi < 4だったのがi >= 4と逆になっていてちょっとややこしいかもしれません。そういう時のために、前にちょっと紹介したuntilや、ifの逆のunlessというものがあります。試しに書いてみるとこうなります。

# unlessとbreakを使うパターン
i = 0
while true
  unless i < 4
    break
  end

  puts "ainachan"
  i += 1
end
# untilを使うパターン
i = 0
until i >= 4 
  puts "ainachan"
  i += 1
end

 どのやりかたが良いかというのは状況によるとしか言えません。大抵は状況により最適な方法は存在しますが、1つとは限りませんし、確固たる理屈があるわけでもなく経験則という場合も多いです。とりあえずは気持ちの良いものを使いましょう。

3回に1回違うことをする

 3回に1回、"ainachan"じゃなくて"anchan"と表示してみましょう。一番簡単なのはこうだと思います。

puts "ainachan"
puts "ainachan"
puts "anchan"
puts "ainachan"
puts "ainachan"
puts "anchan"

 無限にやるならこうです。

while true
  puts "ainachan"
  puts "ainachan"
  puts "anchan"
end

 ここで考えなおしてみましょう。「3回に1回」ということは、ループの回数を数えてカウンタが3の倍数の時だけ表示を変えればよいのです。その実装が以下のコードになります。

i = 1
while true
  if i % 3 == 0
    puts "anchan"
  else
    puts "ainachan"
  end

  i += 1
end

 %というものが出てきましたが、これはmod演算といって、割り算の余りを出す演算子です。要するにifの条件式は「iを3で割った余りが0ならば」という意味になります。つまり「3の倍数ならば」ということです。前と違ってi = 0ではなくi = 1から始まっているのは、最初の1回目で"anchan"と表示しないためです。ちなみに%の詳細はIntegerクラスのマニュアルにあります。

 余談ですが、10年くらい前にはこのようにループとifを使いこなせてmodの意味がわかっていればプログラマとして就職できるという噂がありました。実際どうだったのかはよくわかりません。

リストの中身に従って繰り返す

 1行目は"ainachan"、2行目は"anchan"、3行目は"mendako"と表示しようと思います。これも繰り返しで表現することができます。要するに「表示する」という部分は同じなので、文字列のリストを作って、それぞれに対してputsしてあげればよいのです。というのが以下のコードです。

["ainachan", "anchan", "mendako"].each {|s|
  puts s
}

 ここで[ ... ]の部分はArrayクラスのメソッドです。この["ainachan", "anchan", "mendako"]の部分で配列(Array: リストのようなもの)を作っています。配列にも前に少し紹介したeachメソッドがあり、ブロックの中でそれぞれに対してputsを実行します。sには配列の中身が1つずつ入ってきます。とりあえず書いてみればなんとなくわかると思います。

 これを使って「3回に1回〜」を実現することもできます。こんな感じです。

["ainachan", "ainachan", "anchan"].each {|s|
  puts s
}

 無限にやるならこれ自体をループしてしまえばよいのです。

while true
  ["ainachan", "ainachan", "anchan"].each {|s|
    puts s
  }
end

 無限の場合は、Arrayクラスのcycleメソッドを使って書くこともできます。cycleは配列に含まれる要素を繰り返すメソッドで、["ainachan", "anchan", "mendako"].cycleのように書くと["ainachan", "anchan", "mendako", "ainachan", "anchan", "mendako", "ainachan", "anchan", "mendako", ...]のような感じになります。要するに以下を実行してみましょう。先程のコードと同じ結果になるはずです。

["ainachan", "ainachan", "anchan"].cycle.each {|s|
  puts s
}

 ということはつまり、以下のコードは一番最初の無限ループのコードと同じ結果になります。

["ainachan"].cycle.each {|s|
  puts s
}

 意味は少し複雑になって、「"ainachan"だけが含まれた配列(["ainachan"])を無限に繰り返して(cycle)、その中身を全て(each)表示する(puts)」ということになります。この場合も、どちらが良いのかは状況次第です。

 もちろん、breakと組み合わせて"mendako"がきたらループを抜けるというようなこともできます。

["ainachan", "anchan", "mendako", "kumachka"].each {|s|
  if s == "mendako"
    break
  end
  puts s
}
end

 この場合は"mendako"と"kumachka"は表示されません。

繰り返しの途中でやりなおしたり次に行ったりする

 breakと似た話で、繰り返しの途中で後の処理をせずに次に行きたいことがあります。例えば、以下のコードです。

["aina", "an", "mendako", "kumachka"].each {|s|
  print s
  if s == "mendako"
    puts
    next
  end
  puts "chan"
}

 このコードの意味は、「文字列が"mendako"の時だけ"chan"を付けない」という意味になります。ここでprintは、改行せずに文字列を表示するメソッドです。それと、putsはうしろに何も指定しないと改行だけするメソッドになります。

 "mendako"のif文の中にnextという命令があります。これは繰り返し(eachのブロック)の中の処理を途中で辞めて次の繰り返しに行く、という意味で、この場合はput "chan"を実行せずに次の"kumachka"の処理に移ります。実行してみるとなんとなくわかると思います。

 このnextと似たものとしてredoがあります。こちらは次に行かずに、今と同じものをもう一度やりなおします。つまり以下のようにnextredoに置き換えたものを実行すると、"mendako"を処理しようとしてredoされて再度"mendako"を処理しようとするので無限ループします。

["aina", "an", "mendako", "kumachka"].each {|s|
  print s
  if s == "mendako"
    puts
    redo
  end
  puts "chan"
}

 redoはあまり使うことはないかもしれませんが、もう一度やればうまくいくという状況があれば思い出してください。リトライ(retry)というのもあって似ていますが、たぶんリトライの方が出番は多いと思います。後で説明します。

範囲にしたがって繰り返す

 Rangeについて書こうと思ったけどあまり変わらないのでやめ。(1..4)(1...5)のように数字の範囲を指定することができます。これらはRangeクラスのオブジェクトで、eachメソッドがあるので、(1..4).each {|i| puts i }とかやってみてください。

再帰

 再帰は少し変わったパターンです。うまく使えばわかりやすくなりますが、なかなか難しい概念ですし、性能も良いとは言えないところがあるのでさほど使われることはありません。でも状況によってはすごく有用なこともあるという、そういうものです。そういうのもあるんだなと思っておくだけでも良いでしょう。

 メソッドは自分自身を呼び出すことができます。これを利用して無限ループを書くことができます。

def ainachan
  puts "ainachan"
  ainachan # 自分自身(ainachanメソッド)を呼び出し
end

ainachan

 ainachanメソッドの中でさらにainachanメソッドを呼び出しています。それだけでは最初の1回目が実行されないので、最後にainachanメソッドを呼び出しています。これを実行すると、無限ループの時と同様に"ainachan"と表示され続けるはずですが、続けているうちに途中でSystemStackErrorみたいなエラーが出て止まると思います。これは実行した環境(状況みたいなもの)によって変わるのでなんとも言えませんが、数秒で止まる人もいれば数時間も止まらない人もいると思います。それはまあそういうものです。

 このエラーは自分自身を呼び出す(これを再帰といいます)回数が多すぎた時に起きます。このようなエラーが発生する可能性があるので、再帰はあまり性能がよくないとされています。一応それを克服する方法もありますが、それなりに特殊なことになるので難しい。

 ここで何が起きているか、何故エラーが起きるかというと、Rubyの外の話も色々と関わってくるので、ここでは説明しませんが、そちらの方に興味があればそういう方向に行くのもアリです。(私はそっちの方が詳しいです)

 実のところ、再帰というのはすごく強力な概念なので、ここで説明している繰り返しのほとんどを再帰を使って書き直すことができます。ただ、エラーが起きることもあるように、Rubyを含む多くのプログラミング言語ではあまり効率が良くないので、かなり限られた状況でしか使われません。逆に言うと再帰で書けるコードのほとんどは他の繰り返しに置き換えることができるのです。必要以上に避ける必要もありませんが、繰り返しで書けるならできるだけ繰り返しで書く方が良いでしょう。

リトライ(と例外)

 繰り返すことの目的のひとつとして、エラーが起きたからやり直すというものがあります。つまりリトライです。redoと似ていますが、こちらはコンピュータの本質的な問題に対処するための仕組みです。

 コンピュータには同じ処理でももう1回やれば成功するかもしれないという事があります。例えば外部のデバイスと通信するときです。

 つながっていないハードディスクに書き込もうとするとエラーになりますが、つないでからリトライすると成功するかもしれません。インターネットにつながっていない時に通信しようとするとエラーになりますが、時間が経って再接続された後だと成功するかもしれません。

 このように、プログラムとしては間違いは無くてもコンピュータそのものの状況(故障など)や世の中の状況(当然ながらインターネットを使うプログラムは世界が滅ぶと使えません)によって実行が不可能になるということがあります。これをRubyでは例外(Exception)と呼んでいます。Rubyではエラーも例外の仲間です。エラーと言った時は例外の話をしていると思って問題ありません。前節で出てきたSystemStackErrorもエラーであり、Rubyでは例外の一種です。

 例題として例外を起こすとなるとそれなりの準備もあって面倒くさいので、サンプルコードでは例外を発生させる命令を使います。例外は、一番簡単なものとしては他のオブジェクトと同じくExceptionクラスを使ってException.newで作ることができます。これだけではただのクラスと同じなので、「発生させる」といった場合にはraise命令を使う必要があります。つまりこうです。

raise Exception.new

 この一行をirbなどで実行してみてください。これまで散々見てきたようなエラーが起きた時みたいな表示が出たらたぶん成功です。本当にエラーだったらなんかごめんなさい。

 このraiseというのは、例外を発生させる命令です。あまり使うことはありませんが、プログラムの中でなにか想定外のことが起きた時に使います。存在しないメソッドを呼んだ時や、想定しない使い方をした時などに発生します。例えば、Integerメソッドにはmendakoメソッドは存在していないので2.mendakoはNoMethodErrorという例外が起きますし、1を0で割った時にどうなるかは誰にもわからないので1 / 0はZeroDivisionErrorが起きます。irbなどで試してみてください。

 ちなみにraiseでは適当な文字を指定することもできます。なにか適当な名前を思いつかない時はエラーメッセージを投げておけばOKです(raiseを使うことを「投げる」と言うことがあります)。つまりこういうことができます。

raise "例外が起きたよ"

 Rubyでは、例外が起きた時に何かをすることができます。そして「例外が起きた時にできる何か」の中にはリトライというものがあります。これを使って無限ループを作ることができます。具体的にはこうです。

begin
  puts "ainachan"
  raise "なんかエラー" # 例外が発生
rescue
  retry
end

 これを実行すると無限ループすることを確認できると思います。

 例外(エラー)が起きそうな場所はbegin ... rescue ... endで囲む必要があります。この中のbegin ... rescueの中でエラーが発生するとrescue ... の間のプログラムが実行されます。この場合はretryです。retryは、rescueに対応するbeginまで戻って、そのbeginのところからやり直しますという命令です。つまりここでは、raiseのところで例外が発生するのでrescueの節が実行されてretryの作用でbeginのところまで戻って再びputs "ainachan"が実行されることになります。

 試しにraise "なんかエラー"の行を削除してみてください。例外が発生しなくなるので1回だけ"ainachan"が表示されて終了すると思います。rescue ... endの節は、その前の節で何事も起きなければ実行されることはありません。

 これが何の役に立つのかというと、例えば通信エラーだった場合にはretryの前に再接続のプログラムを実行すれば良いわけです。あまり使うことは無いかもしれませんが、みなさんが使う通信やファイル読み書きのプログラムではライブラリの中のどこかでこのような処理をしています。機会があったあ思い出してみてください。

おわり

 このように、繰り返しひとつとってもプログラムにはやりかたがたくさんありますし、目的もたくさんあります。もちろんここで説明した意外にも様々なパターンがありますが、少なくともRubyにおいてはほとんどがここで挙げたパターンの組み合わせになると思います。

 「色々なやり方があるんだな」とわかった上で、やり方を探しつつ、時にはより良い方法を探してマニュアルを読んだり先人のコードを読んだり詳しい人(私を含む)に尋ねたりして、良いコードを書けるようにがんばってみてください。

「20分ではじめるRuby」解説 4ページ目

前回: 「20分ではじめるRuby」解説 3ページ目 - yunomuのブログ

最後です。

実行

 まずは3ページ目に戻って、「ファイルに保存して実行」です。

このファイルを“ri20min.rb”という名前で保存して、“ruby ri20min.rb”と 実行しましょう。

 メモ帳でもなんでもいいので、MegaGreeterの書いてあるソースコード部分を全部打ち込みます。別にコピペでもいいです。ただ、打ち込んでみて雰囲気を感じるのも最初は良いものです。ここで「メモ帳は使いにくにな」と思って壮大なテキストエディタ探しの旅に出るのも楽しいかもしれません。WindowsだとEmEditorとかTeraPadなどが有名ですが、そういうRubyに対応したエディタだとソースコードに自動的に色をつけてくれたりして、見やすくなることがあります。

 書いたソースコードを保存します。保存場所は、コマンドラインirbなどを実行しているディレクトリ(フォルダ)がいいでしょう。dirコマンドなどを実行するとファイル一覧が表示されると思いますが、その一覧の中に"ri20min.rb"があればとりあえず成功です。実行しましょう。

C:¥Users¥yunomu> ruby ri20min.rb

こんな感じで。

 例の通りに表示されていれば成功です。なんかエラーが出た場合は、中身を見てみましょう。FileNotFoundErrorならファイル名(ri20min.rb)を間違えているか、保存場所が違います。SyntaxErrorやNameErrorやNoMethodErrorの場合は、どこかに打ち間違いとかがあります。メッセージで出ている場所の近辺を探してみましょう。

コメント

Rubyでは、ハッシュマークで始まる行の内容は コメントになり、インタプリタからは無視されます。

'#'で始まる行は意味が無いので何を書いてもいいよという話です。マニュアルでは、Ruby言語仕様の「字句構造」のところにあります。内容は1行くらいしかありません。

次の一文、

このファイルの最初の行は 特別な行で、Unix系のOSではファイルをどう実行するかをシェルに知らせてくれます。

これは、先ほど書いたソースコード(ri20min.rb)の一行目のこれのことです。

#!/usr/bin/env ruby

 これはUnix系のOSの時だけ関係ある話で、別に無くても構いませんし、Windowsとかだと無意味だったり書き方が違ったりします。まあ書かなくてもいいでしょう。

if, elsif, else, end

 say_hiの中でifを使っていますね。if文です。これは条件分岐といって、「晴れていたら外に出かける。そうでなければ家でゲーム」みたいなやつです。条件分岐が出てくるとかなりプログラミングっぽさが出てきます。

 マニュアルはRuby言語仕様の「制御構造」のページです。条件分岐の節の最初にifの項目があります。とりあえず例をirbかなにかで書いて実行してみるとよいでしょう。制御構造は慣れていても書き方をよく忘れるのでよくお世話になるページのひとつです。

 今まではなんとなく流していましたが、「文法」と書いてあるブロックを見てみましょう。

if 式 [then]
  式 ...
[elsif 式 [then]
  式 ... ]
...
[else
  式 ... ]
end

 これはif文の書き方を示しています。[]で囲まれている部分は、あってもなくても良いという意味です。なのでthenは書いても書かなくても良いということです。面倒なので書かなくてよいです。

 say_hiでは一番複雑な、if, elsif, elseが全部登場するパターンで書かれています。つまり一番シンプルなのは以下のパターンです。式がtrueなら"hello"と出力し、falseなら何もしません。

if 式
  puts "hello"
end

 「式」の部分には、例にあるように、truefalseになる何らかの式が入ります。age >= 12ageが12以上ならtrueということです。true(真)false(偽)になる値のことを真偽値(bool)と言います。

 もちろん>=にもマニュアルがあります。これはIntegerのメソッドなので、Builtin librariesのIntegerクラスのページにあります。

self >= other -> bool
比較演算子。数値として等しいまたは大きいか判定します。

 これは、age >= 12の例で説明すると、self(age: Integerの数値)がother(12: これもIntegerの数値)以上どうかを真偽値(bool)で表すという意味で、正しければtrueという意味になります。要するに数学の"≧"です。

 elsifは、おそらく"else if"の略で、最初のifの条件で引っかからなかった場合にもう一度条件を書く場合に登場します。例えばこのように使います。

if age < 12
  # ageが12より小さい時の処理
elsif age < 15
  # ageが12より小さくなくて、15より小さい時
elsif age < 18
  # ageが12より小さくなくて、15より小さくなくて、18より小さい時
else
  # それ以外
end

 このように、elsifはいくつでも書くことができます。elseはそれ以外の場合がある時に1つだけ書くことができます。elsifelseも省略しても構いません。

演算子

 ついでに、演算子式について。

 メソッドの中には演算子と呼ばれるものがあります。演算子メソッドは特別な書き方ができます。例えば、+はIntegerのメソッドですが、普通のメソッドのように1.+(2)と書くこともできますが、1 + 2と書くこともできます。これが実は特別な話だったのです。

 演算子といえば、一般的には+, -, *, /などがありますが、Rubyには他にも色々あります。

 そのあたりのマニュアルは、Ruby言語仕様の「演算子式」のページにあります。

@names.nil?

 say_hiのif文の式のところに書いてある@names.nil?とはなにか。@namesは一旦initializeの中で定義されている変数だと思ってください。その上で@names.nil?は、Builtin librariesのObjectクラスのページに説明があります。

nil? -> bool
レシーバが nil であれば真を返します。

 つまり、@names == nilと同じ意味です。ここでnilというのは、無です。他の言語ではnullだったりします。つまり@names.nil?@namesが無(nil)だった場合にtrueになります。

 さらにnilとは何か。nilはNilClassのオブジェクトです。Builtin librariesのページからNilClassのマニュアルを探して見てみましょう。メソッドのマニュアルを見てみると、おおむね虚無っぽい挙動をしていることがわかると思います。

 nilはどのような時に使うのか。それは、変数を作りたいけどまだ具体的な値を入れたくない時に使います。例えば以下のコードのように、tallの値によって内容を変えたい時などです。

a = nil # 変数aを作りたいが中身がまだ決まっていない。
if tall > 150
  a = "anchan"
else
  a = "ainachan"
end
puts a

 ただしこの場合は、if文が値を返すので以下のように書いた方が良いです。(if文のマニュアルを確認してください)

a = if tall > 150
  "anchan"
else
  "ainachan"
end
puts a

 でも似たような場合でもa = nilと書いてから何かやった方が便利な時もあるので、そういう時のためにnilというものが存在します。プログラミングはひとつのことをやるためにいくつもの答えが存在します。その中から適切なものを選ぶために、良い例も良くない例も一応見ておきましょう。上の例でどちらがいいのかは、ちょっこれだけではなんとも言えないというのが正直なところです。

最後に'?'がついているメソッド

 マニュアルをざっと見てまわった方はお気づきかもしれませんが、メソッドの中には'?'で終わるメソッドがあります。@names.nil?もそうですし、例えばIntegerには結構たくさんあります。

 Integerクラスのマニュアルを見てみましょう。インスタンスメソッドの項目にeven?odd?があります。これは偶数か奇数かを判定するメソッドです。他にも、Numberから継承したメソッドの項目にはpositive?negative?なんかもあります。意味はマニュアルを見ればわかるでしょう。継承についてはまた別に説明します。

 '?'がついているメソッドには共通の特徴があります。「bool値を返す」ということです。つまり'?'がついたメソッドはtruefalseの値を返します。

 これは厳密にはルールではないのですが、お約束です。「'?'がついているメソッドではbool値を返すようにしよう」という暗黙の約束がRubyの世界にはあります。別に守る必要は無いのですが、守った方がみんなわかりやすくて便利だよねということです。Rubyに限らずプログラミング言語にはこういうお約束が時々あります。会社やチームによって決められることもあります。特に守る必要も無いのですが、一人の時は自分ルールを持っておくと後で助かることもあるかもしれません。チームにルールがある場合はそちらに従うと良いでしょう。そういうあいまいなことも時々あります。

おわり

 「20分ではじめるRuby」の4ページ目が、最初のsay_hiメソッドの途中で終わりました。次回はつづきで、4ページ目その2という感じで続けていきます。その3くらいまでいきそうな気がしますがよろしくお願いします。

「20分ではじめるRuby」解説 3ページ目

前回: 「20分ではじめるRuby」解説 2ページ目 - yunomuのブログ

オブジェクトを作る

それではgreeterオブジェクトを作り、使ってみましょう。

から始まります。最初から飛ばしていますね。

 2ページ目の最後にclass Greeterを書きましたね。これがクラスだという話でした。クラスとは何か。クラスとは、「整数」とか「文字列」のような、値の形のようなものです。「型」と言う時もあります。

 1ページ目の時に「整数(Integer)」のマニュアルを見ましたが、そのIntegerもクラスです。マニュアルのBuiltin librariesのページで「クラス」の節の中からIntegerを探したのを覚えているでしょうか。Integerはクラスなので、マニュアルのクラスの節に書かれています。逆に、クラスの節に書かれているものは全てクラスなので、ここからの説明はおおむねあてはまります。そしてクラスはここに並んでいる以外にも自分で作ることもできます。つまりその作ったものがGreeterです。

Greeter.newのところから見てみましょう。

irb(main):035:0> greeter = Greeter.new("Pat")

 このnewというのは、クラスを元にオブジェクトを作るというメソッドです。オブジェクトというのは、クラス(型)に対する値のことで、つまりIntegerクラスのオブジェクトが23で、Stringクラスのオブジェクトが"あいなちゃん"とか"anchan"とかになるイメージです。わかりますか。

 IntegerやStringはリテラル(前回出てきた)でオブジェクトを作れますが、それ以外のほとんどのクラスではnewメソッドを使ってオブジェクトを作ります。

 newメソッドのマニュアルはどこにあるかというと、おおむねほとんどのクラスにそれぞれあります。Stringにもあります。適当なクラスのマニュアルを探して見てみましょう。「特異メソッド」の項目にあるはずです。ただし、Integerにはnewはありません。例外的にそういうクラスもあります。例外というのはどこにでもあります。そういうものだと思っておきましょう。

 その上でGreeter.new("Pat")が何をしているか。newは実は2ページ目で書いたinitializeを呼び出しています。ここの部分↓

irb(main):025:1>   def initialize(name = "World")
irb(main):026:2>     @name = name
irb(main):027:2>   end

 その後に出てくるgreeter.say_higteeter.say_byeはそのままsay_hisay_byeを呼び出していますが、newだけはinitializeを呼び出します。これはnewが特別だからで、その件についてはObjectクラスのマニュアルの中に書かれています。ややこしいですが、オブジェクトという用語とは別にObjectという名前のクラスが存在します。Builtin librariesのページのクラスの節からObjectのマニュアルを見てみましょう。

 「privateメソッド」と書かれている節にinitializeがあると思います。ここに、要するにここで説明したようなことが書いてあります。newを実行するとinitializeが実行されます。何故そんなことになっているのか気になる場合は聞いてくれれば記事が増えます。

SyntaxError

irb(main):038:0> greeter.@name
SyntaxError: (irb):38: syntax error, unexpected tIVAR, expecting '('

 これはsyntax、つまり文法のエラーです。Rubyとしての書き方がなにか間違えている時に出るエラーです。エラーメッセージでは「'('がくるはずだったのになにかおかしい」というようなことが書かれていますが、そもそもRubyの規則に則っていないのでSyntaxErrorの場合のメッセージはあまり当てにならないことが多いです。'('や')'の書き忘れというパターンが多い気がします。がんばって探しましょう。

Objectの殻の中

 ここではGreeter.instance_methodsを使って呼び出すことができるメソッドの一覧を出しています。この後、Greeter.instance_methods(false)falseを渡して見やすくしていますが、その前の大量に出てきた出力は何か。それこそが、マニュアルのクラスObjectのページで書かれていたメソッドたちです。

引数falseを渡します。 これは祖先のクラスで定義されたメソッドが不要であることを意味します。

 と説明にあります。この「先祖のクラスで定義されたメソッド」という言葉の中の「先祖のクラス」というのがつまりObjectクラスです。Objectクラスは自分で定義したものも含めて全てのクラスの先祖になります。自動的になります。Rubyではそういうルールになっています。そのことは、Objectクラスのマニュアルの「要約」の節に書かれています。これはそういう意味です。

クラスの変更 - まだ間に合います

 ここではattr_accessorが出てきます。このマニュアルはBuiltin librariesのクラス節のClassのページにあります。クラスそのものもオブジェクトで、言わばClassクラスのオブジェクトなのです。これが重要になるのは何か既存のものの動きを書き換えたいとかそういうマニアックな欲求がある時なので、そういうのも後でいいでしょう。Rubyはなんでもアリなところがあるのでこういう機能はちょいちょい出てきます。

MegaGreeterあたりのこと

 ここで急に「ファイルに書いてみましょう」と言われて驚いたかと思います。その前にひとつ。

IRBを抜けるには、“quit”や“exit”とタイプするか、コントロールキーを押しながらDキーを押します。

 ここで出てくる"quit"はIRBの特殊なコマンドですが、exitRubyの機能です。マニュアルで言うとBuiltin librariesのモジュールKernelにあります。前回(2ページ目)の記事で解説したKernelモジュールです。ここにexitも「Rubyプログラムの実行を終了します。」と書いてあります。

 それともうひとつ、「コントロールキーを押しながらDキーを押します。」について。これは専門用語で、Ctrl-DとかC-Dとか^Dとも書きます。「ここでデータは終わりですよ」という意味です。IRB以外のコマンドでも使えることがあるのでたまに思い出すといいかもしれません。

 これとは別に「コントロールキーを押しながらCキーを押す」というのもあります。Ctrl-CとかC-Cとか^Cとも書きます。これは制御不能になった時に押します。具体的には無限ループになってしまった時などです。

 以下のコードをIRBで実行してみましょう。

irb(main):011:1* while true
irb(main):012:1*   puts "ainachan"
irb(main):013:0> end

 これが"ainachan"連呼野郎です。止まりません。制御不能です。なのでC-Cで止めます。止まりましたよね? 止まらなかったらごめん。どうしようもないわ。

おわり

 MegaGreeterについては次回でよろしく。

次: 「20分ではじめるRuby」解説 4ページ目 - yunomuのブログ

「20分ではじめるRuby」解説 2ページ目

前回: 実際のところ20分ではじまるかもしれないけどよくわからないよねRuby - yunomuのブログ

つづき。「20分ではじめるRuby」の今回は2ページ目についてです。

今回もマニュアルを見ながらいきましょう。

docs.ruby-lang.org
www.ruby-lang.org

メソッド定義

 メソッドを定義しましょう。こういう感じのやつです。

def hi(name)
  puts "Hello #{name}!"
end

 まず「メソッド」というのは、これまで機能とか関数とか言っていたやつのことです。専門的には少し違いがありますが、Rubyのマニュアル上ではほとんど同じような意味で書かれているので、だいたいメソッドとか関数とか言っておけば通じると思います。前回出てきたputsMath.sqrtや、四則演算の+*なんかもメソッドです。

 定義というのは、「書く」くらいの意味です。別に「作る」でもよいです。

 メソッド定義のやり方は、前回の組み込みライブラリの話とは違い、リファレンスマニュアルのトップページの「Ruby言語仕様」の節にあります。Rubyの文法の「クラス/メソッドの定義」のところです。その先のページの「メソッド定義」の節で書き方が説明されています。例がいくつかありますが、今回の例に近いのはhellofooが出てくるやつだと思います。メソッド定義のやりかたを忘れたらこのページに戻ってきましょう。気になる例を自分で書いてみるのもいいかもしれませんが、結構高度なものもあるのでまだよくわからないかもしれません。機会があれば別に説明します。

「Stringに穴を開ける」?

 表現が謎ですが、先程のhiメソッドでnameを文字列に埋め込んで"Hello #{name}!"としていたやつについて。やってみておわかりのとおり、埋め込んでいます。この埋め込みは、文字列の機能だからStringの機能かと思いきや、実は違って、これも「Ruby言語仕様」の中にあります。「リテラル」の項目です。

 このように、機能が見つからない時はもしかしたら言語仕様の中にあるかもしれないというのはRubyではよくあるので、「そういうものか」くらいに思っておいてください。

 リテラルとは、プログラムの中に書かれた値そのものです。具体的には"Hello World"という文字列とか、2とか3とかの数値とかのことです。例えば文字列の場合、"'で囲むとRubyプログラムの中で使える文字列になります。数字はそのまま書いただけで数値になります。こういうルールや仕組みを「リテラル」と呼びます。

 このマニュアルのリテラルのページには、どういう書き方をすると文字列や数値として認識されるのか、とか、例に出てきたような埋め込みの書き方などが書かれています。具体的には、埋め込みは「文字列リテラル」の節の「式展開」の項目に書かれています。

 文字列リテラルについては、ちょっと面白いのは式展開の前の項目の「バックスラッシュ記法」です。例えば以下のような文字列を表示したい場合

よっしゃよっしゃ
わっしょい

 つまり、改行をしたい場合にどうするか。putsは必ず最後に改行するのでputsを2回書いてもよいです。

puts "よっしゃよっしゃ"
puts "わっしょい"

 でもこれをバックスラッシュ記法を使うと1行でも書けます。

puts "よっしゃよっしゃ\nわっしょい"

 このように、\nを書いた部分が改行になります。文字列を"よっしゃよっしゃ\nわっしょい\a"にすると、表示は変わりませんがなんか「ピ」とか音が鳴るかもしれません。環境によっては鳴らないかも。音はともかく、改行は便利なので覚えておくといいと思います。

変数について

 そういえば、前回の「20分ではじめるRuby」1ページ目の最後に変数が出てきていました。こういうやつです。

irb(main) > a = 3 ** 2

 計算結果をaに格納して使い回すことができるやつです。変数は数値や文字列と同じように関数に渡したり(puts a, hi(a))、文字列に埋め込んだり("Hello #{a}")できます。

 変数については、Ruby言語仕様の「変数と定数」の項目にあります。前回出てきたのはこのページの中の「ローカル変数」というやつです。見てみると「名前は小文字で初めましょう」とか書いてあると思います。ここには主にそういうルールと、あとスコープについて書かれています。スコープというのは、その変数を使うことができる範囲のことです。定義する前に使ってはいけないとか、有効になってない場所でも使えないとか、そういう話です。詳しくはサンプルコードを動かしてみてください。

 この「変数と定数」のページでは、他にも色々な種類の変数を扱っています。書き方が変わるとルールやスコープが変わってきます。そのうち出てくるので新しいものを見かけたらまたこのページに戻ってきてもいいでしょう。「20分ではじめる」でもひととおり出ているので、このページのサンプルコードを書いてみるというのもいいかもしれません。

Greeterあたりのこと

 ここでclassが出てきます。最初からやってきて、このあたりでよくわからなくなった人も多いんじゃないでしょうか。実際、クラスを書くという時はプログラムが結構大きくなった時とか大きいものを作ろうとしている時が多いので、覚えてもしばらくはあまり登場機会は無いかもしれません。ただ、それなりのものを作ろうという時や、既存のプログラムを改造する時などには必ず出てくるので、一応やっておいてもいいでしょう。

 書き方などは、言語仕様の「クラス/メソッドの定義」のページです。メソッドの時に出てきたページです。結構色々できるんですが、それはまあ追い追い。

 ここで@nameというのが出てきますね。これはインスタンス変数といって、これも言語仕様の「変数と定数」のページで出てきます。クラスの中でだけ使える変数です。説明は次のページでちょっとありますね。

おわり

 これで2ページ目もおわりです。意外と書くことが多いですね。

つづき: 「20分ではじめるRuby」解説 3ページ目 - yunomuのブログ