PythonユーザーのためのRuby

概要

山﨑研の主言語がRubyである&TapyrusがRuby準拠、だったりでRubyを学びたい。

'20年入学組のバックグラウンドであるPythonで勉強しよう。

参考

書籍「プログラミング言語Ruby」
- オライリージャパン

書籍「初めてのRuby」
- オライリージャパン

https://www.ruby-lang.org/ja/documentation/ruby-from-other-languages/to-ruby-from-python/

https://qiita.com/ryosuketter/items/ddad508cb0124e4fe378

https://qiita.com/kidach1/items/b1672f1c16e2d15f2d9c

https://docs.ruby-lang.org/ja/latest/doc/spec=2fvariables.html

https://qiita.com/kasei-san/items/75ad2bb384fdb7e05941

https://www.sejuku.net/blog/14955

本編

Ruby公式「PythonからRubyへ」

Ruby公式にこんなページがある。最適すぎてわろた。

抜粋してまとめてついでに追記もしていく。

Pythonとの類似

類似①「irbと呼ばれる対話型の実行環境があります。」

Pythonはターミナルでpythonって打つと実行環境が出てくる。これはRubyにもあってirbと呼ばれる。軽いデバッグとか気軽にできるので覚えておくと便利。

類似②「Pythonにおける三重引用符のように、文字列リテラルを複数行に記述できます。」

使ったことある人ない人いるかもしれないけど複数行に渡らせて文字列を記述できる。これはコードの可読性向上を目的によく使われるね。

msg = """\ やあ! これは三重引用符の例だよ!!! 便利だね!!!""" print(msg) # >>>やあ! # これは三重引用符の例だよ!!! # 便利だね!!!

類似③「配列は同じように機能します。」

基本は同じ。例えば宣言の方法とか。

ただし、ちょくちょく違う。

例えば次。

list = ["a", "b", "c", "d"] print(list[1]) # >>> b print(list[4]) # >>> IndexError: list index out of range
Python
list = ["a", "b", "c", "d"] pp list[1] # => b pp list[4] # => nil
Ruby

Pythonではインデックスエラーを吐くけど、Rubyでは吐かずにnilを返す。

しまむらは昔これに引っかかってぶちぎれてた。

それと多くの言語で禁止されている範囲外への代入。

list = [0,1,2] pp list # => [0,1,2] list[9] = 9 pp list # => [0, 1, 2, nil, nil, nil, nil, nil, nil, 9]

宣言時にはサイズ3だったけど無理くり list[9] = 9 で拡張してる。飛んだ部分には自動で "nil" を入れてくれる。

これがエラー出ず通るのがRuby。通らずIndexError吐くのがPython。

類似④「オブジェクトは強力で動的な型付けを持ちます。」

両者とも動的型付け言語です。

動的型付け?

JS、Python、Rubyなどのスクリプト言語における型付け方法。プログラムを書く際に型付けは行わず、実行時に型の整合性チェックをする。それにより実行速度は遅くなる。対義は「静的型付け」。

静的型付け?

JavaやC、C#などのコンパイラ言語における型付け方法。プログラムを書く際にあらかじめ型付けを行い、コンパイルを経て実行する。コンパイル時に整合性チェックが行われるため実行速度は早い。

型推論?

静的型付け言語においても明示的な宣言なしで型を決定すること。Java、C#、GOなどで採用されており、記述時は let や var などで宣言する。

類似⑤「すべてはオブジェクトです。そして、変数はオブジェクトへのただの参照です。」

両者ともオブジェクト指向言語です。

類似⑥「for文・while文はほぼ一緒」
# 回数指定 for num in 1..5 do puts num end # オブジェクト内をfor for var in collection do puts var end # while while zyoukenn do puts "hello" end

ほぼ一緒です。末尾の do は省略可能。

ただしdo-while文は違うので注意。そもそもPythonにはdo-while文がないのでRubyで新規に覚える形にはなるが...。

Python: 用意されてないので無理矢理実装する

while True: 実行される処理 if 条件: break

Ruby: beginで始める

begin 実行される処理 end while 条件

ちょっと特殊。

Pythonとの相違

相違①「定数をつくれます。」

Pythonにはない「定数」を作れる。アルファベット大文字で始まる識別子は定数とみなされる。

