Hatena::ブログ(Diary)

三等兵

2010-04-14

Ruby/PythonライクにJSが書けるCoffeeScriptでコーヒーブレイク

Python知らないんだけど気分転換に。適当に。

http://jashkenas.github.com/coffee-script/

Jeremy Ashkenas氏がRuby/Pythonライクな文法でJavaScriptを記述する、興味深い試みをおこなっている。同氏が開発した「CoffeeScript」と呼ばれる簡易言語は、"a little language that compiles into JavaScript"の名のとおり、コンパイルするとJavaScriptコードを出力するというものだ。Ruby製のコンパイラが用意されており、デベロッパはCoffeeScriptで書かれたファイルをコンパイラに通すことでJavaScriptコードを得られる。

http://journal.mycom.co.jp/articles/2010/01/13/coffeescript/index.html

パイソンて、あれか。パイソンて英語で牛?だっけ?牛はカウか。ヒヒーンとかいうてつっこんでくる何かしらの動物?馬じゃなくて。なんかこう、パイソーン!て突撃してくるウシみたいなの?うん違いますねごめんわからない。触ったことないけど大丈夫でしょう。


なんでほたるはすぐしんでしまうんorz

?o?C?¥?? - Google ????

インスト

環境はただのwindows7

rubyは1.8.7

windowsでできるのかな?なんか微妙だけど物は試し。


rubyの簡単インスト。

http://rubyforge.org/projects/rubyinstaller/

http://arton.hp.infoseek.co.jp/indexj.html

ruby久しぶり。新しいPCにしてから入れてなかった。じゃあCoffeeScriptはRubyGemsでインストールします。

RubyGemsの使い方。これ使ったことないよ。

http://webos-goodies.jp/archives/51106257.html


コマンドプロンプトから、

C:\Users\username>gem install coffee-script

でいけた。rubyのpathは通ってないといけないかな。で、公式では今のところCoffeeScriptは0.6.1なんだけど、gemsだと0.3.2だった。アップデートも確認したけどなし。ということはまあ大きい変更なさそうだしこれでいっか。


コマンドの引数を確認。

C:\Users\username>coffee

coffee compiles CoffeeScript source files into JavaScript.

age:
  coffee path/to/script.coffee
    -i, --interactive                run an interactive CoffeeScript REPL
    -r, --run                        compile and run a CoffeeScript
    -o, --output [DIR]               set the directory for compiled JavaScript
    -w, --watch                      watch scripts for changes, and recompile
    -p, --print                      print the compiled JavaScript to stdout
    -l, --lint                       pipe the compiled JavaScript through JSLint

    -e, --eval                       compile a cli scriptlet or read from stdin
    -t, --tokens                     print the tokens that the lexer produces
    -v, --verbose                    print at every step of code generation
    -n, --no-wrap                    raw output, no function safety wrapper
    -g, --globals                    attach all top-level variable as globals
        --narwhal                    use Narwhal instead of Node.js
        --install-bundle             install the CoffeeScript TextMate bundle
        --version                    display CoffeeScript version
    -h, --help                       display this help message

引数とか詳しいことは、

http://journal.mycom.co.jp/articles/2010/01/13/coffeescript/index.html

で見るとして。とりあえずバージョンを確認。

C:\Users\username>coffee --version
CoffeeScript version 0.3.2

んしゃ。


hello world

面倒なのでデスクトップ上でやる。

C:\Users\username>cd desktop

今度は適当なファイルにCoffeeScriptを書く。

alert 'hello world'

拡張子どうしよう?んー、hello.coffeeにしよう。デスクトップに保存してファイルをコンパイルしてみた。

C:\Users\username\Desktop>coffee hello.coffee

おーできたできた。hello.jsってのが作られた。

//hello.js
(function(){
  alert('hello world');
})();

関数は引数で外せる。ファイルの拡張子に関しては何でも良さそう。hello.coにして通してみたけど普通に生成された。でもcoffeeという拡張子が一番良さそう。


文法

空白、インデント、セミコロン無し、それぞれ気をつける。その他ちゃんとしたリファレンスはこっちで。英語ですが、コードをみればなんとなく何言ってるか分かるかもしれない

