とりあえず、こんな感じにプログラム書いて、コンパイルして、実行してみましょう。
sum.js
----------------------------------------
var sum : long = 0;
for(var i : int = 1; i <= 100000000; i++)
sum += i;
print(sum);
これは1から1億まで足し合わせた数を出力するプログラムです。
コマンドラインから、
jsc sum.js
としてコンパイルし、
sum.exe
として実行しましょう。あっという間に答えが出ましたね。
JITコンパイラなのにめちゃくちゃ速そうですね。
この辺についてはあとから触れます。
通常のモードでは、変数は宣言しないと使えません。
var a : long;
aを
long型と宣言しています。
longは64ビットの整数です。
従来どおり型を指定しない方法もあります。
この場合は、内部的に色々な型に変わることもできるのでしょう。
var a = 1; //内部的には整数
print(a);
a = "sss"; //文字列に
print(a);
上の例もそうですが、次のようにすると変数を初期化できます。
var a : int = 1;
aを
intで宣言して1で初期化しています。
見た目がすごく悪く感じるのは私だけでしょうか。
複数の変数をまとめて宣言するには次のようにします(5/6/03)。
var a : int, i : int;
次のようだとaの型が決まりません。
var a, i : int;
要はVBと同じなわけですが、冗長ですね。
printステートメントで標準出力できるようです。
var a : int = 1;
print(a);
引数はStringオブジェクトですが、
そうでない場合は自動的に変換されるようです。
おそらく、ToStringメソッドが発効されるのでしょう。
WScript.Echoと書くよりずっといいですよね。
従来のJScriptと比較するために、
上のプログラムをちょっと変えた次のプログラムを動かしてみましょう。
sum_euler.js
----------------------------------------
import System;
var t : int = Environment.TickCount;
var sum : double = 0.0;
for(var i : int = 1; i <= 1000000000; i++)
sum += 1.0 / double(i);
print(sum);
print((Environment.TickCount - t) + "ms");
これを実行すると、結果(log(1e9) + 0.57...になるはず)と、
実行時間が表示されます。
うちのAthlon1.2GHzでは14280ms、すなわち14.280sでした。
JITコンパイラが働く部分が入っていない時間とはいえ速いです。
従来のJScriptだとどうなるか、ブラウザ上から動かしてみましょう。
ソースは次のようです。
function sum_euler() {
var t = (new Date()).getTime();
var sum = 0;
for(var i = 1; i <= 1000000; i++)
sum += 1 / i;
var dt = (new Date()).getTime() - t;
alert(sum);
alert(dt + "ms");
}
次のボタンを押してください。結果と実行時間が表示されます。
うちでは791msとなりました。
ループの回数が3桁少ないので、
単純計算で55倍の時間がかかっていることになります。
これくらい速いとC++と比較してもよさそうです。
sum_euler.cpp
----------------------------------------
#include <stdio.h>
#include <windows.h>
#define _EULER1 1
double euler(int n) {
double result = 0.0;
for(int i = 1; i < n; i++)
result += 1.0 / (double)i;
return result;
}
//パラレル版
double euler2(int n) {
double result1 = 0.0;
double result2 = 0.0;
for(int i = 1; i < n; i += 2) {
result1 += 1.0 / (double)i;
result2 += 1.0 / (double)(i + 1);
}
if(n % 2) //odd
return result1 + result2 + 1.0 / (double)n;
else //even
return result1 + result2;
}
void main(void) {
int t;
t = GetTickCount();
#ifdef _EULER1
printf("%f\n", euler(1000000000));
#else
printf("%f\n", euler2(1000000000));
#endif
printf("%dms\n", GetTickCount() - t);
}
Borland C++ 5.5.1でコンパイル、実行してみると、17.605s。
あれ?JScript.NETの方がC++より速いんですか。
上のプログラムの4行目を、
#define _EULER2 1
としてみましょう。
こうすると実行される関数がeulerでなくeuler2になります。
こういうプログラムはCPU内でパラレル化できます。
いまどきのCPUは演算する部分が複数あるので、
互いに依存性のない命令は同時に実行できます。
互いに依存性がないというのは、
次の2つのコードのように相手の結果に自分の結果が左右されないことをいいます。
result1 += 1.0 / (double)i;
result2 += 1.0 / (double)(i + 1);
こういう風に書くとたいてい実行速度が速くなるのですが、
BCCだと速くならないんですね。
Visual C++だと、14.280s。
つまり、JScript.NETと同じ。すごいですね。
これって、JITコンパイラがパラレル化してるってこと?
別のコードでも試したのですが、
このときはパラレル化したC++よりかなり速かったです。
こういうプログラムを作ると、
sum 723
みたいにコマンドライン引数を取って、
1 + 1/2 + ... + 1/723
を計算するみたいなことをしたいですね。
これは次のようにします。
sum_euler2.js
---------------------------------------------------------
import System;
var n : int; //引数の自然数
//引数を格納する配列
var args : String[] = Environment.GetCommandLineArgs();
//引数の処理
try {
if(args.length != 2) //引数の数チェック
throw(1);
n = parseInt(args[1]); //ここで例外が発生するらしい
if(isNaN(n))
throw(1);
if(n <= 0) //正かどうかチェック
throw(1);
}
catch(e) {
print("usage : sum_euler2 n\nn : positive integer.");
Environment.Exit(1);
}
print(euler(n));
function euler(n : int) : double {
var sum : double = 0.0;
for(var i : int = 1; i <= n; i++)
sum += 1.0 / double(i);
return sum;
}
解説はしませんが、上のように書いておけばよいでしょう。