相違②「名前付けについての規約がいくつかあります。」

例えば
・クラス名は大文字から始める
・変数名は小文字で始める
などPythonよりもより厳格なルールがある。

また、変数名に関して

ローカル変数

hensuu

グローバル変数

$hensuu

インスタンス変数

@hensuu

クラス変数

@@hensuu

などRuby独特の記法も存在する。

相違③「二重引用符で囲まれた文字列は、エスケープシーケンスや、 式展開を解釈します。 一重引用符で囲まれた文字列は、Pythonでいうraw文字列と同じ扱いとなります。」

結構ややこしい。

各言語での一重引用符(シングルクォーテーション)と二重引用符(ダブルクォーテーション)の使い方をまとめてみる。

Python

区別はない。文字列中にシングルクォーテーションがあるときはダブルだといいよねくらい。逆も然り。ちなみにGoogleはほぼダブルで統一してるらしい。はえ〜。

Java

文字リテラルはシングル、文字列リテラルはダブル。

C#

文字(char型)はシングル、文字列(string型)はダブル。そうしないとエラー吐く。

Ruby

ただの文字列ならどちらでも良い。

ただし、

エスケープシーケンス(改行記号 \n など)を入れたいときはダブルでないといけない。

puts "あああ\nいいい" # => あああ # いいい puts 'あああ\nいいい' # => あああ\nいいい

また、式展開したいときもダブルである必要がある。

シングル使うのはPythonと同じようにダブル内でダブル使いたい時とかのみ。逆にいうとシングルで囲まれた文字列には「式展開が含まれません」ということを暗示することができる。

つまりダブル使っておけば間違いないね。可読性の観点から見ても統一していこう。

式展開?

Pythonでフォーマット文で文字列中に変数組み込むのと似たノリ。

name = "嶋村" puts "私の名前は#{name}です" # => 私の名前は嶋村です

とても便利。

相違④「TrueとFalseは、trueとfalseになります。 また、Noneの代わりはnilになります。」

文字通り。Pythonでの TruetrueFalsefalseNonenil と表記する。

相違⑤「elifの代わりにelsifを使います。」

こういうところほんとに........ほんとに。

if num == 1: print("1だよ") elif num == 2: print("2だよ") else: print("それ以外だよ")
if num == 1 puts "1だよ" elsif num == 2 puts "2だよ" else puts "それ以外だよ" end

elif の代わりに elsif を使う。後述もするけどブロックの最後には end が必要。

Javaとか一般には else if だからPythonも異端だけどRubyでまた別の出てくるのほんとにむかつk

また、if notunless と表記する。

unless num == 1 puts "1ではないね" else puts "1だね" end

注意点として、unless 文には elsif 節は認められない。

相違⑥「importの代わりにrequireを使います。」

文字通りですね。

ちなみにRubyではライブラリ管理にgemを使う。パッケージマネージャーはbundler。

ついでに、Pythonのバージョン管理はpyenvとかanacondaが主流だけどRubyではrbenvが主流。

相違⑦「一度定義した変数を、未定義にする方法はありません。 変数をnilで設定すれば、変数に入っていた値をGCできるようにはできますが、 スコープが存在する限り変数自体はシンボルテーブルに残り続けます。」

難しいこと言ってますね。

Pythonではいらなくなった変数やオブジェクトは del することで簡単に未定義、廃棄することができる。

しかし、Rubyにはそのような機能はない。変数に nil を入れることで変数内の値をGC(ガベージコレクション)することはできるが、スコープが存する限りは残り続ける。

特徴⑨「クラスのコンストラクタには initialize を使う」

Pythonだと __init__ を使うけどRubyでは initialize を使う。

Python

class Point def __init__(self, x, y): self.x = x self.y = y # 呼び出し p = Point(0,1)

Ruby

class Point def initialize(x,y) @x, @y = x,y end end # 呼び出し p = Point.new(0,1)

先述の通り、@x はインスタンス変数。

相違⑩「スライスの仕様が異なる」

Python

list = [0,1,2,3,4,5,6,7,8,9,10] print(list[0:6]) # [0, 1, 2, 3, 4, 5]

Ruby

