noyのブログ

健康なエンジニアを目指す人のブログ

7つの言語 7つの世界 ~ Ruby の柔軟性を見よ

f:id:noy72:20180720205219j:plain

最近、『7つの言語 7つの世界』を読んでいます。Ruby, Scala のように広く知られている言語から、Io, Prolog などのあまり見たことがないような言語まで、計 7 つの言語の特徴を紹介するという本です。技術書ではなく読み物に近いです。単に読むだけでは面白くないので、少しだけですが実際に言語を触ってみました。

私自身は C++Java をほんのちょっとだけ触った程度です。

第2章は Ruby です(第1章は、はじめに)。

特徴

If it walks like a duck and quacks like a duck, it must be a duck

つまりどういうこと?

test関数でoutputを呼んでいる。このとき、test関数にどのようなインスタンスを渡しても、output関数さえ持つなら期待された通りに動く。

C++のテンプレートを使って書くと以下のようになる。

つまるところ、必要な機能を持っているのならば、それは期待されているオブジェクトとして見なすことができるということ(自信なし)。

長所

短所

  • パフォーマンス
    そもそもパフォーマンスを重視している言語でない。
  • 平行性とOOP
    これは Ruby に限った話じゃない気がする。
  • 型の安全性
    動的型付けだから仕方ない。

1日目

セルフスタディ

探してみよう

  • Ruby の範囲に関する情報 (p18)

範囲ってスコープのことかな? - 変数と定数 (Ruby 2.5.0)

試してみよう

文字列 "Hello, world" を出力する。

いつもの。

文字列 "Hello, Ruby" の中の "Ruby" という単語のインデックスを検索する。

Stringクラスにあるindexメソッドを使えば良い。

自分の名前を10回出力する

いくつか方法がある。forでもwhileでも当然書ける。
timesはFixnumのメソッドで、Fixnum回ループする。

ファイルに格納されている Ruby プログラムを実行する。

乱数で生成した数字と入力された数字を比較する

printは改行せずに出力する。出力を複数行う場合はコンマで区切る。
改行コードは "\n" で、 '\n' では改行しない。シングルクォーテーションは式を展開しないため、単なる文字列として表示してしまう。

2日目

配列

irb(main):001:0> a = []
=> []
irb(main):002:0> a[0] = 12345
=> 12345
irb(main):003:0> a[1] = "asdf"
=> "asdf"
irb(main):005:0> a[3] = [1,2,3]
=> [1, 2, 3]
irb(main):006:0> a
=> [12345, "asdf", nil, [1, 2, 3]]

配列に複数の型を次々と追加できる。怖い。

irb(main):001:0> a = [1,2,3]
=> [1, 2, 3]
irb(main):002:0> a.push('a')
=> [1, 2, 3, "a"]
irb(main):003:0> a.pop
=> "a"
irb(main):004:0> a
=> [1, 2, 3]
irb(main):005:0> a.pop
=> 3
irb(main):006:0> a
=> [1, 2]

配列は順序付きコレクションなので幾つかのメソッドが使える。
参考:Array - Rubyリファレンス

irb(main):001:0> a = [1, 2, 3]
=> [1, 2, 3]
irb(main):002:0> a[0]
=> 1
irb(main):003:0> a[-1]
=> 3

セグフォではない。 シンタックスシュガーのおかげ。

ハッシュ

irb(main):001:0> num = {1 => "one", 2 => "two"}
=> {1=>"one", 2=>"two"}
irb(main):002:0> num[1]
=> "one"
irb(main):003:0> num[2]
=> "two"
irb(main):004:0> num = {:array => [1, 2, 3]}
=> {:array=>[1, 2, 3]}
irb(main):005:0> num[:array]
=> [1, 2, 3]

C++で言う所の map。Value と Key はなんでも良い。

コードブロック

コードブロックとは名前のない関数である。do ~ end 、あるいは {} で囲うことでコードブロックになる。

