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.45
1.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]]