intime o'

たとえ自分が創りだした世界がどんなものだろうと、その世界で賢い判断を下せ (アダム・ファウワー著・矢口誠訳「数学的にありえない」・下より)
引き籠りのwhileループは変数にスコープを与えない - javascript 2012/08/14(Tue.)
最近はjavascriptの処理系としてnode.jsではなくてSpiderMonkeyを使うように
してる.それはたぶん、起動のコマンドが "node" よりも "js" のが短いから.
(エイリアスかければいいだけだけど)
それに、入出力関係や終了コマンドが短くラクだから.
SpiderMonkeyの入出力は、
readline()
print()
入と出で一つずつしかないが、node.jsよりはずっとマシだ.
終了は quit()と短いのもいい.

でも最適化に関して、SpiderMonkeyよりもnode.jsの方が優れていることを
私は発見した.つまり、末尾再帰についてnode.jsでは答えを出すのに
SpiderMonkeyではエラーが出る

確か私が試したのでは、普通に階乗を再帰で求めるというやつを実行してみて
function fact(n) {
    if (n < 2) return 1;
    else return n * fact(n-1);
}
これは割りとどちらでもすぐにスタックオーバーフローとか too much recursion
とかエラーを吐いて答えを出せなかった

function fact(n) {
    function fact_tail(n, acc) {
        if (n < 2) return acc;
        else return fact_tail(n-1, n*acc);
    }
    return fact_tail(n, 1);
}
でやってみるとは有効数字表示やInfinity だとかいう答えを一応許せば
n = 3000 のあたりでSpiderMonkeyは"too much recursion"を残して死ぬ
一方 node.jsはその10倍の n=30000 ほどでスタックオーバーフローとして
死ぬ.
ただ末尾再帰を末尾再帰と分かる処理系なら、そもそもスタックを消費する
はずはないので、これは醜い揚げ足取りだ.


本題.
ブロックが変数にスコープを与えないということ.

C/C++ に代表される手続き型言語では { } に囲まれるステートメント
をブロックとよび、ブロックの初めでは変数を宣言するのがC言語の
スタイルである.C99とか(もしかしたら違うかも、とにかくC言語の
新しい規格)C++では途中でも変数の宣言をできるけど.
その変数が使えるのは、宣言文のあるブロックの中で、宣言文以下
である.即ちそれがスコープであろう.

関数の宣言はブロックであり、forループ、whileループもブロックである.

javascriptにおいて、変数にスコープを与える、つまり変数の参照に
制限を与えるブロックは関数宣言だけである.

js> i
typein:8: ReferenceError: js is not defined
js> for (var i=1;i<10;++i);
js> i
10
js> for (var i=10;i<20;++i);
js> i
20

あー、でもfor(){}の()の中はブロックって言えるのかな.
たぶん言うよね.

js> k
typein:15: ReferenceError: k is not defined
js> for (i=0;i<10;++i) { var k = 1}
js> k
1
js> for (i=0;i<10;++i) { k = 3; }
3
js> k
3

var の有り無しによる挙動の変化はたまに罠になる.
上のコードでは変化は無いけれど.

forループ、whileループのブロックの中での変数は
その一つ外の変数と同じである.

こんなことはjavascriptを知っていると言う人なら
みな知っており、従って次のような技巧的な書き方も
知られている.

js> var s = 0;
js> (function(){
    var s = 1;
    print(s);
})();
1
js> s
0

functionブロックは変数にスコープを与える.
即ち、ブロックの中でvarをつけた変数宣言は関数の中でのみ
参照できるものであり、同じ名前の変数が外にあってもそちらは
参照されない.

スコープを利用して安全なコードを書きたいがために
上のように無名関数を宣言し、それを即座に実行する書き方は
見た目はよろしくないが、確かに見ることはある.

関数の宣言と実行を同時にする書き方として
function(){return 1}()
と書いて動くのもあるが、動かないのもある.
動かないものとは即ちSpiderMonkeyであり、以下のように書けば
普通どの処理系でも動く
(function(){return 1})()
関数の宣言をカッコでくくるのである.

var を付けない変数宣言というのは単なるグローバル変数への
代入文であり、

js> var s = 0;
js> (function(){
    s = 1;
    print(s);
})();
1
js> s
1

となるので註意.

コメ(0) | トラ(0)