FizzBuzzの世界

投稿者 nanki 2011-08-25 18:20:00 GMT

ちょっと興味深いfizzbuzzを発見したので。


Guardを使ったCoffeeScript開発

投稿者 nanki 2011-05-10 16:29:00 GMT

guardはファイルの変更を検知して、決められたタスクを実行するためのツールである。 Mac OS X / Linux / Windows それぞれのファイルシステム変更検知APIに対応しており、それ以外のプラットフォームではポーリングにて変更検知を行う。

$ gem install rb-fsevent # Mac OS X の場合
$ gem install guard
$ gem install guard-coffeescript
$ guard init coffeescript
$ vi Guardfile
guard 'coffeescript', :input => 'src/', :output => 'dist/'
$ guard start

こうすることで、src/ 以下の.coffeeファイルに変更があった場合即座にコンパイルされ、dist/以下に.jsファイルが生成される。

なお、rb-fseventは入れなくても動くがその場合はポーリングになる。 Mac以外のOSではこうするらしい。

$ gem install rb-inotify # Linuxの場合
$ gem install rb-fchange # Windowsの場合

guard-coffeescriptの他にも様々なguardがあるようです。

参考:

猫も杓子もCoffeeScriptの世の中、CoffeeScriptに手を出してしまったがために(JavaScriptの世に)帰らぬ人となりつつある人はたくさんいると思う。 僕も最近CoffeeScriptに手を染め五分で帰り途を見失ってしまった。

CoffeeScriptの編集にはujihisa氏のshadow.vimが便利だ。 shadow.vimを手短に紹介すると、main.js を開いて編集していると思ったら実はmain.js.shd(中身はCoffeeScript)を編集していて、気づいた時にはすでにmain.js.shdのコンパイル結果がmain.jsに保存されている、というvim-pluginである。

さて、JavaScriptを本格的に使うには、モジュールシステムを避けて通る事はできない。 グローバル変数を多用することも許されているJavaScriptでは、ライブラリによるオブジェクト空間の汚染は予測できない結果を引き起こしかねない。

モジュールシステムは未だ黎明期なので、本稿ではベストのものを選択するよりは、CoffeeScriptで書くクライアントサイド(以下CS)コードをサーバサイド(以下SS)でテストすることを念頭に、モジュールシステムの選び方を検討する、という方向で行こうと思う。 なお、ブラウザによる互換性は面倒くさいのでチェックしない。どうせやってることは一緒なのでいざとなれば解決可能な問題だし、各自で使用前にチェックしてください。

CommonJS

JavaScriptの処理系はちょっとリストアップするのが大変なほどある。

これらでばらばらのライブラリAPIを使われてはたまらないので、標準を決めようという事で、CommonJSと呼ばれる標準のようなものがあるらしい。 CS-APIではまだ存在する実装をまとめただけのような雰囲気もあり、CommonJS準拠だから選ぶ、ということにはならないが、 ありがたいことに、CommonJSに参加?しているプロジェクト一覧があり、これをベースに見ていくと楽できそうだ。

リストのうち、ブラウザ向けのものは

以下はリストには載ってないが見つけたやつ。

モジュールシステムについては、Modulesに関係する仕様と提案があり、これを参考にする。

Modules

おおまかに言うと、Modules/1.0では以下のようなrequireexports変数を使ったオブジェクトのエクスポートを要求している。

    // math.js
    exports.add = function(x, y) { return x + y }

    // increment.js
    var add = require('math').add
    exports.increment = function(val) {
      return add(val, 1)
    }

    // main.js
    require('increment').increment(2) //=> 3

SSの処理系ではこれは広くサポートされているようだ。

Modules/AsynchronousDefinition (AMD)

