enchant.jsのクソな部分

enchant.js、いいライブラリなんだけど「これってどうなの?」って思うところもあって、それを箇条書きにしていく。

  • リポジトリ
    • ブランチに master と develop と develop-env と v0.5.0 がある
      • たぶん新機能は develop で開発するんだろうけど、 master にも色々コミットされていたりする
      • pull-request はどのブランチにすればいいの
      • どうして v0.4.4 とかはタグで管理してるのに v0.5.0 はブランチなのか
        • v0.5.0 ブランチは develop ブランチと何が違うのか
    • git-flow とか参考にすればいいと思う
  • コーディング規約
    • 場所によって書き方が違ったりする
      • if 文の { の前にスペースを入れるか、とか
    • ひどい所だとインデントでスペースとタブが混ざってたりする
      • 関係ないけれど、コード書くエディタはホワイトスペースと全角スペースとタブを区別できないとダメだと思う
  • テスト
    • tests ディレクトリに少ししかない
  • ブラウザごとの違いとかバグとか
  • dev/enchant.js
    • サイズが大きすぎる
      • 読むときスクロールが大変
      • jQuery みたいにファイルを分割すればいいのに
        • どうせ rake コマンド使うんだし
  • スパゲッティ
    • ひとつのメソッドが30行超えているのは、よくないと思う
      • asset 関係のメソッド追うのに一苦労
    • プログラマにソースのよくからまったスパゲッティを見せると死ぬ
      • 死なない
  • rotate() とか scale()
    • なんで Entity じゃなくて Sprite のメソッドなの
    • transform-origin の指定がやりにくくて不便
    • 拡大しても当たり判定はそのままなのが少し面倒くさい
  • addEventListener()
    • 長い
    • どうして addListener() とか on() とかにしなかったの
      • DOM に合わせたんだろうけど、そんな必要なかった
  • Sound
    • クソ
    • うんち

Homebrew で何かインストールするとき "Permission denied" と言われたときの対処法

未来の自分の為に書き残しておく。


今日Macで

brew upgrade

したら、coreutils のインストール中に "Permission denied" と出て終わってしまった。
それなら、と

sudo brew upgrade

したら、今度は "Cowardly refusing" と言われて実行すらされない。


調べたら同じ状態になった人がいたみたいだ。
https://github.com/mxcl/homebrew/issues/12297
この人は lame で止まったみたい。
解答されているように

sudo chown -R $USER /usr/local

と権限を治して、事無きを得た。


もしかしたら、ディスクユーティリティで権限の修復をしても治ったのかもしれない。ディスクユーティリティで権限の修復ではダメなようです
最初 coreutils の問題だと思って、数時間はまってしまった。

wise9に記事を投稿して、それが掲載されました

wise9 › ダンジョンマップを生成するアルゴリズムの解説[投稿記事]
ざっくりと自分が何をやったのか、書き残しておきます。


まず、原稿をGoogle Docsで書きました。
図を描いて挿入する機能があるので便利です。


書いたら、HTML(と画像をまとめたZIP)としてダウンロードしました。
(ついでに、マルチバイト文字がすべてエスケープされていて、悲惨なHTMLだったのを直しました)
このページに書かれているアドレス宛に必要事項を書いて、HTMLと画像をまとめたZIPを添付してメールしました。


簡単なので、みんな記事を書いて投稿すればいいと思います。

JavaScriptで関数を非同期呼び出しする

よくあるパターン。

setTimeout(function(){
  // 非同期でしたい処理
}, 0);

これ結構パフォーマンス悪いみたい。

node.jsの場合

process.nextTick(function(){
  // 非同期でしたい処理
});

最新のIEの場合

setImmediate というAPIが使えるらしいからそれを使う。

setImmediate(function(){
  // 非同期でしたい処理
});

それ以外のブラウザ

MessageChannel を使うのがいいっぽい。

var ch = new MessageChannel();
ch.port1.onmessage = function(){
  // 非同期でしたい処理
};
ch.port2.postMessage(0);

まとめ

だいたいこんな感じ。
uncurryThis はこの記事で知りました。

var asyncCall = function(){
  var _slice = Array.prototype.slice;
  var _bind = Function.prototype.bind || function(thisObject){
    var callee = this;
    var args = _slice.call(arguments, 1);
    return function(){
      var a = _slice.call(arguments);
      return callee.apply(thisObject, args.concat(a));
    };
  };

  var uncurryThis = _bind.call(_bind, Function.prototype.call);
  var slice = uncurryThis(_slice),
    bind = uncurryThis(_bind),
    apply = uncurryThis(Function.prototype.apply);

  return typeof process !== 'undefined'? function(){
    var args = slice(arguments);
    process.nextTick(apply(bind, null, args));
  }: typeof setImmediate === 'function'? function(){
    var args = slice(arguments);
    setImmediate(apply(bind, null, args));
  }: typeof MessageChannel === 'function'? function(){
    var ch = new MessageChannel(), queue = [];
    ch.port1.onmessage = function(){ queue.shift()(); };
    return function(){
      var args = slice(arguments);
      queue.push(apply(bind, null, args));
      ch.port2.postMessage(0);
    };
  }(): function(){
    var args = slice(arguments);
    setTimeout(apply(bind, null, args), 0);
  };
}();

第1引数に非同期で呼び出したい関数、第2引数にthisになるオブジェクト、第3引数以降に引数を指定する。

asyncCall(function(a, b, c){
  this.valueOf(); // 0
  a; // 1
  b; // 2
  c; // 3
}, 0, 1, 2, 3);

のように使える。

SConsでJavascriptを組み立てる

JavaScriptでも一定以上の規模のプログラムは、
機能ごとにファイルを分けたほうがいいと思います。
jQueryでもそうですし。


そんなわけで「分割したファイルをひとつにする方法」を簡単にメモしておきます。
Python が好きなので make じゃなくて SCons を使います。
Python の機能を使って色々できるところが make より嬉しいです。


事前準備

  1. Pythonをインストール
    • Windows以外は最初から入ってると思います、たぶん
  2. SCons を easy_install 使ってインストール
    • 自分の Mac では pip install でインストールしようとしたら失敗しました
  3. scons -v でちゃんとインストールされたか確認しておく
  4. Windowsの場合はgowを入れてパスを通しておくと捗ります

簡単なSConsの使い方

プロジェクトのフォルダに SConstruct という名前のファイルを作成して
どういう処理をするか書いていきます。

Command('target.js', ['source1.js', 'source2.js'], 'cat $SOURCES > $TARGET')

と書いて

cd path/to/project/folder
scons

ってやると source1.js と source2.js をくっつけた target.js が作られます。
二回目以降は source1.js か source2.js が変更された時だけ実行されます。
cat とかのコマンドがわからない人は調べてください。


応用

スクリプト全体を匿名関数で囲いたい場合

Command('target.js', ['source1.js', 'source'], [
    'echo "(function(){" > $TARGET',
    'cat $SOURCES >> $TARGET',
    'echo "})();" >> $TARGET'
    ])

echo で前後に書き加えているだけです。


Closure Compilerを使いたい場合

Command('target.js', 'source.js', 'java -jar compiler.jar --js $SOURCE --js_output_file $TARGET')

compiler.jarを事前に用意しておく必要があります。
複数のファイルをコンパイルするときはちょっと工夫する必要があります。


まとめ

ファイル分割すると見通しがよくなるし、なにかと便利です。
単体テストもやりやすくなります、たぶん。
はやりの CoffeeScript のコンパイルにも使えるはずです。

話題のソートアルゴリズム「sleep sort」をJavascriptで実装したよ

「sleep sort」については以下のリンクを見てもらうとして
4chan BBS - Genius sorting algorithm: Sleep sort常識を覆すソートアルゴリズム!その名も"sleep sort"! - Islands in the byte streamはてなブログBig Sky :: Sleep sort in Go


まず本題のコード

function sleepSort(array, callback, f){
    var l = array.length, result = [];
    var i = l;
    
    if(f == null)
        f = Number;
    
    while(i--) (function(value){
        setTimeout(function(){
            result.push(value);
                if(--l === 0)
                    callback(result);
        }, f(value));
    })(array[i]);
}


arrayにソートしたい配列、
callbackにコールバック関数、
fに配列の値を数値に変換する関数(省略可)
をそれぞれ指定するとcallbackにソートされた結果が渡されます。


値が負だったり実数だったりすると、
ちょっと工夫しないと使えません。


勢いで書いたコードなので、
whileの中で匿名関数使っているなどの問題もあります。
そもそも、このアルゴリズムに使いどころがあるのか疑問ですが。


最後にサンプル

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <script>
        function sleepSort(array, callback, f){
            var l = array.length, result = [];
            var i = l;
            
            if(f == null)
                f = Number;
            
            while(i--) (function(value){
                setTimeout(function(){
                    result.push(value);
                    if(--l === 0)
                        callback(result);
                }, f(value));
            })(array[i]);
        }
        
        window.onload = function(){
            function randint(){ // ランダムな256未満の整数
                return Math.random() * 256 | 0;
            }
            
            var array = [], i = 100; // ランダムな整数100個の配列を作る
            
            while(i--)
                array.push(randint());
            
            // もとの配列を表示 (before : 〜)
            document.getElementById('array').textContent =
                JSON.stringify(array, null, 4);
            
            // ソート
            sleepSort(array, function(result){
                // 結果を表示 (after : 〜)
                document.getElementById('result').textContent =
                    JSON.stringify(result, null, 4);
            });
        };
    </script>
    <style>
        div { margin: 1em 0 }
        #array::before { content: 'before :' }
        #result::before { content: 'after :' }
    </style>
</head>
<body>
    <div id="array"></div>
    <div id="result"></div>
</body>
</html>


ここまで30分で書けたあたりが、
このアルゴリズムのすごいところかも。