初心者がRailsを勉強するブログ

Railsを0からお勉強するブログです。

はじめてのCoffeeScript

今日は、CoffeeScriptを見ていこうと思います。
公式のドキュメントは、ここhttp://coffeescript.org/にあります。

簡単にまとめると、

・functionは->
・クラスをclass文で書ける
・ifやforは後置できる
・引数の()は省略できる
・関数で最後に評価した値を自動的に返す
・インデントで構造を表す
・リスト内包表記が書ける
・比較で1 < x < 5の書き方ができる
・"#{a} is variable"のように、文字列に変数を埋め込める
・文字列、正規表現を複数行で書ける
・switchはRubyのcaseにそっくり
・例外はほぼjsと一緒

で、演算子が一部変更されてます

 js       -> coffee

 ===  -> is
 !==   -> isnt
 !        -> not
 &&     -> and
 ||       -> or
 true   -> true, yes, on
 false  -> false, no, off
 this   -> @, this
 in      -> of
 なし   -> in

こんな感じ?
RubyPythonに慣れてれば、すごくしっくり来そうですね。

CoffeeScriptコンパイルは、

$ coffee -c ファイル名

で出来ます。同じ名前のjsファイル吐きます。-cをつけずに、

$ coffee ファイル名

だと、jsファイルを吐かずに実行してくれます。

CoffeeScriptコンパイルして得られるjsは、中身を見ればわかると思いますが、グローバル空間を汚さないようになっています。
なので、下のコードは、jsの方はブラウザのコンソールにコピペすれば関数を呼べるように書いていますが、CoffeeScriptの方はコンパイル後のjsをそのままコピペしても関数は呼べません。動かす際はコンパイル後のjsの無名関数の中で関数を呼び出してください。


それでは本編に入ります。
普通に文法を見てもおもしろくないので、jsと比較しつつ、遊んでみましょう。

まず、FizzBuzzを比べてみます。ifとforを使いつつ、関数にしてみます。

javascript

function fizzbuzz( n ) {
  for ( var i = 1; i <= n; i++ ) {
    // FizzかBuzzがついたら表示する文字列
    var message = '';

    // 3の倍数ならFizzをつける
    if ( i % 3 === 0 ) {
      message += 'Fizz';
    }

    // 5の倍数ならBuzzをつける
    if ( i % 5 === 0 ) {
      message += 'Buzz';
    }

    // messageが空なら数字を表示する
    console.log( message || i );
  }
}

CoffeeScript

fizzbuzz = (n) ->
  for i in [1..n]
    # FizzかBuzzがついたら表示する文字列
    message = ''

    # 3の倍数ならFizz, 5の倍数ならBuzzをつける
    message += 'Fizz' if i % 3 is 0
    message += 'Buzz' if i % 5 is 0

    # messageが空なら数字を表示する
    console.log message or i

coffeeの方がかなり簡潔で見やすいです。
ifが後置で書けるので、かなりすっきりしてます。
1からnまでの配列をRubyのRangeみたいに[1..n]と書けるのがいいですね。

jsでは0がfalseなので、coffeeでunlessを使ってみましょう。

CoffeeScript

fizzbuzz = (n) ->
  for i in [1..n]
    # FizzかBuzzがついたら表示する文字列
    message = ''

    # 3の倍数ならFizz, 5の倍数ならBuzzをつける
    message += 'Fizz' unless i % 3
    message += 'Buzz' unless i % 5

    # messageが空なら数字を表示する
    console.log message or i

やっぱり、unlessはすっきりします。

さらに、forを後置して、こんな風にも書けます。
関数にしなければ、ワンライナーですね。

CoffeeScript

fizzbuzz = (n) ->
  console.log [['Fizz'][i%3], ['Buzz'][i%5]].join("") or i for i in [1..n]

FizzBuzzはこんなもんにしておきましょう。
CoffeeScriptは書いてて楽しいです。


次に、クラス定義の違いを見てみましょう。
Carクラスを書いてみます。
車の名前を属性にして、走るメソッドを入れてみます。

javascript

// Carオブジェクトのコンストラクタ
function Car( name ) {
  this.name = name;
}

// 走るメソッド
Car.prototype.run = function( speed ) {
  console.log( this.name + 'が' + speed + 'キロで走ってます。' );
}