CSでは、スクリプトの読み込み方法が限られている(scriptタグの追加かXHRによる読み込み)し、同期的読み込みを前提とした上記のような定義方法は実用的ではない。 そこでModules/Transport/* という名前でいくつかの提案がされている。

歴史的な経緯はよくわからないが、Modules/Transport/Aの実装であったRunJSはModules/Transport/Cの実装であるRequireJSへと名前を変え、 Modules/Transport/CModules/AsynchronousDefinition(以下AMD)へと昇格?しているらしい。 AMDは以下のような形式で、他のものと比べて暗黙的な変数の導入がなく、スジがよいように思える。 これがデファクトになっていくのだろうか。

    // define(moduleId?, deps?, factory function / object to be exported)

    // math.js
    define({
      add: function(x, y) { return x + y }
    })

    // increment.js
    define('increment', ['math'], function(math) {
      return {
        increment: function(val) {
          return math.add(val, 1)
        }
      }
    })

CoffeeScriptとの相性もよい。これは今回は重要なポイントだ。参考までにCoffeeScript版も書いておく。

    // increment.coffee
    define ['math'], (math) ->
      increment: (val) -> math.add(val, 1)

Modules/1.xとの互換性を考慮した

    // increment.js
    define('increment', ['math', 'require', 'exports'], function(math, require, exports) {
      // これより内側がModules/1.xの定義そのままで動く
      exports.increment = function(val) {
        return require('math').add(val, 1)
      }
    })

といった書き方も用意されているようだ。

採用しているライブラリは

など。

Modules/Wrappings

    //increment.js
    module.declare(['math'], function(require, exports, module) {
      return {
        increment: function(val) {
          return require('math').add(val, 1)
        }
      }
    })

あんまり違いがわからないが、ポイントはfactory関数の引数がrequire, exports, moduleの三つで固定されていることだろうか。その名前から、Modules/1.xとの互換性目的のものと思われる。 exportsを使ってもよいし、factory関数の返り値としてオブジェクトを返せばそれがそのままモジュールになる。

Modules/Async/A

AMDに対応したローダーの仕様はModules/Async/Aに書かれている。 といっても、Yabbleの実装(下記)をそのまま書いてあるだけのような気がするが。 対応ライブラリはYabbleのみしか書かれていない…え…

結局のところ、AMDはdefineの文法的形式が決まっているだけで、ローダにはまだ決定打がない。 コードを書く際はローダの呼び出しを最小限にするのが良さそうだ。

この辺がCommonJSの限界のようなので、あとは各々の実装を見ていくのがよさそうだ。

Yabble

    // increment.js
    require.define({
      increment: function(require, exports, module) {
        var math = require('math')
        exports.increment = function(val) {
          return math.add(val, 1)
        }
      }
    }, ['math'])

    // main.js
    require.ensure(['increment'], function(require) {
      require('increment').increment(2); // 3
    });

第一引数に依存するモジュールを、第二引数にcallbackを指定する。モジュールのロードが完了すると、callbackが呼ばれる。

テストコードも豊富でなかなか完成度の高いコードだ。 しかし、SSで使われることは想定されておらず、本体に手を入れずに使うことはできなさそう。 SSのテストコードでは利用する機能を搾って、同じインターフェースを持つ他のライブラリに丸投げする事が可能だ。 (例えばNodulesは、require.ensureを持っている)

Nodules

    //main.js
    require.ensure(['increment'], function(require) {
      require('increment').increment(2); // 3
    });
$ node PATH_TO_NODULES/lib/nodules.js main.js

こんな感じで。

Nodulesの機能のこれはごく一部で、この他に

  • HTTP上のjsをrequireする(そのためのrequire.ensure)
  • Packages/Mappingsの実装

など。

コマンドラインからの起動が必要なのはマイナスポイント。 あと、defineの第一引数を指定すると、ファイル名(フルパス)と違うと怒られた。

RequireJS

同じものがRequireJSではこうだ。

    require(['increment'], function(increment) {
      increment(2); // 3
    });

シンプルすぎて泣ける。かっこいい。 なお、defineは、一つのファイルに複数のモジュールを記述することを許しているがその場合は、

    //libs.js
    define('math', {  })
    define('increment', ['math'], {  })

    //main.js
    require(['require', 'libs'], function(require) {
      require('math').increment(2); // 3
    });

というように多段にしてrequireすればよい。

RequireJSはSSでの(node, rhinoに対応)実行も行えるようになっており、

$ PATH_TO_REQUIREJS/bin/x main.js

とすればいい。この時、engine-nativeのrequire関数はオーバロードされる。 ただこの方法だと、CoffeeScript中のrequireなどは置き換えられないため若干使いづらいし、そもそもrequireを汚染されるのは気持ちのいい物ではない。

これに関しては、wrapperを書く事で、通常のライブラリと同様読み込むことができるようだ。

    // requirejs-node.js
    (function() {
      var read = require('fs').readFileSync
      var run = require('vm').runInNewContext

      var env = {
        importScripts: function(url) {
          run(read(url), env)
        }
      }

      var code = read(require.resolve('require'))
      run(code, env)

      exports.require = env.require
      exports.define = env.define
    }).call()

    // main.coffee
    rjs = require 'requirejs-node'
    rjs.require ['your_library'], (library) -> ...

rjs.requireで読み込まれるライブラリ内ではrequire = rjs.require, define = rjs.define となる。

Dojo

Dojoでは1.6以降AMD形式のライブラリを提供していく予定らしい。 Loaderは提供しないので、今回は関係ない。

LoaderにはbdLoad / RequireJS を使えとのこと。

backdraft / bdLoad

backdraft frameworkの中のbdLoadというのがそれらしい。

のが売りだそうだ。

RequireJSのテストコードがレポジトリに含まれていることから、RequireJSのよりよい実装と考えてもよさそうだ。

NodeJS用のインターフェースもデフォルトで用意されている。

上記RequireJSではwrapperを書いたが、bdLoadではデフォルトでほぼ同じ様になる。

    // bdload-node.coffee
    bdLoad = require('node').boot()

    bdLoad.require
      paths:
        mylib: './mylib/lib'

    bdLoad.require ['mylib/mylib'], (mylib) -> ...

これはかなりいい。RequireJSを選ぶ理由はほぼない気がする。RequireJSはリファレンスマニュアルがかっこいい。

ちなみに、backdraftレポジトリをcloneしようとしたら、AAを用いたCAPTCHAのようなものが表示され、フォントが違うので読めなくてログインできない。CAPTCHAは見る価値あり。

OzJS

作者(dexteryy)はtudou.comのフロントエンジニアらしい。 スライドは中国語で詳しくはわからないが、かなり詳細に論じている様子が伺える。 他のライブラリに比べると圧倒的にサイズが小さいし、TUICompilerというのを使って静的に複数のスクリプトをまとめることができるようだ。

ただし、NodeJSからは使えなかった。 あとoz.defという名前で微妙にAMDと互換性がないので、気にする人はdefine = oz.defしてそっちを使うのがよいかもしれない。

    // increment.js
    var define = oz.def
    oz.def('math', 'math.js')

    define('increment', ['math'], function(math) {
      return {
        increment: function(val) {
          return math.add(val, 1)
        }
      }
    })

    // main.js
    oz.require(['increment'], function(increment) {
      increment.increment(2) //=> 3
    })

FlyScript

Modules/Wrappingsを採用しているFlyScriptだが、何があったのかgithubのレポジトリが消えている。 作者の他のレポジトリも丸々ないので公開やめたのかな…

SeaJS

Modules/Wrappingsなのでこんな感じ。

    //increment.js
    define(['math'], function(require, exports, module) {
      return {
        increment: function(val) {
          return require('math').add(val, 1)
        }
      }
    })

    //main.js
    seajs.config({
      base: './...',
      alias: {
        //...
      }
    })

    seajs.use(['increment'], function(increment) {
      increment.increment(2) //=> 3
    })

SSでは動かなかった。 Backbone.js系のライブラリを添付してくれていて助かるが、必ずmoduleId.jsを読みに行ってしまうみたい。 これは、production環境でファイルをまとめたい時などには困るだろう。 お手軽にBackbone.jsで遊ぶ専用か。

JSLocalnet

  • ライセンスがGPLv3/プロプライエタリ
  • XHRにしか対応してない
  • IE8非対応
  • コメントがスペイン語
  • レポジトリがhg
  • テストコード一切無し

と、異なる文化圏にあるようでちゃんと見るのやめました。

PINF JS Loader / bravojs

Modules/2.0!

Modules/Wrappingsとの違いがよく分からない…

未実装の部分が多いが、完成するとかなりの処理系(JetPack, NodeJS, browsers…) にまたがって共通のモジュールシステムを提供することになるようだ。 Modules/2.0提案用のリファレンス実装という色が濃く、unstable感が高いので今回はパス。

curl.js - updated on 2010/05/05

curl.jscujo platform用のAMDローダ。 以前まではdojoのローダを使っていたようだが、今はAMDを使うようになったamdブランチがあるようだ。 まだリリースされて一ヶ月とあまり情報もないので見逃していた。 SSでは動かない。

  • 素直にAMDを実装
  • next, thenなど独自機能
  • ‘text!xxxx.text’ でテキストファイルをロード
  • ‘js!xxxx.js’ でnon-AMDなjsをロード

といった感じ。特に最後の拡張は、AMD非対応jsが多い現状を考えると面白い。

作者のスライド(オススメ)によれば、AMDではrequireはThe Wild Westだという。 しかし、requireの呼び出しはコードの1%にも満たないので、もっとやれ!ということのようだ。

    curl({
      baseUrl: '...',
      paths: ['.']
    }, {})

    require(['increment'], function(increment) {
      increment.increment(2); // 3
    });

    // 別の書き方
    require(['increment'], (function(increment) {
      increment.increment(2); // 3
    })
    .next(['js!jquery.js!order', 'js!zoom.js!order'])
    .then(function() {/* success */}, function(e) {/* error */});

