はじめに
プログラミング初学者の筆者がPythonを独学で2か月程学んだ後、Rubyを1ヶ月程勉強した際に無意識的に誤解してしまったところ、気を付けるべきところをまとめてみました。
間違っている箇所があればぜひご指摘ください。
returnは不必要
Rubyでは最後に評価された式がメソッドの返り値になりreturnは必要ない。
returnはメソッドを途中で脱出する場合に使われることが多い。
意図しない値が返っている時はこの仕様を思い出したい。
def add(a, b)
a + b # returnを書く必要がない
end
puts add(1, 2)
# => 3
関数(メソッド)のスコープ
Rubyはローカル変数の参照は定義されたスコープ内のみ。
メソッド定義の外側で宣言されたローカル変数のスコープは、メソッド定義の内側には及ばない。
以下の a はグローバル変数のように使えない。
a = 1
def inner():
b = 2
print(a + b)
inner()
# => 3
a = 1
def inner
b = 2
print(a + b)
end
inner
# =>`inner': undefined local variable or method `a' for main:Object (NameError)
Rubyだとグローバル変数の宣言は頭に$をつける(非推奨とされる)
$a = 1
def inner
b = 2
print($a + b)
end
inner
# => 3
クラスへのアクセス権
- Python
・デフォルトで隠蔽されていない。
・ _, __ を変数名・メソッド名の頭につけると隠蔽される。 - Ruby
・デフォルトで隠蔽されている。
・インスタンス変数:アクセスメソッドを設定すれば隠蔽が解除される。(attr_accessor 等)
・メソッド:private / protected / public の設定により隠蔽レベルを制御できる。
ex)インスタンス変数nameの参照
class Person:
def __init__(self, name):
self.name = name
taro_obj = Person("taro")
print (taro_obj.name)
# => taro
class Person
def initialize(name)
@name = name
end
end
taro_obj = Person.new('taro')
puts taro_obj.name
# => undefined method `name' for #<Person:0x000001edc13e8e28 @name="taro"> (NoMethodError)
Rubyでは直接インスタンス変数にアクセスできないのでエラー。参照用のメソッドかアクセサメソッドを設定する。
class Person
# アクセサメソッド
attr_accessor :name
def initialize(name)
@name = name
end
# もしくはここで外部からの参照用メソッド
# def name
# @name
# end
end
taro_obj = Person.new('taro')
puts taro_obj.name
# => taro
最初かなり躓いたポイント。
モジュール
- Python
ファイル名がモジュール名(test.pyならtestがモジュール名) - Ruby
module 構文で定義されるコード。モジュール名はmoduleの中で定義される。
文字列がミュータブル(mutable)
ミュータブルなオブジェクトとは、作成後にその状態を変更すること(破壊的変更)ができるオブジェクトのこと。
逆にイミュータブルは作成後に変更できない(そもそも破壊的メソッドが定義されていない)オブジェクトのこと。
Pythonの文字列はイミュータブルだがRubyの文字列はミュータブル。
代わりにイミュータブルな文字列として振る舞うシンボルがある(内部的には整数)
ex)文字列の先頭を大文字にしてみる
s = "sample"
s[0] = "S"
print(s)
# => TypeError: 'str' object does not support item assignment
s = "sample"
s[0] = "S"
print(s)
# => Sample
freezeメソッドを付けることで破壊的な処理を防ぐことが出来る。
マジックコメント # frozen_string_literal: trueをファイルの一番上入れて文字列リテラルのオブジェクトを一気にfreezeも出来る。
s = "sample"
s.freeze
s[0] = "S"
print(s)
# => can't modify frozen String: "sample" (FrozenError)
# frozen_string_literal: true
s = "sample"
# s.freeze
s[0] = "S"
print(s)
# => can't modify frozen String: "sample" (FrozenError)
Rubyでは最後に!が付く多くは破壊的メソッド (!が付くから必ずしも破壊的メソッドとは限らないし逆も然り。単に危険的な意味合いが強い。)
ex) 文字列を大文字にするupcaseは非破壊的メソッド、upcase!は破壊的メソッド
真偽値
- Python
偽: None と False、数値型におけるゼロ: 0, 0.0, 0j, Decimal(0), Fraction(0, 1)、空のシーケンスまたはコレクション: '', (), [], {}, set(), range(0) - Ruby
偽: nilとfalseのみ。
特に数値型0と空文字列、空配列などの扱いには注意する。
Rubyでif条件文中に == のところを = と誤って書いてしまったときに意図しないtrueでif文の中が実行されてしまう。
not and or でなく ! && ||
not, and, or は演算子としての優先度が低いので同じように使うと意図しないエラーが起きる。
優先度【高】→【低】
! → && → || → not → (and or)
and と or の優先順位は同じなので左から評価される。
t1 = true
t2 = true
f1 = false
!f1 || t1
# => true
not f1 || t1
# => false
t1 || t2 && f1
# => true
t1 or t2 and f1
# => false
and orは条件分岐に用いず制御フローに用いる。
除算の挙動
整数同士の除算が異なる
-
Python3
//
もしくは/
演算子を用いる。
//
:整数同士の割り算は整数。小数以下切り捨て。
/
:整数同士の割り算で割り切れても小数が返る(float型に型が変換される) -
Ruby
/
演算子を用いる。
整数同士の割り算は整数。小数以下切り捨て。 -
(Python2)
/
演算子を用いる。整数同士の割り算は整数。小数以下切り捨て。
/
演算子を同じように使わない。
a = 12 // 4
print(a) # => 3
print(type(a)) # => <class 'int'>
b = 12 / 4
print(b) # => 3.0
print(type(b)) # => <class 'float'>
c = 12 / 4
puts c # => 3
puts c.class # => Integer
比較の連鎖
Rubyでは数学のように繋げて比較演算子を書くことが出来ない。
a = 2
if 0 < a < 3
puts a
end
# => error
a = 2
if 0 < a && a < 3
puts a
end
# => 2
配列、リストの差分
言語の違いではないかもしれない。Pythonでのリストに近いものとしてRubyでは配列が使われるが、 - 演算子を使うとその要素の差分が取得できる。
list_a = [1, 2, 3, 4]
list_b = [3, 4]
diff_list = list_a - list_b
print(diff_list)
# => TypeError: unsupported operand type(s) for -: 'list' and 'list'
arr_a = [1, 2, 3, 4]
arr_b = [3, 4]
diff_arr = arr_a - arr_b
p diff_arr
# => [1, 2]
For文
Rubyであまりfor文は多用しない。その代わりeachメソッドやmapメソッドといった繰り返しメソッドが主に使われる。
その他
case文が存在する、
range()関数、pass文が存在しない
終わりに
PythonとRubyは似ている言語なので学習しやすい反面、無意識的にPython知識でRubyを補完してしまっていた。
まだまだ駆け出しの身なので注意深く学んでいきたい。
この記事も少しずつ増やしていきたい。
参考文献
Ruby公式リファレンス
Python公式ドキュメント
プロを目指す人のためのRuby入門(チェリー本)
PythonからRubyへ(Ruby公式)
PythonとRubyの変数のスコープのまとめ