http://jashkenas.github.com/coffee-script/


とりあえず簡単なのだけ適当に。

/* ---変数1--- */
num: 1+=1
bool: Boolean(1);
//↓    ↑がCoffeeScriptで↓がJSに
var bool, num;
num = 1 += 1;
bool = Boolean(1);

/* ---変数2--- */
/*スコープ注意。同じ変数名は気をつける。*/
num: 1
func: ->
  str: 'varialbe'
  num: 10
  str
str: func()
alert num+','+str
//↓
var func, num, str;
num = 1;
func = function func() {
  var str;
  str = 'variable';
  num = 10;
  return str;
};
str = func();
alert(num + ',' + str); // 10,variable

/* ---配列--- */
arr: [1, [21, 22], {'one': 1, 'two': 2}, 4, 5]
//↓
var arr;
arr = [1, [21, 22], {
  'one': 1,
  'two': 2
}, 4, 5
];

/* ---オブジェクト--- */
/*returnつくのが気になる*/
obj: {
  'elm1': 3
  'elm2': 2
  'func': (y, x) -> alert y*x
}
obj.func(obj.elm1, obj.elm2)
//↓
var obj;
obj = {
  'elm1': 3,
  'elm2': 2,
  'func': function func(y, x) {
    return alert(y * x);
  }
};
obj.func(obj.elm1, obj.elm2);

/* ---関数1--- */
func1: (x) -> x*x
//↓
var func1;
func = function func1(x) {
  return x * x;
};

/* ---関数2--- */
/*関数の終わりはインデントで判定っぽい。
returnは必ずつくの?*/
func2: (x,y) ->
  x
  y
alert func2(1,2)
//↓
var func2;
func2 = function func2(x, y) {
  x;
  return y;
};
alert(func2(1, 2));

/* ---関数3--- */
/*書き方戸惑う*/
func3: (->
  x: {
  'one': 1
  'two': 2
  'three': 3
  }
  (num) -> x[num] 
)()
//↓
var func3;
func3 = (function() {
  var x;
  x = {
    'one': 1,
    'two': 2,
    'three': 3
  };
  return (function(num) {
    return x[num];
  });
})();

こりゃJSの書き方かな。


ステートメントなどRubyやPythonっぽいのはかけないので適当にコピペ。

/* ---if文--- */
mood: greatly_improved if singing

if happy and knows_it
  claps_hands()
  cha_cha_cha()

date: if friday then sue else jill

expensive: or do_the_math()
//↓
var date, expensive, mood;
if (singing) {
  mood = greatly_improved;
}
if (happy && knows_it) {
  claps_hands();
  cha_cha_cha();
}
date = friday ? sue : jill;
expensive = expensive || do_the_math();

/* ---while文--- */
if this.studying_economics
  while supply > demand then buy()
  while supply < demand then sell()

num: 6
lyrics: while num -= 1
  num + " little monkeys, jumping on the bed.
    One fell out and bumped his head."
//↓
var _a, lyrics, num;

if (this.studying_economics) {
  while (supply > demand) {
    buy();
  }
  while (supply < demand) {
    sell();
  }
}

num = 6;
lyrics = (function() {
  _a = [];
  while (num -= 1) {
    _a.push(num + " little monkeys, jumping on the bed. \
One fell out and bumped his head.");
  }
  return _a;
}).call(this);

/* ---for文--- */
countdown: num for num in [10..1]

egg_delivery: ->
  for i in [0...eggs.length] by 12
    dozen_eggs: eggs[i...i+12]
    deliver new egg_carton(dozen)
//↓
var _a, _b, _c, _d, countdown, egg_delivery, num;
countdown = (function() {
  _a = []; _c = 10; _d = 1;
  for (_b = 0, num = _c; (_c <= _d ? num <= _d : num >= _d); (_c <= _d ? num += 1 : num -= 1), _b++) {
    _a.push(num);
  }
  return _a;
}).call(this);
egg_delivery = function egg_delivery() {
  var _e, _f, _g, _h, dozen_eggs, i;
  _e = []; _g = 0; _h = eggs.length;
  for (_f = 0, i = _g; (_g <= _h ? i < _h : i > _h); (_g <= _h ? i += 12 : i -= 12), _f++) {
    _e.push((function() {
      dozen_eggs = eggs.slice(i, i + 12);
      return deliver(new egg_carton(dozen));
    }).call(this));
  }
  return _e;
};