サイズ

CSで利用する際に気になるのはサイズだが各ライブラリの主要ファイルのサイズを以下の方法で調べた。

$ uglifyjs -nc --unsafe < xxx.js | wc -c
$ uglifyjs -nc --unsafe < xxx.js | gzip -c | wc -c

OzJSがもっとも小さく、RequireJSが最も大きいがその差は数倍程度の開きでしかなく、あまり大きな要素にはならないように感じた。 まとめの項に表を示す。

まとめ

CSのモジュール定義はAMDでほぼ確定、ローダは標準なしということで、 様々なモジュールローダを見て来たが、SS/CSで動くよう作られたライブラリはやはり少なかった。 現時点では、SS/CSで使うライブラリをわけるのが選択の幅を狭めない妥協点だ。

| library    | license             | server-side | size  | gziped | nank  |
| RequireJS  | MIT / mBSD          | ?           | 16486 | 5788   | ★★★☆☆ |
| bdLoad     | BSD                 | o           | 11221 | 4714   | ★★★★★ |
| OzJS       | MIT                 | x           | 3143  | 1543   | ★★★★☆ |
| SeaJS      | MIT                 | x           | 5489  | 2319   | ★★☆☆☆ |
| Yabble     | MIT                 | x           | 6635  | 2468   | ★★★★☆ |
| bravojs    | MIT                 | o           | 8646  | 2671   | ★★★☆☆ |
| curl.js    | MIT                 | x           | 4627  | 2247   | ★★★★☆ |
| JSLocalnet | GPLv3 / Proprietary | x           | -     | -      | -     |
| FlyScript  | MIT                 | -           | 8594  | 3831   | -     |
| Nodules    | mBSD / AFL2.1       | o           | -     | -      | -     |