前述した times を利用したコード、

これは、"noy"という文字列を表示するというコードブロックを 10 回実行している。

出力
name
noy

main -> pass_block -> call_back と、二つのputsを持つコードブロックが渡されている。

ラムダ式みたいな感じ?

Mixin

モジュールという関数と定数の集まりをクラス混ぜ込むことで、クラスは混ぜ込まれた振る舞いと定数を持つ。

この例では、output モジュールは get の返り値を出力する。ここで、get がいったいなんなのかは書かれていない。Java であればインタフェースが必要になるが Ruby はダックタイピングがあるためインタフェースがいらない。

Output モジュールが混ぜ込まれた Number クラスは、値を出力する out メソッドを持っている。モジュールを混ぜ込むことで機能を追加することができる。

Javaで書くとこうかな?

あれ、インタフェース……

セルフスタディ

探してみよう

配列からハッシュへ、ハッシュから配列へ

参考:ハッシュに含まれるキーや値を配列として取得 - ハッシュ - Ruby入門
参考:配列からハッシュを作成する - ハッシュ(Hash)クラス - Ruby入門

ハッシュの各要素を繰り返す

参考:ハッシュに対する繰り返し - ハッシュ - Ruby入門 

配列で実現可能なデータ構造

計算量を考えないのであれば、
- スタック (pop, push)
- キュー (drop, push)
- デック (unshift, push, drop, pop)

insert や delete も使えるから、割となんでも実現できそう。

やってみよう

ファイル内で、ある文字列を含む行を表示する簡易 grep を作る

ruby grep.rb data.txt 0719

ARGVにコマンドラインの引数が入る。一つ目でファイルを指定、二つ目で探す文字列を入力する。あとは1行ずつ読み出して、indexメソッドで判定する。探す文字列が含まれていない場合は nil が返る。

3日目

オープンクラス

Numeric クラスは数値を表すクラスで、Integer や Float クラスのスーパークラスである。よって、Numeric クラスに tax メソッドを追加すると、Numeric クラスのサブクラスは tax メソッドを使うことができる。

同じ名前のクラスが二つ以上ある場合でも実行できる。上記の例では、Text クラスは initialize メソッド、 tax メソッド、 sale メソッドを持つ。

このように、クラスが一度定義されると、それ以降の同じ名前のクラスは 変更(メソッドの追加など) を行う。

クラス名、メソッド名、メソッドのシグニチャが全てば同じであれば、単に上書きする。

method_missing

method_missingは、存在しないメソッドが呼ばれた時の動作を定義する。上記の例では、メソッド名に含まれる文字をソートした配列を返す。

存在しないメソッドを読んでもエラーを返さなくなる(代わりに method_missing メソッドが呼ばれる)ので、デバッグが難しくなる恐れがある。

参考:method_missing - Rubyリファレンス

セルフスタティ3日目

試してみよう

上記のプログラムを変更し、

というAPIを使えるようにせよ。

とりあえず、CSVファイルを読み取るようにする。each メソッドで table.each を使いファイルの中身を舐める。CSV::Row クラスで method_missing を定義し、CSV::Row.[key] と書かれたら、シンボル key をキーにする(わかりにくい)バリューを返す。

多分、書籍が想定した問題と違うことをしている。

所感

C++Javaと比べて柔軟性がかなり高いと思った。割となんでもできる。動的型付けの言語はほとんど触ったことがなかったので、型がバラバラすぎない……? 考えなさすぎじゃない……? とやってはいけないことをしている気分になった。あと、計算量どうなるのという不安も。

静的型付け -> 動的型付け 「型がなくて不安だけど楽」
動的型付け -> 静的型付け 「柔軟性なさすぎ。。もうマジ無理。。」
ってなりそう。

コミュニティ大きいし、web系に使えるし、使いやすいと思うので、とても良いと思いました(小並感)。