/* ---クラスっぽいもの--- */
class Animal
  move: (meters) ->
    alert @name + " moved " + meters + "m."

class Snake extends Animal
  constructor: (name) ->
    @name: name

  move: ->
    alert "Slithering..."
    super 5

class Horse extends Animal
  constructor: (name) ->
    @name: name

  move: ->
    alert "Galloping..."
    super 45

sam: new Snake "Sammy the Python"
tom: new Horse "Tommy the Palomino"

sam.move()
tom.move()
//↓
var Animal, Horse, Snake, sam, tom;
var __extends = function(child, parent) {
  var ctor = function(){ };
  ctor.prototype = parent.prototype;
  child.__superClass__ = parent.prototype;
  child.prototype = new ctor();
  child.prototype.constructor = child;
};
Animal = function Animal() {  };
Animal.prototype.move = function move(meters) {
  return alert(this.name + " moved " + meters + "m.");
};
Snake = function Snake(name) {
  this.name = name;
  return this;
};
__extends(Snake, Animal);
Snake.prototype.move = function move() {
  alert("Slithering...");
  return Snake.__superClass__.move.call(this, 5);
};
Horse = function Horse(name) {
  this.name = name;
  return this;
};
__extends(Horse, Animal);
Horse.prototype.move = function move() {
  alert("Galloping...");
  return Horse.__superClass__.move.call(this, 45);
};
sam = new Snake("Sammy the Python");
tom = new Horse("Tommy the Palomino");
sam.move();
tom.move();

PythonだかRubyだか混じっていてよくわからないのですが、ステートメントは短くて良いけどこれパッとみて判断付くのって慣れている人や英語圏な人だよ。クラスはRubyっぽい。


コンパイルして即実行

no title

これ使うらしい。あんまり知られていないみたいだけど。CommonJSの実装の1つ。エンジンは今のところrhinoだけ?

http://narwhaljs.org/engines.html

解凍したフォルダを見る限りrhinoしかないのですがv8などもインストできたと思う。でもうまくできないときもあったりするんじゃないか。


文法チェック

JavaScript Lint

JSLintが必要。


感想

  • 新しい言語触ってた気分
  • まだ若いのでこれからどうなるかってところらしい
  • 関係ないけどサーバサイドのJSなんのこっちゃ難しすぎ。すごいなあ
  • やっぱりコーヒーじゃなくて水飲もう水
    • 最近水の硬さとかまろやかさの違いが分かるようになった
    • ネスカフェからスカウトこねーかなー
    • もう98円ぐらいのあの水でも十分おいしい
    • 浄水された水とか売ってる都市もあるよね。それ家庭でのめるて良いね
    • ああ、でもコーヒーとか体に良いって聞いたわ
    • でも飲みすぎると匂いしみついてくさくなる
    • 案外あの匂い嫌いな人多い
    • そういうわけで、カフェオレでいいんじゃね。ドロリッチでいいんじゃね
    • いや、しかしそれは外道なのだよ明智くん
    • おっけじゃあ山奥のカフェいってくるNow!

いったいなにをいっているんだ

2009-03-04

mailread.rbを使ってみる

>Ruby\lib\ruby\1.8\mailread.rbのソース
class Mail
  def initialize(f)
    unless defined? f.gets
      f = open(f, "r")
      opened = true
    end

    @header = {}
    @body = []
    begin
      while line = f.gets()
	line.chop!
	next if /^From /=~line	# skip From-line
	break if /^$/=~line	# end of header

	if /^(\S+?):\s*(.*)/=~line
	  (attr = $1).capitalize!
	  @header[attr] = $2
	elsif attr
	  line.sub!(/^\s*/, '')
	  @header[attr] += "\n" + line
	end
      end
  
      return unless line

      while line = f.gets()
	break if /^From /=~line
	@body.push(line)
      end
    ensure
      f.close if opened
    end
  end

  def header
    return @header
  end

  def body
    return @body
  end

  def [](field)
    @header[field.capitalize]
  end