list = [0,1,2,3,4,5,6,7,8,9,10] puts list[0..5] # => 0, 1, 2, 3, 4, 5

Pythonでは [ 以上 : 未満 ]

Rubyでは [ 以上 .. 以下 ]

と書く。とても注意。

Rubyの特徴的な機能

特徴①「多様なプリント文」

知ってるだけで4つもある。なんでなん。

  1. print: 引数を出力する。末尾で改行なし。
  2. puts: 引数を出力する。末尾で改行。基本これ。
  3. p: 引数&オブジェクトを分かりやすく出力。デバッグ用。
  4. pp: 引数&オブジェクトを分かりやすく出力。p よりもより綺麗に、より分かりやすく表示してくれる。配列を表示するときに大活躍する。デバッグ用。
# hashを用意 hash = {"last_name" => "shimamura",\ "first_name" => "hayato",\ "old" => 22} print hash # => {"last_name"=>"shimamura", "first_name"=>"hayato", "old"=>22} puts hash # => {"last_name"=>"shimamura", "first_name"=>"hayato", "old"=>22} # p hash # => {"last_name"=>"shimamura", "first_name"=>"hayato", "old"=>22} pp hash # => {"last_name"=>"tanaka", # "first_name"=>"takuya", # "old"=>22}

基本はputsを使う。デバッグ用ではppを使う。明確に分けておくとコードまとめるときにどれがデバッグ用の出力か分かって便利。

特徴②「ブロックの最後にはendが必須」

module, class, def, if などで end が必須。忘れると動きません。

module TestModule class TestClass def print_hello bool = true if bool puts "Hello!" end end end end

だるいですね。ただインデントがどえらい厳格なPythonとどっちがいいかっていうと個人の好みですね。

さらっと出てきたmoduleはC#の名前空間的なあれです。

特徴③「例外処理」

Pythonでは try-except、C#・Javaでは try-catch

Rubyでは...

# 例外が起こる可能性があるコード begin wallet = Glueby::Wallet.load(wallet_id) tokens = Glueby::Contract::Token.issue!(issuer: wallet, token_type: Tapyrus::Color::TokenTypes::NFT, amount: 1) # InsufficientFunds エラーをキャッチする rescue Glueby::Contract::Errors::InsufficientFunds pay2user(wallet_id, 10_000) retry # その他全てのエラーはこっちへ # => error でエラーオブジェクトを error 変数に格納 rescue => error puts error end

それらしい例が思いつかなかったのでガチコード持ってきました。

ブロックチェーン上でトークンを発行するコードにおいて、発行者の資金不足エラー InsufficientFunds が起こる可能性がある場合のコード。エラーが起きたら解消するまで pay2user メソッドを実行し続ける(retry により繰り返される)。それ以外のエラーが起きたら適当に出力する。そんなコード。

rescue でエラーを検知して対処コードを書く。つまりエラーを "レスキュー" するんですね。かわいい。

特徴④「クラスはオープンである」

割とレベル高いけどとてつもなく便利なので紹介したい。メタプログラミングの分野。

オープンクラス」

既存のクラスを任意の場所で "オープン" して、メソッドの修正や追加など変更を加えられる機能。例えば既存のStringクラスをオープンしてメソッドを追加すればあらゆるStringオブジェクトに対して使えるオリジナルメソッドを作れる。

多分探せば他の言語でもあると思う。オーバーライドと一緒。

class String def hello self + ", hello!" end end str = "しまむらです" puts str # => しまむらです str.hello puts str # => しまむらです, hello!

また次のように簡易に書くこともできる

def Math.square(x) x*x end

これでコアライブラリの一つであるMathモジュールにsquareメソッドを追加したことになる。

クラスやモジュールはオープンであり、実行時に変更や追加ができる。

特徴⑤「モジュールからメソッドを呼び出す」

C++に記法は似てる。てか同じ?C++に詳しくないからわからん。

例えばさっき出たNFTのコード

tokens = Glueby::Contract::Token.issue!(issuer: wallet, token_type: Tapyrus::Color::TokenTypes::NFT, amount: 1)

これは「"Glueby"モジュールの"Contract"モジュールの"Token"クラスの"issue"メソッド」を使うとなる。

つまり

Module::Module::Class::method

