ほぼ新入社員といっていい会社の後輩から、「Excelの先生を探しています」と相談された。最近は学生時代にしっかりCSを学んでいたり、趣味でプログラミングをやっている新人君も多いが、彼女はそういうバックグラウンドを持っていないタイプだ。うちの会社ではそういう人材は普通である。実際、ギークばかりを採ってもうちの仕事がはかどるってわけでもないのだ。
2022年になっても、Excelの需要は高い。バグの傾向分析をしたり、プロジェクトのメンバーリストを元に所属会社別の人数を数えたり、いかにも新入社員に回ってきそうな雑用というのはたくさんあるものだ。そういうものはだいたいがみんなExcelを使ってやっている。彼らはプログラミングというのは納品するためのアプリケーションをつくるためにやるもので、その他の日常の細かなことはExcelの関数でやるものだと考えている。どうしようもなくなるとExcel VBAを担ぎ出すが、それを「プログラミング」だとは思っていない。なぜなら、Excel VBAの抽象化されたRangeオブジェクトの操作は、彼らが思っているプログラミングとどうも似ていないからだ。
私は一時期かなりExcel VBAを書いたし(プロジェクトで雑用に使える許可されたプログラミング環境がそれしかない時代があったのだ)、WindowsのCOMは偉大な発明であり、あんな複雑なものがちゃんと動いて使えているのはマイクロソフトが成し遂げた一種の奇跡みたいなものだと思っているので、割と好きでもある。ただ、さすがにベースとなる言語であるVBAで文字列操作をするのは大分泣きそうだし、2022年にはもっとよい言語がたくさんあるのでアレで仕事をするのはだいぶキツい。
そのようなExcel VBAをプログラミングのバックグラウンドがない新入社員が最初のプログラミング環境として学ぶとそれは大混乱をするだろうし、不幸だろうと思う。もうちっと下地をつくってからであれば、Excel VBAのリファレンスも読みやすくなるに違いない。それに、バグの傾向分析をやるならちょっとしたスクリプト言語の方が何倍も楽ちんだ。というわけで、世界で一番学習しやすい言語であるRubyを真面目に教えてみることにした。
Rubyのことなら内部の仕組みまで含めてある程度わかっている自信があるのだが、2022年の新入社員にRubyを学ぶと何に困難を覚えるのかはさっぱりわからない。私が教材を作るよりは、定評のある本を選ぼう。幸い、彼女は英語に不自由しないので(最近の新人君は間違いなく我々より優秀だ)、会社が金を払ってくれているOreilly Safariで読み放題の中から「Head First Ruby」をやってみることにした。「Head First xxx」シリーズは翻訳をいくつか読んだことがあって信頼がおけるブランドだし、Amazonの★も4以上だ。きっと初心者をはじめてのプログラミングの世界に誘ってくれるに違いない。たぶんな。まあ、ちょっと今年はトラブルプロジェクトに巻き込まれていて、教材を作る時間がないんだ。
というわけで、これを週に1章ずつやるぞということにした。これが新人君にとってキツいのかどうかもわからん。やってみながら考える。電子書籍はページ数がないから分量もよくわからないんだなあ。それにしたって、自分も一応読んでおく必要があるので、いまから1章を読む。そして、ハンズアウト部分をやった結果をこの後に貼る。ただ、まあ、1章は見るべきものはなんもないかな(笑)
Get Ruby
> ruby -v ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [arm64-darwin21] > ruby ^Cruby: Interrupt
うん、Rubyインタプリタはちゃんといる。本ではバージョンが2.0.0だ。結構古いぞ。
Use Ruby
> echo 'puts "hello world"' > hello.rb > cat hello.rb ───────┬──────────────────────────────────────────────────────────────────────────────────────────────────────────────── │ File: hello.rb ───────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────── 1 │ puts "hello world" ───────┴──────────────────────────────────────────────────────────────────────────────────────────────────────────────── > ruby hello.rb hello world
伝統を重んじている。
Use Ruby - interactively
irbは大事だ。
> irb irb(main):001:0> 1 + 2 => 3 irb(main):002:0> "Hello".upcase => "HELLO" irb(main):003:0> exit >
Math operations and comparisons
Rubyを電卓として使おう
> irb irb(main):001:0> 1 + 2 => 3 irb(main):002:0> 5.4 - 2.2 => 3.2 irb(main):003:0> 3 * 4 => 12 irb(main):004:0> 7 / 3.5 => 2.0 irb(main):005:0> 2 ** 3 => 8 irb(main):006:0> 4 < 6 => true irb(main):007:0> 4 > 6 => false irb(main):008:0> 2 + 2 == 5 => false
Strings
文字列だ。
irb(main):009:0> "Hello" => "Hello" irb(main):010:0> 'world' => "world"
Variables
+=
はいきなり理解出来るものだろうか・・・?
irb(main):011:0> small = 8 => 8 irb(main):012:0> medium = 12 => 12 irb(main):014:0> small + medium => 20 irb(main):015:0> pie = "Lemon" => "Lemon" irb(main):016:0> pie = 3.14 => 3.14 irb(main):017:0> number = 3 => 3 irb(main):018:0> number += 1 => 4 irb(main):019:0> number => 4 irb(main):020:0> string = "ab" => "ab" irb(main):021:0> string += "cd" => "abcd" irb(main):022:0> string => "abcd"
Everything is an object!
Rubyを最初に学ぶと「すべてがオブジェクト!」と言われても、驚きがあるわけでもないよね
irb(main):023:0> "Hello".upcase => "HELLO" irb(main):024:0> "Hello".reverse => "olleH" irb(main):025:0> 42.even? => true irb(main):026:0> -32.abs => 32
Calling a method on an object
EXERCISEがある。やっとこう
irb(main):001:0> 42 / 6 => 7 irb(main):002:0> 5 > 4 => true irb(main):003:0> name = "Zaphod" => "Zaphod" irb(main):004:0> number = -32 => -32 irb(main):005:0> name.upcase => "ZAPHOD" irb(main):006:0> number.abs => 32 irb(main):007:0> "Zaphod".upcase => "ZAPHOD" irb(main):008:0> -32.abs => 32 irb(main):009:0> name.reverse => "dohpaZ" irb(main):010:0> number += 10 => -22 irb(main):011:0> name.upcase.reverse => "DOHPAZ" irb(main):012:0> rand(25) => 0 irb(main):013:0> rand(25) => 9 irb(main):014:0> rand(25) => 6 irb(main):015:0> name.class => String irb(main):016:0> number.class => Integer irb(main):017:0> name * 3 => "ZaphodZaphodZaphod"
これだけでも語るべきことはいくつかある気がする。
Rubyではほとんどすべてがメソッド呼び出しだ。
irb(main):018:0> 42./(6) => 7
変数への代入は違うけど。
irb(main):019:0> number.=(-32) /Users/tambara/.rbenv/versions/3.1.2/lib/ruby/3.1.0/irb/workspace.rb:119:in `eval': (irb):19: syntax error, unexpected '=' (SyntaxError) number.=(-32) ^ from /Users/tambara/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/irb-1.4.1/exe/irb:11:in `<top (required)>' from /Users/tambara/.rbenv/versions/3.1.2/bin/irb:25:in `load' from /Users/tambara/.rbenv/versions/3.1.2/bin/irb:25:in `<main>'
まあ、難しいことは後で出てくるだろう・・・
Let’s build a game
伝統に則って、数当てゲームを作るようだ。ゲームの仕様を英語で読み取るのが難しいが・・・この8つを満たすプログラムをつくるぞと。
- 名前を入力してもらって、挨拶する
- 1から100までの数字をランダムで決める
- プレイヤーの挑戦回数を記録する。10回で当てなきゃいけない
- プレイヤーに数字を入力させる
- プレイヤーの入力した数字と正解を比較して、高いとか、低いとか伝える
- 一致してたら正解だと伝え、挑戦回数も教える
- 挑戦回数を使い果たしたら、正解の番号を教える
- 正解するか、挑戦回数を使い果たすまで繰り返す
伝統に則ってるなあ・・・
Running scripts
ここからはスクリプトを書かせる。
# Get My Number Game # Written by: Tambara puts "伝統と格式の数当てゲームにようこそ" print "お名前をどうぞ: " input = gets puts "#{input}さん、いらっしゃい"
> ruby get_number.rb 伝統と格式の数当てゲームにようこそ お名前をどうぞ: たんばりん たんばりん さん、いらっしゃい
いらんところに改行はいったー。chomp
しておかなければならないな。
まあ、それは後で。
Comment
コメントというものがあるねと。
"puts" and "print"
なんで2つあるのか不思議に思うかもしれない。
そして、なんでputsはレシーバーを取らないのだと書いてある。でもそれは2章でやるそうだ。
Method arguments
メソッドには引数をつけられる。
puts
はいくつでも引数をつけられるのだ。
first line => nil irb(main):002:0> puts "second line", "third line", "fourth line" second line third line fourth line => nil
"gets"
後ろの改行も入っちゃうぜー
hoge => "hoge\n"
Parentheses are optional on method calls
メソッドに括弧つけるか問題
irb(main):004:0> puts("one", "two") one two => nil irb(main):005:0> puts "one", "two" one two => nil irb(main):006:0> gets hoge => "hoge\n" irb(main):007:0> gets() hoge => "hoge\n"
しかし、Rubyistはメソッドが引数を取らない場合には「断固として(adamant)」括弧はつけないのだと書いてある。うむ。
String interpolation
interpolateは「補間」らしいが、日本語にしてもわからない。るりまはこれを「式展開」と呼んでいる。
irb(main):008:0> puts "The answer is #{6 * 7}" The answer is 42 => nil
シングルクォートでくくると展開されない。
irb(main):009:0> puts 'The answer is #{6 * 7}' The answer is #{6 * 7} => nil
ここの節のコラムで
- 行末のセミコロンとかいらんの?
- mainメソッドとかいらんの?
という質問が取り上げられていて、答えは両方、「めんどくさいからいらん」である。うむ。
What’s in that string?
あ、さっき私がひっかかった入力してもらった名前の最後に改行が入ってる問題がここで取り上げられる
Inspecting objects with the “inspect” and “p” methods
p
メソッドは私がRuby初心者のときに不思議に思ったものの1つ。なぜなら、当時(まだ20世紀の話だ)出たばっかりのバイブルでpの説明はなかなか出てこないのに、みんなruby-list(Rubyのメーリングリスト)では使われまくりだったので。
というわけで、ここでp
とセットでinspect
も紹介される。良いね。
Escape sequences in strings
そして、p
を説明したら、エスケープシーケンスの話もする必要があるということ。構成が考えられてる。
そして、シングルクォートとダブルクオートの文字列の使い分けが説明されている。
Calling “chomp” on the string object
ここでchomp
の説明がくると。
What methods are available on an object?
呼べるメソッドの確認をしてる。どんなオブジェクトにもmethods
メソッドがある。
「るりま」の場所はここで教えた方がいいかもしれない。
Generating a random number
次はランダムな数を得るところを作る。
# Get My Number Game # Written by: Tambara puts "伝統と格式の数当てゲームにようこそ" print "お名前をどうぞ: " name = gets.chomp puts "#{name}さん、いらっしゃい" puts "---" puts "1から100までの数字を用意します" puts "当てられるかなー?" target = rand(100) + 1 puts "target = #{target}"
実行するとこんな感じ
> ruby get_number.rb 伝統と格式の数当てゲームにようこそ お名前をどうぞ: だるま だるまさん、いらっしゃい --- 1から100までの数字を用意します 当てられるかなー? target = 6
Converting to strings
文字列への変換について
irb(main):001:0> num_guesses = 0 => 0 irb(main):002:0> remaining_guesses = 10 - num_guesses => 10 irb(main):003:0> puts remaining_guesses + " guesses left" (irb):3:in `+': String can't be coerced into Integer (TypeError) from (irb):3:in `<main>' from /Users/tambara/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/irb-1.4.1/exe/irb:11:in `<top (required)>' from /Users/tambara/.rbenv/versions/3.1.2/bin/irb:25:in `load' from /Users/tambara/.rbenv/versions/3.1.2/bin/irb:25:in `<main>'
エラーメッセージの意味がわかりづらいというのはあるだろうなと思う。これを強制的に変換してしまう言語もあるがRubyはそうしない。
Pythonはどうだっけな。
> python Python 3.10.6 (main, Aug 14 2022, 16:05:52) [Clang 13.1.6 (clang-1316.0.21.2.5)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> num_guesses = 0 >>> remaining_guesses = 10 - num_guesses >>> print(remaining_guesses + " guesses left") Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for +: 'int' and 'str'
しないね。
というわけで、to_s
する
irb(main):004:0> puts remaining_guesses.to_s + " guesses left" 10 guesses left => nil
ただし、puts
やprint
は渡したら変換してくれるから、任せたらいいのでは・・・あ、改行が・・・
irb(main):005:0> puts remaining_guesses, " guesses left" 10 guesses left => nil
こうする方法はある(もちろん2行にわけて書いていい)。
irb(main):009:0> print remaining_guesses; puts " guesses left" 10 guesses left => nil
Ruby makes working with strings easy
つか、式展開つかえやと。違いない
irb(main):010:0> puts "#{remaining_guesses} guesses left" 10 guesses left => nil
Converting strings to numbers
入力された文字列を数字にする必要がある。to_i
使うだけ。
Conditionals
if文が出てくる。true
とfalse
が出てくる。&&
と||
も出てくる。
というわけで、ここまでのところを含めてコードはどうなったかというとこんな感じと。
# Get My Number Game # Written by: Tambara puts "伝統と格式の数当てゲームにようこそ" print "お名前をどうぞ: " name = gets.chomp puts "#{name}さん、いらっしゃい" puts "---" puts "1から100までの数字を用意します" puts "当てられるかなー?" target = rand(100) + 1 num_guess = 0 guessed_it = false puts "あなたには、#{10 - num_guess}回のチャンスが残っています" print "正解だと思う数字をどうぞ: " guess = gets.to_i if guess < target puts "うーん、あなたの答えは小さすぎるようです" elsif guess > target puts "うーん、あなたの答えは大きすぎるようです" elsif guess == target puts "#{name}さん、大正解!" puts "あなたは#{num_guess}回目で正解出来ました" guessed_it = true end if not guessed_it puts "残念。あなたは正解出来ませんでした(答えは#{target}でした)" end
実行する。まだ1回しか応えられないので、クソゲーである。
> ruby get_number.rb 伝統と格式の数当てゲームにようこそ お名前をどうぞ: あらた あらたさん、いらっしゃい --- 1から100までの数字を用意します 当てられるかなー? あなたには、10回のチャンスが残っています 正解だと思う数字をどうぞ: 50 うーん、あなたの答えは大きすぎるようです 残念。あなたは正解出来ませんでした(答えは42でした)
The opposite of “if” is “unless”
if not guessed_it
はダサいのでunless
の説明。unlessはあんまりほかの言語で見たことがない気がするけど、思っているより頻繁に使う。特にガード節とかで。
Loops
while
の説明。until
も説明してくれるけど、unless
と違ってこっちはまず書かない。
しかし、この例では使うのである。おおお。
というわけで、コードはこうなった。
# Get My Number Game # Written by: Tambara puts "伝統と格式の数当てゲームにようこそ" print "お名前をどうぞ: " name = gets.chomp puts "#{name}さん、いらっしゃい" puts "---" puts "1から100までの数字を用意します" puts "当てられるかなー?" target = rand(100) + 1 num_guesses = 0 guessed_it = false until num_guesses == 10 || guessed_it puts "あなたには、#{10 - num_guesses}回のチャンスが残っています" print "正解だと思う数字をどうぞ: " guess = gets.to_i num_guesses += 1 if guess < target puts "うーん、あなたの答えは小さすぎるようです" elsif guess > target puts "うーん、あなたの答えは大きすぎるようです" elsif guess == target puts "#{name}さん、大正解!" puts "あなたは#{num_guesses}回目で正解出来ました" guessed_it = true end end unless guessed_it puts "残念。あなたは正解出来ませんでした(答えは#{target}でした)" end
Let’s try running our game!
動かしてみようってことで、この章は終わり。
> ruby get_number.rb 伝統と格式の数当てゲームにようこそ お名前をどうぞ: たんばりん たんばりんさん、いらっしゃい --- 1から100までの数字を用意します 当てられるかなー? あなたには、10回のチャンスが残っています 正解だと思う数字をどうぞ: 50 うーん、あなたの答えは大きすぎるようです あなたには、9回のチャンスが残っています 正解だと思う数字をどうぞ: 30 うーん、あなたの答えは大きすぎるようです あなたには、8回のチャンスが残っています 正解だと思う数字をどうぞ: 20 うーん、あなたの答えは小さすぎるようです あなたには、7回のチャンスが残っています 正解だと思う数字をどうぞ: 25 うーん、あなたの答えは大きすぎるようです あなたには、6回のチャンスが残っています 正解だと思う数字をどうぞ: 23 うーん、あなたの答えは大きすぎるようです あなたには、5回のチャンスが残っています 正解だと思う数字をどうぞ: 22 たんばりんさん、大正解! あなたは6回目で正解出来ました