PINF JS Loader / bravojs の組み合わせは今回の様な用途を本気で目指したものであり、今後注視しておきたいが、今変化の直中であり手を出しづらい。

また、中国製のライブラリがいくつか(OzJS, SeaJS)見受けられ、中国でJavaScriptがキてるのが感じられる。 この組み合わせは強そうだ。

バランスがいいと感じたのはbdLoadであったが、さらに踏み込んで使いたい場合はOzJS / curl.js もよい選択だと思う。

おまけ

名前が挙ってたので調べたけど、関係なさそうなやつ。

Teleport

teleportは、browserでの開発にNodeJSのエコシステムを持ち込むものらしい。

$ npm init # package.json作成
$ npm link
$ vi index.html
$ teleport activate

とすることで、開発中のpackageにhttp://localhost:4747/PACKAGE_NAME/でアクセスできるようになる。 http://localhost:4747/teleport-dashboard/でnpm管理下のパッケージ一覧が見られる。 browser上でrequire('...')などとすると、npmパッケージをロードすることができる。

この時、Modules/1.x 形式のモジュールを、defineでくるんでCSに送信する。

また、require/define がすでに定義されている場合は、上書きしないような配慮がされている。 うまく開発プロセスに組み込めば、ブラウザによるテストも楽になりそう。