end

これを使ってみます。でもなにやってんのかさっぱりわがんねぇ。なんじゃこりゃ。initializeメソッドがあるということは、ここに引数で渡せば良いのかな。

あーわかった。こういうのってメソッドみていけばいいのか。それで実行してみたら文字化けするんだけど。元でそれを調節しなきゃいけないのかな。でもどうしすればいいのでしょう。いやん。

だめだ。わからんかった。Mailクラスでどこ直したらいいのかわからない。配列にする前にやればいいのかなって無理だった。というわけで元でどうにもできんから、headerとbodyメソッドを使うときに文字列にする。



というわけで、とかつながえているけどここまでに2時間程度調べたり試したりしているんで、血と涙と汗と切なさがいりまじってるんだ!まじで刹那な切なさじゃないんだよ←うまい!


はい、headerはそのままでいいとして、bodyにはArray#joinを使います。配列を連結して文字列にするってやつね。これしないと配列やハッシュではnkfライブラリ使えないので。

nkfってーのはあれ。文字コードを変換するライブラリです。文字化けするのはこの本文でした。英語だったらそのままでいんだろうけど。いろいろ説明するの面倒だから実行スクリプトを貼ってしまおう。

require "mailread"
require "nkf"

a = Mail.new("mail.eml")

a.header.each{|i| puts i}
puts NKF.nkf("-s -m0", a.body.join())

