CoffeeScriptでObserverパターン

javascriptパターンの7章のサンプルをCoffeeScriptでかきなおしてみた。オリジナルのコードはこれ

コンソールログを開くと実行されていることがわかる。

ProductName JavaScriptパターン ―優れたアプリケーションのための作法
Stoyan Stefanov
オライリージャパン / 2940円 ( 2011-02-16 )


CoffeeScriptで書きなおしたほうの実行結果。ターミナルで確認できるので楽ですね。

$ coffee pubsub.coffee 
Just read big news today
Just read big news today
Just read big news today
About to fall asleep reading this interesting analysis

コード。デバッグ用にprintSubscribersっていうメソッドを付けた。それからオリジナルコードはmix-inしていたので最初はfor-of文でそのとおりに書いたんだけど、for-of文でtypeof methodするとfunctionじゃなくてstringが返ってくるのが嫌だったので、継承を使って実装してみた。

class Publisher
  constructor: ->
    @subscribers = { any:[] }

  subscribe: (fn, type) ->
    type = type or 'any'
    @subscribers[type] = [] unless @subscribers[type]?
    @subscribers[type].push(fn)

  unsubscribe: (fn, type) -> @visitSubscriers('unsubscribe', fn, type)

  publish: (publication, type) -> @visitSubscriers('publish', publication, type)

  visitSubscriers: (action, arg, type) ->
    pubtype = type or 'any'
    subscribers = @subscribers[pubtype]
    for subscribe,i in @subscribers[pubtype]
      switch action
        when 'publish' then subscribe(arg)
        else @subscribers[pubtype].splice(i,1) if subscribe is arg

  printSubscribers: -> console.log(@subscribers)

class Paper extends Publisher
  daily: => @publish('big news today')
  monthly: => @publish('interesting analysis','monthly')

class Subscriber
  drinkcoffee: (paper) -> console.log('Just read '+ paper)
  sundayPreNap: (paper) -> console.log('About to fall asleep reading this '+ paper)

paper = new Paper
joe = new Subscriber
paper.subscribe(joe.drinkcoffee)
paper.subscribe(joe.sundayPreNap, 'monthly')
#paper.printSubscribers()
paper.daily()
paper.daily()
paper.daily()
paper.monthly()

この本どうなんだろう、一度読んでおくべきなのかな?

ProductName CoffeeScript: Accelerated JavaScript Development
Trevor Burnham
Pragmatic Bookshelf / 2355円 ( 2011-08-03 )


CoffeeScriptでPythonでいうところのenumerateが使いたい

for loopを二回かけたい場合。

つまりjavascriptではこんな感じのよくある2重ループ。

for (var i =0; i<a.length; i++) {
  for (var j = i+1; j <a.length; j++) {
    # なんか
  }
}

こういうループを回したい場合はpythonでいうところのenumerateとかは必要なくて二番目の返り値にインデックスが返るっぽい(返り値の数をチェックしてんのかな?よくわからないが)

for el1, i in a
  for el2 in a[i+1..]
    #なんか

となるので、非常にわかりやすい。

HTML5 Canvasを読み始めた

600ページ超の熱い本だが、一通りきちんとコードを追いかけようと。

ProductName HTML5 Canvas: Native Interactivity and Animation for the Web
Steve Fulton
Oreilly & Associates Inc / 2922円 ( 2011-05-13 )


ただ写経するだけではつまらないので、coffeescriptで書いていくことにする。

class Debugger
  log: (message) ->
    try
      console.log(message)
    catch exception
      return

canvasSupport = -> Modernizr.canvas

canvasApp = ->
  return if !canvasSupport()

  theCanvas = document.getElementById("canvasOne")
  context = theCanvas.getContext("2d")

  d.log("Drawing Canvas")

  drawScreen = ->
    context.fillStyle = "#ffffaa"
    context.fillRect(0, 0, 500, 300)
    context.fillStyle = "#000000"
    context.font = "20px _sans"
    context.textBaseline = "top"
    context.fillText("Hello World!", 195, 80)

    context.strokeStyle = "#000000"
    context.strokeRect(5, 5, 490, 290)

  drawScreen()