モジュール数は任意。モジュールメソッドとかだとクラスが抜けることもある。

C++に似てるっていうのは、例えば標準出力使うとき

#include <iostream> std::cout << "Hello, World";

これは「"std"名前空間の"cout"メソッド」を呼び出してる。

つまり

namespace::method

ちょっと似てるね。

特徴⑥「メソッドの戻り値」

Pythonとかでは return で値を返すけどRubyでは最後に評価された内容を戻り値として判断する。

例えば

Python

def square(num): return num*num

Ruby

def square(num) num*num end

num*num がメソッド本体において最後に評価されるので戻り値は num*num した値になる。

普通に return でも返せる。

特徴⑦「感嘆符と疑問符」

Rubyのメソッドにはちょくちょく疑問符・感嘆符が出てくる。

感嘆符を用いるのは、メソッドの利用に注意が必要だということを知らせるため。例えばArrayクラスのsortメソッドには sortsort! の二種類がある。前者は元オブジェクトのコピーをソートして返してくれるが後者は元オブジェクトを直接ソートする。後者ではソート前のデータが失われるので注意が必要、という意味で感嘆符が付けられている。

特徴⑧「ミュータブルがとてもミュータブル」

ミュータブル?

ミュータブルは、変更可能な変数の型。ミュータブルオブジェクトは、オブジェクトを作成した後に状態を変更できるオブジェクト。
対義語はイミュータブル。作成後は状態を変更できない。

例えばRubyの文字列はミュータブル。[]を使えば文字列内の文字を書き換えることも可能。さらに << を使えば追加することも可能。この << 演算子は文字列以外にも度々見かける。

str = "あいうえお" puts str # => あいうえお str[2] ="く" puts str # => あいくえお str << "か" puts str # => あいくえおか

Pythonの文字列もミュータブルだからそんなに違和感ないかもしれない。

また、freezeメソッドを呼び出すと変更が不可になる。凍っちゃう。これは他のオブジェクトについても同様。

# 続き str.freeze str << "き" # => can't modify frozen String: ~~~ (FrozenError)

FrozenErrorを吐く。凍ってるよって。

特徴⑨「アクセッサの宣言」

アクセッサを定義するとき、attr_accessor を使う。つまり自動でゲッターとセッターを用意してくれる。

class Point attr_accessor :x :y def initialize(x,y) @x, @y = x,y end end # ゲッター、セッターとして使えるようになる p = Point.new(0,1) puts p.x # => 0 p.x = 2 puts p.x # => 2

ちなみにゲッターのみの作成は attr_reader、セッターのみの作成は attr_writer で行える。

特徴⑩「継承」

端的にコードだけ。

class Point3D < Point end

こんな感じ。以上。

特徴11「多様なループ処理」

忘れてて終盤になってしまった。

・each

配列やハッシュ内の要素に対してループする。

# 配列内をループ [1,2,3,4].each do |num| p num end # 1 # 2 # 3 # 4 # ハッシュ内をループ {ruby:"rails",php:"Cakephp",python:"Django"}.each do |k,v| p "#{k}はkey、#{v}はvalue" end # rubyはkey、railsはvalue # phpはkey、Cakephpはvalue # pythonはkey、Djangoはvalue

ハッシュではとても便利。

for 文とやれることは同じだけどこっちの方がよく使われる(らしい)。

・until

while が条件が true の間実行するのに対して until は条件が false の間実行する。

until zyoukenn do puts "hello" end

書き方は while と同じ。do-until 文も作れる。

・loop

loopbreak しない限り無限にループする。Pythonでいう white True:

num = 0 loop do puts num num += 1 if num >= 5 break end end

・times

指定回数ループする。

# 単純に指定回数回す 3.times do p "ループするよ" end # ループするよ # ループするよ # ループするよ # do |num| すれば回数を使える 3.times do |num| p num end # 1 # 2 # 3

・upto

指定した数に達するまで変数を増やしていく

1.upto(3) do |num| p num end # 1 # 2 # 3

・downto

指定した数に達するまで変数を減らしていく

3.downto(1) do |num| p num end # 3 # 2 # 1

他にもuptoやらdowntoやらstepやらたくさんある。興味あれば覗いてみると面白いかも。