これでメールのヘッダと本文が表示できました。メール内容ヘッダ公開怖いのでなしです。どこ消せば良いのさ。とりあえず解説しときます。今後の自分のために(笑

require "mailread"
require "nkf"

ここはrequireでまずmailread.rbをもってきてます。ライブラリの使い方っていうのは、こうやってrequireでもってくるもんなんですね。今までこれらのライブラリどうやってつかえばいいのさ!とか思っていたけど。ほんと当たり前のことなのだけれど(汗

あとnkfは、文字コードの変換のライブラリですね。コマンドオプションでksとかkuとかやってもうまく表示できなかったので。これは今思うと当たり前なんですけど。

a = Mail.new("mail.eml")

ここでmailread.rbのmailクラスをnewで実体化させています。引数の部分はe-mailのファイル名。同じ作業フォルダにおいておきましょう。変数名は適当にaにしてます。

こうやって実体化させることによってライブラリを使うんですね。最初はこんな簡単なプログラムなのに、英語の羅列でしょぼーんになりましたが今はどんとこいじゃー。という気分。

こうくると他のライブラリもさわってみたくなってきましたよ。それで最後、

a.header.each{|i| puts i}
puts NKF.nkf("-s -m0", a.body.join(""))

これの一行目はheaderの部分をeachで表示しています。別にeachでなくてもいいんですけどね。元のプログラムみるとハッシュで代入されていますが、キーはほっとけみたいな感じで。ちなみにa.headerってのは実体化させたaのheaderメソッドを呼び出していますね。

二行めはメールの本文部分を表示しています。NKF.nkf()はよくわからないんですが、お望みの文字コードに変換できるもんです。オプションの「"-s -m0"」はshift-jisにして-0mはなんかヘッダのMIMEがどーのこーのとよくわからん。本文関係ないかもしれないからいらないかも。

a.body.join()は、まずa.bodyってのは実体化したaのbodyメソッドを呼び出しています。んで後にあるのはjoinメソッド。配列だけのメソッドかもしれないですね。ハッシュでは使えなかったです。

これは配列を文字列にするというもの。簡単にかくと、

a = [1,2,3]
p a            #=>[1, 2, 3]
p a.join       #=> "123"

こんな感じにかわるもんです。配列が文字列になってるでしょ。

joinメソッドの引数には、配列の区切りに入れたい文字列を指定できます。

a = [1,2,3]
p a            #=> [1, 2, 3]
p a.join       #=> "123"
p a.join(",")  #=> "1,2,3"

こんな感じですね。


使い方は以上です。newメソッドの引数の「"mail.eml"」を自分のメール名にかえればまんま実行できると思いますよ。初心者の人ファイト。

郵便番号の検索プログラム

とりあえず適当にやりまくればいいってもんじゃないけど、もう考えたって仕方がない。Ralisに移る前にいろいろやっておかないとね。

郵便番号を検索するぜ。これはたのしいRubyにのっているもんです。

http://www.post.japanpost.jp/zipcode/download.html

まずダウンロード。2つに1つ。

で、まず読み込み速度をはかる。

t0 = Time.now
open("KEN_ALL.CSV").each{|line|
  line.chomp!
  line.split(",")
}

p Time.now - t0

はいできたー。何秒だったっけ。6秒ぐらいかかった気がする。

で、まあいろいろあって説明するのがめんどくさいというか、はっきりいって分からない。もうなにやってんのかさっぱりわからない。泣ける。

>ruby jzipcode.rb

require "gdbm"
require "csv"


module JZipCode
  COLUMN_ZIP = 2
  ZIP_FILE = "KEN_ALL.CSV"
  DB_FILE = "KEN_ALL.db"
  
  module_function
  
  def make_database(zipfile, dbfile)
    return if File.exist?(dbfile)
    open(zipfile){|io|
      GDBM.open(dbfile, 0644, GDBM::NEWDB){|db|
        io.each{|line|
          colums = line.split(",")
          zipcode = colums[COLUMN_ZIP].delete('"')
          if tmp = db[zipcode]
            line = tmp + line
          end
          db[zipcode] = line
        }
      }
    }
  end
  
  def find(code)
    make_database(ZIP_FILE, DB_FILE)
      GDBM.open(DB_FILE, nil, GDBM::READER|GDBM::NOLOCK){|db|
        if line = db[code]
          return CSV.parse(line)
        end
      }
      return nil
  end
end

データの登録と、データの検索ができるプログラム。なにやってんのこれ。調べる気力もわかないほどだ。なんなんだーこれはー。引数がわけわからん。なんでデータベースできてるの。もう死ねた。

実行ファイルはこれ。

>ruby jzipcode2.rb
!# ks

require "jzipcode"
require "nkf"

t0 = Time.now
code = ARGV[0] || "1000000"
if rows = JZipCode.find(code)
  rows.each{|row|
    address = row[6] + row[7]
    unless /^\210\310\211\272\202\311/ =~ row[8]
      address << row[8]
    end
    puts NKF.nkf("-s", address)
  }
end
p Time.now - t0

まだ分かりやすい。ライブラリまじってくるとほんとわからない。なんでデータベース自動でできるんだろ?検索のメソッドはまあいいとして、make_databaseのメソッドが死ねる。

自分のレベルを知るためだけに書いたものでした終わり。

2009-02-28

ソースのコメント部分だけを削除したいプログラム1

というプログラムをためしにやっていました。なんだったっけ。1.9で無くなった?mailread.rbをみてみたら、なんかコメントうぜぇとか思いまして。英語読めんわからん。

mailreadを読んでみようという前に、勉強がてらコメント部分だけとってしまおうという簡単そうなプログラムを書いてみましたら見事できなーいわーい。

あとちょっとだと思うんだけどなー。

require "fileutils"
filename = "copymailread.rb"
FileUtils.cp("/Ruby/lib/ruby/1.8/mailread.rb", filename)

mail = open(filename)
while text = mail.gets
  i = text.index(/#/).to_i
  r = text.rindex(/\s/).to_i
  if /#/ =~ text
    text[i..r] = ""
    text.sub!(/\s+/, "")
  end
    p text
end
mail.close

こんな数行しかないプログラムを書くのに1日を要した。ここにたどり着くまでに、何度間違えたことか。その過程を綴ったのに、あほのせいで消えたのな。

まだ完璧じゃないというか、地味に文字列オブジェクトのこってしまう。「""」におきかえてるだけですからね。抹殺することができない。


手順としては、まずコピーするファイル名を指定して、作業フォルダに名前をつけて保存。

require "fileutils"
filename = "copymailread.rb"
FileUtils.cp("/Ruby/lib/ruby/1.8/mailread.rb", filename)

ここの部分は、getsかARGVに変えたりしたらそれなりになるかと思うけどめんどくさい。

while text = mail.gets
  i = text.index(/#/).to_i
  r = text.rindex(/\s/).to_i
  if /#/ =~ text
    text[i..r] = ""
    text.sub!(/\s+/, "")
  end

1行単位でファイル開き、その際にiとrには指定した文字列のバイト位置を調べた。indexメソッドですね。これ思いつくのに5時間かかったんだから///

#から行末までをなくしたいわけで、文字列じゃよくできないからバイト数値でやってみた。我ながらなんというコペルニクス的転回、いや現代的にいうならパラダイムシフトでもいうべきことでした。

んでto_iメソッドで数値にかえて代入。ifで#のある行だけに限定し、i..rまでの間の文字を「""」に変えます。ここがどうもうまくいかない。どうしても文字列オブジェクトを入れないといけないんだろうか?オブジェクトそのものを破壊ってできないのかな。

んで次の行でお空白もとりあえず削除しーの終わり。


あとは全部まっさらにしてwriteメソッドで書き換えてしまえばいいんじゃないかなと思ったけど、やってない。昨日はここで次の日に回す予定だったのに。

とりあえず今日はここで保留。

require "fileutils"
dir = ARGV[0]
filename = ARGV[1]
FileUtils.cp("/Ruby/lib/ruby/1.8/#{dir}", filename)

mail = open(filename)
while text = mail.gets
  i = text.index(/#/).to_i
  r = text.rindex(/\s/).to_i
  if /#/ =~ text
    text[i..r] = ""
    text.sub!(/\s+/, "")
  end
    p text
end
mail.close

こんな感じにすればなんでも対応できるかな。

あーけど文字列に置き換えるってのはよくないな。望ましいのは先頭に#があるものはifかなんかで排除して、途中に#があるものはそのままって感じで出力すると、きれいに整形されておきかえるってこともないんですけどそんなんわからん。

無念。2に期待する。

2009-02-27

はじめてのRubyプログラミングについて

これのこと。その前に、

で勉強してみてからのみてみると、表現が悪いけど薄いと感じてしまうから、はじめてのRubyプログラミングだけだと「できる気になった」だけに陥るかもしれないですね。

基本的にはやさしくかかれていて、クラスやオブジェクトのあたりもわかりやすかったけど、たのしいRubyを読んでからだと物足りない部分が目立つ。間違いのプログラムもあったし。


はじめてのRubyプログラミングは「読むだけ」なら良いかもしれないですね。実践するといっても例題少ないし簡単だし。たのしいRubyをやった後だから復習としては十分でした。getsメソッドの使い方でこういうのもあるんだなと思いましたね。たのしいの方はファイル読み込むぐらいしか使わなかったもの。


といっても基本的によくまとまっているんじゃないかなと思いました。たのしいRubyよりもプログラムの書き方が丁寧というか、飛躍している部分が少ない。

2009-02-17

Ruby勉その23-エラー処理と例外2

例外が起ころうが起こらまいが、常に実行したい処理がある場合はensure節に後処理を記入できるみたいです。後処理っていうみたい。


後処理

構文は、

begin
  例外を発生させる可能性のある処理
rescue =>変数
ensure
  例外の有無にかかわらず実行される処理
end

ファイルをコピーするメソッド。fromをtoにコピーします。

def copy(from, to)
  src = open(from)
  begin
    dst = open(to, "w")
    data  src.read
    dst.write(data)
    dst.close
  ensure
    src.close
  end
end

"w"って何。調べたよちくしょう。書き込み専用ってことらしい。IOクラスに説明があった。もう理解しない。流れをつかむこと前提でやってるなこれ。


やりなおし

エラーしたらやりおしましょ、ってやつ。ファイルが開けるようになるまで、10秒ごとにopenメソッドを実行して、成功すれば読み取るって例。

file = ARGV[0]
begin
  io = open(file)
rescue
  sleep 10
  retry
end

data = io.read
io.close

ファイルが開けないと無限ループに陥るプログラム。


rescue修飾子

ifやunless修飾子のようにrescueにも修飾子がありまふ。

構文は、

式1 rescue 式2

という形で、式1で例外が発生すりゃ式2の値が全体の値になるよというもん。

begin
  式1
rescue
  式2
end

と一緒。

n = Integer(val) rescue 0

だとIntegerは整数値のクラスなんで、文字列いれたら例外ですね。右側の値は0が返ることになりますね。難しい処理じゃないときとか。


例外処理の構文

メソッドの処理全体をbegin〜endで括るような場合は、それらを省略できる。

def foo
  begin
    メソッドの本体
  rescue => ex
    例外処理
  ensure
    後処理
  end
end

が、

def foo
  メソッドの本体
rescue => ex
  例外処理
ensure
  後処理
end

という感じに。クラスでも使えるけどそんなことするこたまずないって。


捕捉する例外を指定

複数の種類の例外が発生する可能性があって、個別に対処する場合は複数のrescue節を記述することで処理を分けることができる。

begin
  例外を発生させる可能性のある処理
rescue Exception1, Exception2 => 変数
  Exception1またはException2に対する処理
resucue Exception3 => 変数
  Exception3に対する処理
resucue
  それ以外の例外が起こった場合の処理
end

という感じ。クラスを指定すれば、想定している例外だけを捕捉することができる。

file1 = ARGV[0]
file2 = ARGV[1]
io = nil
begin
  io = open(file1)
rescue Errno::ENOENT, Errno::EACCES
  io = open(file2)
end

クラスはよくわからんけどファイルがない場合とファイルを開くための適切な権限がない場合に発生する例外らしい。ので、1が無理だったら2を開くってわけ。


例外クラス

全ての例外はExceptionクラスのサブクラスとなっていて、エラーの種類に応じた例外を定義している。組み込みの例外は結構定義されているね。書くのめんどいけど。

rescue節は指定した例外クラスを捕捉すると同時にそのサブクラスも捕捉するって。自分で例外クラスを定義するときは、StandardErrorクラスを継承したクラスを作成し、さらにそれを継承するのが一般的とのこと。

class Myclass < StandardError; end
class Myclass1 < MyError; end
class Myclass2 < MyError; end
class Myclass3 < MyError; end

とやれってこと。そうすると例外のクラス指定するときにMyclassだけ書けばいいってことですね。


例外を発生させる

raiseメソッドを使う。自分で判定した条件をもとに例外を新しく発生させる場合や、直前に捕捉した例外を再び発生させて、例外を呼び出し元に伝えたいときに使用する。4つの形式があり、

raise メッセージ

RuntimeErrorを発生させる。新しく生成された例外オブジェクトにメッセージとして文字列をセット。


raise 例外クラス

指定した例外を発生させる。これはさっきやったやつですね。


raise 例外クラス, メッセージ

新しく生成された例外オブジェクトにメッセージとして文字列をセット。RuntimeErrorは発生しないやつ?なんだこれは。


raise

rescue節の外ではRuntimeErrorを発生させ、内では最後に発生した例外をもう1度発生させる。


なかなか奥が深いもんですね。こういうスキルも必要なんですか。


catchとthrow

実行の流れを制御できるらしい。catchしてthrowするってことかな?

catchの引数にシンボルを指定して、ブロックとともに呼び出す。同じブロックの中で、同じシンボルを引数としてthrowを呼ぶと、ブロック終了。

def test_throw
  throw :test
end

puts "test start"
catch(:test){
  puts "before test_throw()"
  test_throw()
  puts "after test_throw()"
}
puts "test end"

実行結果が、

test start
before test_throw()
test end

えーとこれはあれか。最初に定義したメソッドを呼び出して、途中でブロックを終了させるってやつですね。実際で使われる場合は、

catch(:exit){
  loop{
    loop{
      .
      .
      .
      if val != 0
        throw :exit, val
      end
      .
      .
      .
     }
   }
}

えーとこれは、なんだ。ループを1度に抜けてますね。でvalってなんでthrowの後にあるんだろ。シンボル2つってことかな。わからん。


もーこのあたりにきて頭がパンクしてます。とりあえず第二部終わり。