KMCの新入生向けラピッドゲームコーディング祭りのための記事
Rubyリファレンスの要約
リファレンス: http://docs.ruby-lang.org/ja/2.3.0/doc/index.html
対話環境(REPL)には**pry**を用いる
Ruby 2.3を前提にします
はじめに
Rubyは動的型付け言語で, 変数宣言が不要
pry(main)> hoge = 1 # 数値を代入
=> 1
pry(main)> hoge.class
=> Fixnum
pry(main)> hoge = "str" # 文字列を代入して上書き
=> "str"
pry(main)> hoge.class
=> String
pry(main)> piyo
NameError: undefined local variable or method `piyo' for main:Object
文法
変数と定数
ローカル変数 hoge
小文字から始まるとローカル変数
関数宣言の内側から参照できない
関数宣言とクラス宣言だけがスコープを作る(制御構造は作らない)
pry(main)> local_variable = 10
=> 10
pry(main)> def test; puts local_variable; end
=> :test
pry(main)> test # testを呼び出す
NameError: undefined local variable or method `local_variable' for main:Object
グローバル変数 $hoge
$から始まるとグローバル変数
pry(main)> $global_variable = 10
=> 10
pry(main)> def test; $global_variable *= 20; end
=> :test
pry(main)> test # testを呼び出す
=> 200
pry(main)> $global_variable
=> 200
pry(main)> $undefined_variable # 未定義はnil
=> nil
rubyに-wオプションをつけて起動すると未定義のグローバル変数でWarningを出してくれる。
定数 Hoge
大文字から始まると定数
再代入をすると警告が出るだけ
pry(main)> ConstVariable = 10
=> 10
pry(main)> ConstVariable = 20
(pry):59: warning: already initialized constant ConstVariable
(pry):57: warning: previous definition of ConstVariable was here
=> 20
リテラル
-
nil,false,true,self - 整数 Integer : 10進
123,1_000_000_007, 16進0xffff, 2進0b1011, 8進0377 - 浮動小数 Float :
123.451.2e-3 - 有理数 Ratioal :
42r - 複素数 Complex :
42i - 文字列 String :
"string\n"'string\n'- 式展開
"2 * 2 = #{2 * 2}" #=> "2 * 2 = 4" - 文字列リテラルの中で改行して良い
- 式展開
- 配列 Array :
[0, 1, "hoge"]-
%記法
%W(hoge #{2*2}) #=> ["hoge", "4"]
-
%記法
- ハッシュ Hash :
{ a:"A", b:"B" } - 範囲式 Range : 終端を含む
"a" .. "z"', 含まない1 ... 10
- 配列展開
[*1..3] #=> [1,2,3]
演算子
- 四則演算 :
1 + 2,3 * 4 - 1 - 代入:
a = [1, 2, 3],b = a - 自己代入 :
i += 1,a ||= 1 - 多重代入 :
a, b = 1, 2 - 分割代入 :
x, y, z = [1, 2, 3] - 配列の残り :
head, *tail = [1, 2, 3, 4] - ネストした分割 :
(x, y), z = [[1, 2], 3]: - 条件演算子 :
x == 0 ? 1 : 0(?周りの空白は必要) - nil ガード :
nil&.size #=> nil(nil.sizeはエラー)
インクリメントは存在しない
制御構造
最後に評価したものが式の値となる
if / unless
nilとfalseだけが偽と評価される, 0や""は真
then は省略可能
if age >= 20 then
puts "adult"
elsif age >= 13 then
puts "teenage"
else
puts "child"
end
最後に評価したものを使う例
type = if age >= 20 then "adult" else "child" end
unlessはif notと同じ
unless baby?
feed_meat
end
式一つなら後置ifが出来る
a = 0 if a.nil?
case-when
多言語のswitch-caseの強化版
===演算子を内部で使う
breakは不要
case age
when 0..12
puts "child"
when 13..19
puts "teenage"
when 25, 42, 61 # or 条件
puts "yaku"
else
puts "adult"
end
0..12===3 が true を返す
while/until
doは省略可能
1行なら後置出来る
breakはbreakだが, continueではなくnext
break valで値を返せる
redoはループ条件を無視して現在の繰り返しをやり直す
while x > 1 do
x = if x.even? then x / 2 else 3 * x + 1 end
end
x = if x.even? then x / 2 else 3 * x + 1 end while x > 1
for
存在はするが, Enumerator(後述)が便利なため使われない
10回ループ
10.times do |i|
puts i #=> 0, 1, ... 9.
end
配列の走査
["a", "b", "c"].each do |c|
puts c #=> a, b, c.
end
添字と共に走査
["a", "b", "c"].each.with_index do |c,i|
puts [c,i] #=> ["a", 0] ["b", 1] ["c", 2]
end
begin-resure
例外捕捉
rescue内でretryでを書くと, beginからやり直せる
begin
raise NameError, "missing hogehoge"
rescue => e
p e
end
メソッド呼び出し
関数呼び出しのカッコは省略できる
-
puts(1, "str")よりもputs 1, "str" -
exit()よりexit
このため, 命令のようにかける
ブロック付き呼び出し
他言語でいうラムダ式的扱い
{}とdo endに機能的な違いはない
一行なら{}, 複数行ならdo endと使い分ける事が多い
[1,2,3].each{|i| puts i }
[[1,3],[2,2],[3,1]].each do |(a, t)| # 分割代入
t.times do
puts a
end
end
関数定義
関数名の最後に?,!が使える
def hey(name="unknown") # デフォルト引数
puts "hey #{name}!"
end
# ここから下でheyが使える
def comp(x, y)
return y-x > 0, y - x #=> 2要素配列を返す
end
c, v = comp 1, 3
ブロック付き関数の定義
def double
yield 0
yield 1
end
double{|i| puts "hello #{i}" }
標準ライブラリ
Kernel
ここに定義されているモジュール関数はプログラム内のどこでも使える
- 標準出力 :
print "hoge", "piyo" - 出力して改行 :
puts "hoge", "piyo" - 見やすく出力 :
p "hoge" - プログラムを終了 :
exit - 10秒停止:
sleep 10 - ファイル読み込み :
open("a.txt").readlines #=> ["a\n", "b\n"] - 乱数
- 整数乱数 :
rand(3) #=>0,1,2 - [0,1)の乱数 :
rand #=>0.13521686772195707 - さいころ :
rand(1..6)
- 整数乱数 :
Object
ここに定義されているメソッドは大抵のオブジェクトで使える
- nil判定 :
hoge.nil? - 文字列変換 :
100.to_s,[1,2,3].to_s - 変更禁止 :
hoge.freeze - 浅いコピー :
["a","b"].clone
数値 Numeric
- 切り上げ
1.2.ceil, 切り下げ1.2.floor, 四捨五入1.2.round - 絶対値 :
-1.2.abs - 整数同士は切り捨て :
10 / 3 #=> 3 - 小数になる :
10.0 / 3 #=> 3.3333333333333335 - 剰余 :
8 % 3 #=> 2,-8 % 3 #=> 1(割る数が正なら非負) - 累乗 :
2 ** 10 #=> 1024 - nビット目 :
0b1101[1] #=> 0 - 文字列に変換 :
12.to_s #=> "12"
インクリメントは存在しない
i++を書いてしまうと, 構文解析エラー出るので注意したい
整数 Integerは多倍長整数である
※Ruby2.4ではIntegerに統合されたので, この項は重要ではない。
FixnumとBignumクラスが存在するが, どちらもIntegerを継承している
Fixnumでオーバーフローすると, 自動でBignumに変換される
pry(main)> a = 10 ** 10
=> 10000000000
pry(main)> a.class
=> Fixnum
pry(main)> a.class.superclass
=> Integer
pry(main)> a = a ** 10
=> 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
pry(main)> a.class
=> Bignum
pry(main)> a.class.superclass
=> Integer
Math
- 三角関数 :
Math.cos(Math::PI) #=> -1 - 対数関数 :
Math.log(Math::E) #=> 1 - 平方根 :
Math.sqrt(2) #=> 1.4142135623730951
乱数randはKernelに定義されている
配列 Array
-
長さ5のnilの配列 :
Array.new(5) #=> [nil,nil,nil,nil,nil] -
添字で初期化 :
Array.new(5){|i| i} #=> [0,1,2,3,4] -
値で初期化 :
Array.new(5){"h"} #=> ["h","h","h","h","h"] -
[*0..4] == [0,1,2,3,4],('a'..'d').to_a == ['a','b','c','d'] -
a = [0,1,2,3,4]のとき- 参照 :
a[2] #=> 2 - 負数参照 :
a[-1] #=> 4(負数は後ろから) - 範囲参照 :
a[1..3] #=> [1,2,3] - 負の範囲 :
a[1..-2] #=> [1,2,3] - 開始位置, 個数 :
a[2,2] #=> [2,3]
- 参照 :
-
長さ :
[0,1,2].size #=> 3 -
最初の場所 :
[1,2,3,5,8].index(3) #=> 2,[1,2].index(10) == nil -
含む? :
[2,3,14,1].include?(14) #=> true -
連結 :
[1,2,3] + [4,5,6] #=> [1,2,3,4,5,6] -
nilを削除 :
[1,nil,3,nil].compact #=> [1,3] -
文字列をつなげる :
["h","e","l","l","o"].join(",") #=> "h,e,l,l,o"
- 反転 :
[0,1,2].reverse #=> [2,1,0] - ランダムに選ぶ :
["a","b","c"].sample #=> "a" or "b" or "c" - ランダムに並べる :
[0,1,2,3].shuffle #=> [3,1,0,2]
破壊的変更操作
それぞれa = [1,2,3]のとき
- 最後に挿入 :
a.push(10); a #=> [1,2,3,10] - 最後を取り出す :
a.pop #=> 1; a #=> [2,3] - 先頭に挿入 :
a.unshift(10); a #=> [10,1,2,3] - 先頭を取り出す :
a.shift #=> 1; a #=> [2,3] - push :
a << 10; a #=> [1,2,3,10] - 位置, 値を挿入 :
a.insert(2,10); a #=> [1,2,10,3] - 添字で削除 :
a.delete_at(1); a #=> [1,3] - 値を削除 :
a.delete(2); a #=> [1,3] - 同じ値で埋める :
a.fill{5}; a #=> [5,5,5]
破壊的変更
!を最後につけると破壊的変更を行う物がある
pry(main)> a = [*0..2]
=> [0, 1, 2]
pry(main)> a.reverse # aは変更されない
=> [2, 1, 0]
pry(main)> a
=> [0, 1, 2]
pry(main)> a.reverse! # 破壊的
=> [2, 1, 0]
pry(main)> a # aが変更される
=> [2, 1, 0]
Enumerable
- 配列走査 :
[0,1,2].each{|a| puts a} - 要素を置き換える :
[1,2,3].map{|a| a*a} #=> [1,4,9] - マッチするもの :
[1,2,3,5,8,13].select{|a| a.even?} #=> [2,8] - しないもの :
[1,2,3,5,8,13].reject{|a| a.even?} #=> [1, 3, 5, 13] - 添字とmap :
[0,0,0].map.with_index{|a,i| i} #=> [0,1,2] - 畳み込み:
(1..10).reduce{|res, n| res + b} #=> 55 - すべて成立?:
[2, 8, 6].all?{|n| n.even?} #=> true(any?, none?, one?) - 2つずつ重なって :
(1..4).each_cons(2){|v| p v}[1,2] [2,3] [3,4] - 2つずつ重ならず :
(1..6).each_slice(2){|v| p v}[1,2] [3,4] [5,6] - 最大値 :
[5, 3, 6].max #=> 6 - ソート :
[5, 3, 6].sort #=> [3, 5, 6]
# 画面外に出た敵を殺して, 配列から削除する例
enemys.map!{|enemy|
if enemy.out_of_window? # 敵が画面外なら
enemy.die
nil # nilを返す
else # そうでなければ
enemy # そのまま
end
}.compact! # nilを削除
文字列 String
Arrayと似たメソッドを持つ
- 結合 :
"hello" + "world" == "helloworld" - 文字ごとの配列 :
"hello".chars == ["h","e","l","l","o"] - 配列に分ける :
"1,2,3".split(",") == ["1","2","3"] - 数値に変換 :
"123".to_i == 123
連想配列 Hash
{a: 1, b:2}[:a] == 1
構造体のようなクラス Struct
Point = Struct.new(:x, :y) # Pointクラスを作る
point = Point.new(0,3) # Pointクラスのインスタンス
p [point.x, point.y] #=> [0, 3]
メソッド定義もできる
Enemy = Struct.new(:x, :y) do
def move
@x += 10
@y += 10
end
def pos
[@x, @y]
end
end
enemy = Enemy.new(10,10)
p enemy.pos #=> [10, 10]
enemy.move
p enemy.pos #=> [20, 20]
時刻 Time
Time.nowで現在時刻を得られる
time1 - time2は差を秒で返す
t0 = Time.now
10000.times{Array.new(1000)}
time_erapsed = Time.now - t0;
p time_erapsed #=> 0.141656284
参照とコピー
代入は参照の値渡し
origin = ["hoge", "piyo"]
same = origin # `same`と`origin`は同じ参照
shallow = origin.clone # Array*だけ*複製 要素は*同じ参照*
deep = origin.map{|s| s.clone} # 要素も複製
origin[0] = "XXXX" # 参照を上書き
origin[1].reverse! # 参照先を変更
p origin #=> ["XXXX", "oyip"]
p same #=> ["XXXX", "oyip"] # originと同じ参照
p shallow #=> ["hoge", "oyip"] # 要素の変更の影響を受ける
p deep #=> => ["hoge", "piyo"] # 変更されない
クラス
コンストラクタはnewではなくinitialize
class Person
attr_accessor :name, :age
def initialize(name, age) # コンストラクタ
@name = name
@age = age
end
def prof # インスタンスメソッド
"#{@name} is #{@age} years old."
end
end
me = Person.new("wass", 10)
p [me.name, me.age] #=> ["wass", 10]
puts me.prof #=> wass is 10 years old.
インスタンス変数 @hoge
デフォルトではクラス外からアクセス出来ない
外から参照するアクセサを設定することが出来る
class Pet
attr_accessor :weight # 書き込み読み込み可
attr_reader :name # 読み込みのみ可
attr_writer :lifespan # 書き込みのみ可
end
クラス変数 @@hoge
同じクラスのインスタンス間で共有できるグローバル変数
class Session
@@endpoint = "http://..."
# セッターを自分で定義する例
def self.endpoint=(url)
@@endpoint=url
end
end
Session.endpoint = "https://..."
p Session.endpoint #=> "https://..."
クラス定数
クラスの中の定数にアクセスするには::を用いる
class MyMath
TAU = Math::PI * 2
end
p MyMath::TAU #=> 6.283185307179586
クラスメソッド
self.をつける
class MyMath
def self.sec(x)
1/Math.cos(x)
end
end
p MyMath.sec(Math.PI) #=> -1.0
継承
class Enemy < Sprite
end
クラスの再オープン
クラス定義は上書きできる
class Array
def head_tail
h , *t = self
return h, t
end
end
p [1,2,3].head_tail #=> [1, [2, 3]]