JSer が Ruby やるので備忘録
@armorik83 です。なにやら最近は完全にAngular 芸人と呼ばれるようになったのですが、一方で Ruby 知識はド素人、今まで避け続けてきたもんだから仕方がない。で、思い立ったので学び始めます。理由はご想像にお任せ。
以下の文体ですが、他言語にはある程度長けていても、Ruby は一切やったことがない、というレベルを想定しています。比較対象は主に JavaScript ES6、自分用なので雑です。
バージョン
JavaScript のバージョンはES n
で表現するが、基本的にはブラウザの実装次第。Ruby はインタプリタ自体のバージョンを指す。インタプリタといっても、Ruby 1.9 からはYARVという VM 上で動いてるらしい。
最新は執筆時点で2.1.6
, 2.2.2
。この 2 系統あたりを見ておけばいいのかな、1.9.3
はサポート終了してるらしいので、今回は2.0
以降に絞って学ぶ。
読むもの
ざっと2.0.0
のマニュアルを読んでいく。2.1
, 2.2
前提で学ぶとやんごとなき事情でハマる恐れがあるので、あとで知識を上書きする。
リンク先は全て 2.0.0 にしているので、参照される方は注意。
目安にするコーディングスタイル
右も左も分かってないので、とりあえず以下のものを参照する。
https://github.com/cookpad/styleguide/blob/master/ruby.ja.md
特徴
『はじめに』の引用。調べなくても想像つくものと、つかないものに分ける。
想像つく
- インタプリタ
- 変数に型が無い (動的型付け)
- ユーザによるメモリ管理が不要
- 全てがオブジェクト
- クラス、継承、メソッド
- ブロック付きメソッド呼び出し(イテレータ)
- クロージャ(※でも JS とは全然違った)
- 強力な文字列操作/正規表現
- 多倍長整数
- 例外処理機能
- OS への直接アクセス
想像つかん
- 変数宣言が不要
- 特異メソッド
- モジュールによる Mix-in
- ダイナミックローディング
スクリプト言語は JavaScript と PHP の経験があるので、ざっと見て馴染みないなーというものを下に分けた。CLI での Hello World とかはパスだパス!
基礎構文
ステートメント
1 行 1 ステートメント。セミコロンなし。1 行 2 ステートメント以上書きたいときに限りセミコロンで区切る。
JS | Ruby |
---|---|
いつも付けてる | 要らない |
コメント行
#
を使う。行末まで有効。行頭、行中でも構わない。
JS | Ruby |
---|---|
// |
# |
コメントブロック
=begin
, =end
を使う。埋め込みドキュメントと名付けられていて、JS の/*...*/
とは想定が異なる。
JS | Ruby |
---|---|
/* , */
|
=begin , =end
|
変数と定数
Ruby の変数と定数の種別は変数名の最初の一文字によって、ローカル変数、インスタンス変数、クラス変数、 グローバル変数、定数のいずれかに区別されます。
http://docs.ruby-lang.org/ja/2.0.0/doc/spec=2fvariables.html
ローカル変数だけでなく、インスタンス変数も格納された時点から有効になる。クラス変数がstatic
で宣言できるのは TypeScript の特徴。グローバル変数の比較は一例。
JS | Ruby | TypeScript |
---|---|---|
var foobar = 42; |
foobar = 42 |
|
this.instanceVar = 42; |
@instanceVar = 42 |
|
ClassName.classVar = 42; |
@@classVar = 42 |
static classVar = 42; |
const FOO_BAR = 42; |
FOO_BAR = 42 |
|
window.foobar = 42; |
$foobar = 42 |
ローカル変数のスコープ
宣言した位置からその変数が宣言されたブロック、メソッド定義、またはクラス/モジュール定義の終りまでです。
http://docs.ruby-lang.org/ja/2.0.0/doc/spec=2fvariables.html
以下も参照する。
https://github.com/cookpad/styleguide/blob/master/ruby.ja.md#変数
全てがオブジェクト
一例。
数値型はInteger
, Float
になるんだな。Numeric
は Abstract なので後回し。
演算子
-
+
-
*
/
%
おなじみ -
**
累乗 -
+=
自己代入(可能な構文はここ) -
<
>
<=
>=
比較 -
&&
||
!
and, or, not -
&
|
<<
>>
^
~
ビット演算
文字列
http://docs.ruby-lang.org/ja/2.0.0/doc/spec=2fliteral.html#string
シングルクオートとダブルクオートに挙動の違いがあるのは PHP と同じ。JS では ES6 より Template strings が使える
var name = 'armorik83';
var str1 = 'hello ${name}'; // hello ${name}
var str2 = "hello ${name}"; // hello ${name}
var str3 = `hello ${name}`; // hello armorik83
name = 'armorik83'
str1 = 'hello #{name}' # hello \#{name}
str2 = "hello #{name}" # hello armorik83
正規表現
/abc/
で正規表現リテラルなのは JS と同じ。Docsをざっと見た感じ JS とそんなに変わらないが、モードにいくつか違いがありそうだ。
破壊的メソッドと値渡し・参照渡し
メソッド名の後に!
が付くと破壊的メソッド。公式が行っているものの慣習らしく、言語仕様として定められているわけではない。
あと、Ruby では常に値渡しらしい。ただ、オブジェクトなので参照の値渡しということだ。副作用でハマったらこの辺を疑うってことで。
真偽値を返すメソッド
慣用的に、真偽値を返すタイプのメソッドを示すために使われます。
http://docs.ruby-lang.org/ja/2.0.0/doc/symref.html#q
JS とかで、キャメルケースでisFoo
とかするやつ。慣習的に Ruby ではisFoo
しない。
なにがしの言語 | Ruby |
---|---|
isEmpty() |
empty? |
配列
var arr = [1, 2, 3];
arr[1] = 4;
console.log(arr); // [1, 4, 3]
arr = [1, 2, 3]
arr[1] = 4;
p arr # [1, 4, 3]
範囲指定
こういう構文だと覚えるより、Range リテラルを与えていると考えた方がよさそう。
var arr = [1, 2, 3, 4];
console.log(arr[1..3]); // SyntaxError: Unexpected number
console.log(arr[1...3]); // SyntaxError: Unexpected number
console.log(arr.slice(1, 4)); // [2, 3, 4]
console.log(arr.slice(1, 3)); // [2, 3]
arr = [1, 2, 3, 4]
p arr[1..3] # [2, 3, 4]
p arr[1...3] # [2, 3]
逆から
var arr = [1, 2, 3, 4];
console.log(arr.slice(-1)[0]); // 4
console.log(arr.slice(-2, -1)[0]); // 3
arr = [1, 2, 3, 4]
p arr[-1] # 4
p arr[-2] # 3
おおっ、JS 地味につらい…。(比較にすらなってない)
%記法
words = ['foo', 'bar', 'baz']
words = %w(foo bar baz)
Array<String>
は下のようにも書ける。コーディングスタイルではこちらを推奨していた。
ハッシュ
JS でいう Object 型、他の言語でいう連想配列、辞書のあたり。
var price1 = {'apple': 2000, 'orange': 1500, 'grape': 3000};
console.log(price1['apple']);
var price2 = {apple: 2000, orange: 1500, grape: 3000};
console.log(price2.apple);
price1 = { 'apple' => 2000, 'orange' => 1500, 'grape' => 3000 }
p price1['apple'] # 2000
price2 = { apple: 2000, orange: 1500, grape: 3000 }
p price2[:apple] # 2000
apple:
で宣言すると、参照時は:apple
というSymbol リテラル形式なのが特徴的。
制御構造
if, unless
Ruby では
if
を繋げるのはelsif
であり、else if
(C のように)でもelif
(sh のように)でもないことに注意してください。
http://docs.ruby-lang.org/ja/2.0.0/doc/spec=2fcontrol.html#if
end
ってのも JS からすると驚きがデカい。
if cond
# ...
elsif cond
# ...
else
# ...
end
後置if
という書き方もある。
if user.active?
send_mail_to(user)
end
send_mail_to(user) if user.active?
三項演算子はちゃんとある。if
内代入もある。個人的にはif
内代入は使わない。
unless
は鳥肌もので、JS でif (!arg) { return; }
のように書くことに馴染んでいる自分としては、こんな逆転する構文は面倒にしか見えない。
コーディングスタイルで推奨どころかSHOULD扱いなので、そういうものかーと思いながら見てる。
case
case cond
when val1
# ...
when val2
# ...
else
# ...
end
for
var hash = {a: 1, b: 2, c: 3};
for (let k in hash) {
if (hash.hasOwnProperty(k)) {
let v = hash[k];
console.log(k, v);
}
}
hash = {a: 1, b: 2, c: 3}
for k, v in hash
p k, v
end
意外なことに JS のfor
文(for (var i; i < len; i++)
みたいな)と Ruby のfor
文は全く別物だった。JS のfor-in
文が近い。
その他の繰り返し
while
文はまだ馴染みがあるが、times
, upto
, downto
などのメソッドはかなり新鮮。慣れるまでは毎回調べそう。
上の例はeach
メソッドでこうも書ける。do
必須。
hash = {a: 1, b: 2, c: 3}
hash.each do |k, v|
p k, v
end
関数・クラス・メソッド宣言
def
比較に使う JS はオーソドックスな書き方にしている。
function square(n) {
return n * n;
}
console.log(square(3)); // 9
def square n
n * n
end
p square(3) # 9
class, initialize
JS のconstructor
のように、def initialize
を定義する。
class Component {
constructor(store) {
this.store = store;
}
}
class Component
def initialize store
@store = store
end
end
new とアクセサ
class User {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
var u = new User('armorik83', 83);
console.log(u.name); // "armorik83"
console.log(u.age); // 83
class User
def initialize name, age
@name = name
@age = age
end
end
u = User.new('armorik83', 83)
p u.name # NoMethodError
p u.age # NoMethodError
NoMethodError
となるので、アクセサの宣言が必要らしい。
attr_accessor
attr_reader
attr_writer
http://docs.ruby-lang.org/ja/2.0.0/class/Module.html#I_ATTR_ACCESSOR
class User
def initialize name, age
@name = name
@age = age
end
attr_accessor :name, :age
end
u = User.new('armorik83', 83)
p u.name # "armorik83"
p u.age # 83
なるほど。
インスタンスメソッド
class User {
constructor(name, age) {
this.name = name;
this.age = age;
}
isAdult() {
return 20 <= this.age;
}
}
var u = new User('armorik83', 83);
console.log(u.isAdult()); // true
var c = new User('child', 8);
console.log(c.isAdult()); // false
class User
def initialize name, age
@name = name
@age = age
end
def adult?
20 <= @age
end
end
u = User.new('armorik83', 83)
p u.adult? # true
c = User.new('child', 8)
p c.adult? # false
ここだけ見ると、OOPL 経験者なら戸惑うところは無さそう。
クラスメソッド、クラス変数
class ClassName
@@classVar = 42
def ClassName.methodName
# ...
end
end
クラス変数はコーディングスタイルでMUST NOTだった。
継承
class Base
def initialize prop
@prop = prop
end
def print
p @prop
end
end
class Concrete < Base
def initialize prop, childProp
super prop
@childProp = childProp
end
def printAll
p @prop, @childProp
end
end
base = Base.new('a')
base.print
c = Concrete.new('b', 2)
c.print
c.printAll
あまりいい例とは言えん。
馴染みがなくて用途が分かってないもの
以下雑多。
Proc
クロージャっぽいもの? Ruby に第一級関数は無いようで、Proc で実現しているようだ。アプリケーションを書いたことがないので想像が付いてない。
特異メソッド
インスタンスにメソッドを足せるらしい。使い道想像できず。
Mix-in
必要になったら覚えたい。
2.1, 2.2 の新機能
公式ソースではないので、ググるキッカケ的に。
ちょっとは読めるようになった
そもそも、どの解説記事を読むにも構文が分かっておらず読めなかったので、今回はその問題を潰す目的。Ruby らしい文化、Ruby 最高ってなる機能はまだまだ全然分かってないので、まあぼちぼち…。
以下まさかりやオススメ記事が並ぶと大変嬉しいです。それでは。
Discussion