d = new Debugger
canvasApp()

Express+jade+CoffeeScriptという構成で書いてみている。 コードはGitHubに置いてある。

node.jsのweb server exampleをcoffee-scriptで書いてみた

今朝は寝坊したせいで朝コードを書く時間があまり取れなかった。

Node.jsのversionが上がっていたのでnvm installをしつつ、coffee-scriptをちょっとさわる

http = require('http');

http.createServer((req, res) ->
  res.writeHead(200, {'Content-Type': 'text/plain'})
  res.end('Hello World\n')
).listen(1337, "127.0.0.1")
console.log('Server running at http://127.0.0.1:1337/')

ちなみに普通にjavascriptで書くとこうなる。

var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
}).listen(1337, "127.0.0.1");
console.log('Server running at http://127.0.0.1:1337/');

普段Pythonで書いてると文の最後の;とか忘れがちなのでcoffee-scriptは良かったりする

coffeescriptを触ってみている

ついでにnode.jsのアップデートをしつつ、npmを入れた。/usr/localのownerを変更するのは気持ち悪いが、自分しか使わないのでまぁいいかなと(参考)。

注)こっちのほうが良いやり方らしい

tar xvfz node-v0.4.9.tar.gz 
cd node-v0.4.9/
./configure 
make
sudo make install

sudo chown -R $USER /usr/local
curl http://npmjs.org/install.sh | sh

npm install -g coffee-script

さて、CoffeeScriptとはjavascriptにコンパイルできる小さな言語です。文法がPythonっぽいのでとっつきやすそう。

関数適用に括弧がいらない

引数が一つのときだけ?

coffee> add = (x,y) -> x+y
[Function]
coffee> add(1,2)
3
coffee> add 1 2
Error: In repl, Parse error on line 1: Unexpected 'NUMBER'
    at Object.parseError (/usr/local/lib/node_modules/coffee-script/lib/parser.js:472:11)
    at Object.parse (/usr/local/lib/node_modules/coffee-script/lib/parser.js:548:22)
    at /usr/local/lib/node_modules/coffee-script/lib/coffee-script.js:29:22
    at Object.eval (/usr/local/lib/node_modules/coffee-script/lib/coffee-script.js:88:10)
    at Interface.<anonymous> (/usr/local/lib/node_modules/coffee-script/lib/repl.js:39:28)
    at Interface.emit (events.js:64:17)
    at Interface._onLine (readline.js:153:10)
    at Interface._line (readline.js:408:8)
    at Interface._ttyWrite (readline.js:585:14)
    at ReadStream.<anonymous> (readline.js:73:12)

coffee> add1 = (x) -> x+1
[Function]
coffee> add1 3
4

内包標記が使える

coffee> numbers = [1,2,3,4,5]
[ 1, 2, 3, 4, 5 ]
coffee> odds = (n for n in numbers by 2)
[ 1, 3, 5 ]

なかなか楽しげ

ベターJavaScript!? CoffeeScriptが注目されるワケ

Ashkenas氏によれば、JavaScriptからCoffeeScriptに移植したあるライブラリは、元の1/3のコード量になったものがあるといいます。また、JavaScriptにある仕様上の欠陥や落とし穴の多くはCoffeeScriptによって避けられるとしています(欠点を知り、それらを避ける方法をまとめた「JavaScript: The Good Parts――「良いパーツ」によるベストプラクティス」という書籍もありますね)。

ProductName JavaScript: The Good Parts ―「良いパーツ」によるベストプラクティス
Douglas Crockford
オライリージャパン / 1890円 ( 2008-12-22 )


JavaScriptにある仕様上の欠陥や落とし穴を知っておくということも大切ですね。というわけで、次回の読書会は7/30に静岡でやる予定になっています。