Coffeescript

// Carクラス
class Car
  constructor: (@name) ->

  # 走るメソッド
  run: (speed) ->
    console.log "#{@name}#{speed}キロで走ってます。"

CoffeeScriptはこれでprototypeに入れてくれるので、かなり楽ですね。
クラスベースのオブジェクト指向言語に慣れた人は、coffeeの方がかなり見やすいと思います。


最後に、じゃんけんをするプログラムを書いて、比べてみましょう。
jsとcoffeeでswitchの扱いやすさの差が歴然です。
もはやswitchじゃなくてcaseと書きたいレベル。

javascript

function janken() {
  // 1がグー、2がチョキ、3がパー
  var hands = ['ぐー', 'ちょき', 'ぱー'];

  // プレイヤーの手
  var playerHand = window.prompt("じゃんけんをしましょう!\n何を出しますか?\n1. ぐー 2. ちょき 3. ぱー");

  // 日本語入力に対応
  // 不正な入力があったらはじく
  if ( ['1', '2', '3'].indexOf( playerHand ) === -1 ) {
    switch( playerHand ) {
      case 'ぐー':
      case 'グー':
        playerHand = 1;
        break;
      case 'ちょき':
      case 'チョキ':
        playerHand = 2;
        break;
      case 'ぱー':
      case 'パー':
        playerHand = 3;
        break;
      default:
        alert('何言ってるかわかんないです。');
        return;
    }
  }

  // 内部処理用に、playerHandの値をhandsのインデックスに合わせる
  playerHand -= 1;
  var playerHandStr = hands[ playerHand ];

  // COMの手。ランダムで決める
  var comHand = Math.floor( Math.random() * 3 );
  var comHandStr = hands[ comHand ];

  // 勝敗表示用の文字列
  var bothHandsStr = 'あなた: ' + playerHandStr + "\n" + 'COM : ' + comHandStr + "\n";
  var win = "あなたの勝ち!";
  var lose = "あなたの負け…";
  var draw = "あいこ!";

  // 勝敗判定
  // アルゴリズム使用([http://staku.designbits.jp/check-janken/])
  // resultが0であいこ、1でCOMの勝ち、2でplayerの勝ち
  var result = ( playerHand - comHand + 3 ) % 3;
  var messages = [ draw, lose, win ];

  // 結果を表示
  alert( bothHandsStr + messages[ result ] );
}

CoffeeScript

janken = ->
  # 1がグー、2がチョキ、3がパー
  hands = ['ぐー', 'ちょき', 'ぱー']

  # プレイヤーの手
  playerHand = window.prompt "じゃんけんをしましょう!\n何を出しますか?\n1. ぐー 2. ちょき 3. ぱー"

  # 日本語入力に対応
  # 不正な入力があったらはじく
  unless playerHand in ['1', '2', '3']
    switch playerHand
      when 'ぐー', 'グー' then playerHand = 1
      when 'ちょき', 'チョキ' then playerHand = 2
      when 'ぱー', 'パー' then playerHand = 3
      else
        alert '何言ってるかわかんないです。'
        return

  # 内部処理用にplayerHandの値をhandsのインデックスに合わせる
  playerHand -= 1
  playerHandStr = hands[playerHand]

  # COMの手
  comHand = Math.floor(Math.random() * 3)
  comHandStr = hands[comHand]

  # 勝敗表示用の文字列
  bothHandsStr = "あなた: #{playerHandStr}\nCOM : #{comHandStr}\n"
  win = "あなたの勝ち!"
  lose = "あなたの負け…"
  draw = "あいこ!"

  # 勝敗判定
  # アルゴリズム使用([http://staku.designbits.jp/check-janken/])
  # resultが0であいこ、1でCOMの勝ち、2でplayerの勝ち
  result = (playerHand - comHand + 3) % 3
  messages = [draw, lose, win]

  # 結果を表示
  alert bothHandsStr + messages[result]

いかがですか?個人的にはcoffeeの方がかなり書きやすかったです。


CoffeeScriptの文法をさらっとなめてみました。DOMにも触ろうと思ったのですが、すでに相当な文量になってしまっているので、やめます。
CoffeeScriptを使えば、クライアントサイドのコーディングがかなり楽になりそうですね。