リリース時には、$ teleport bundle PACKAGE_NAME/app とすることで、必要なパッケージを展開?してくれるようだが、これはバグで動かなかった。

Transporter

Transporterでは、Modules/1.0モジュールを、様々なtransport形式でCSに提供するSSの仕組みを提供している。 CS-APIは、require.ensure / defineを使うようだが、非同期読み込みには対応しておらず、自分であらかじめscriptタグで読み込んでおく必要があるようだ。 利点がわからない…誰か教えて…

参考:

iPhoneアプリPhotosynthで鴨川デルタを撮影

投稿者 nanki 2011-04-20 10:14:00 GMT

こういうエントリはほとんど書かないんだけど、Photosynthはそのはじめのころからずっと見ていたし、首の消えてしまったおじさん(プライバシーに配慮して)の向うには出町ミスドがあるし、なによりPhotosynthが素晴らしかったので。

iPhone持ってる人は一度は試してみるべき。共有しなければアカウント作成とか要らない。

一部破綻が見られるけど、iPhoneで誰でも撮れるというのはかなりインパクト大きい。 bing map今からでもGoogleMapsと張り合える。

しかし相変わらず弱そうなソーシャルパートw 外部にライセンスするか、api公開していい会社買った方がいいものできそう。


Mac OS X で変な漢字を探す亖つの方法

投稿者 nanki 2011-04-20 07:05:00 GMT

元々の話題は、火が三つの漢字を見た事あるけど見つからない、というものから。

1. 手書き文字入力 / ⌃⌥ Space / Ctrl+Shift+Space

手書き文字入力

詳しくは下記URLの記事を参照。

2. ⌃1, ⌃2 / Ctrl+1, Ctrl+2

部品の共通な漢字を検索 対象となる文字を選択後、Ctrl+1を押す

関連文字に変換 対象となる文字を選択後、Ctrl+2を押す

3. 文字ビューア

「文字ビューアを表示」を選択

記号など探すには便利。地道に探す。 文字ビューア

4. CHISE/漢字構造情報データベースを使う

CHISE IDS Findを使って検索するか、CHISEプロジェクトからデータベースをダウンロードして自分で探す。

今回のように三つの文字が並んでいる漢字を探す場合、例えば”森”、という字なら、

… 森 ⿱木⿰木木

という感じの表現で入っているはずなので、UTF-8を正しく扱えるシステム上で /\t⿱(.)⿰\1\1$/ という正規表現を使えば、同じ漢字が三つ並んだ漢字の一覧を得ることができる。 上記は簡単だが手抜きなやり方で、完全性に関してはいくつか問題がある。

  1. 文字に対する表現の曖昧性 例えば、森に対しては⿱木林 / ⿱木⿰木木の二つの表現が考えられる。検索時にこれを考慮しなくてはならない。(同じ漢字が四つならぶ時など考えたくない…)

  2. 包摂の扱い 同じ漢字なんだけど字形が違う場合、例えば「」の各部品はトメ、ハライの違いを考慮してデータベースに登録されており、上記のような単純な正規表現ではこれは扱う事ができない。

というわけで、今回見つけられた同じ漢字が三つの文字(ただしSnowLeopardで表示されるものに限る)は

post先のサーバがどれかの文字を正しく扱う事ができず途中で切れてしまったので画像で失礼。

参考: