# 各種CALCプログラムをまとめて収録。実行してプログラムをセレクトしてください。 # print(""); //共通に使う設定。 ########################################## # CALCプログラム その1: 式の計算 ########################################## # 与えられた式を評価し結果を表示します。「実行」ボタンを押すと開始します。 # ? に続けて式を入力して[Enter]するか 「入力」ボタンをクリックして下さい。 # 空を入力すると終了します。[↑][↓](矢印キー)で昔入力した値を思い出します。 # 数値には整数、実数、複素数が使えます。複素数の虚数単位は"i"または"j"です。 # 演算には "+", "-", "*", "/", "%"(剰余), "**"(累乗), "()" などが使えます。 # 変数も使えます。直前の結果が変数 rr(result register) に記憶されています。 # 常数として円周率 p, PI、自然対数の底 e、論理値 true, false などが使えます。 # 数学関数として sin(), cos(), tan(), exp(), log(), sqrt() などが使えます。 # その他詳しい説明などは、左上の「ご案内」ボタンをクリックしてご覧ください。 # 「実行」ボタンをクリックするとCALCインタプリタが以下のコードを実行します。 # function COMPLEX_CALC(){ //// CALC_logo(); // CALCロゴの表示。ロゴの表示にアニメーションも入れてみた。:トップで表示。 var rr; // 結果を返す変数(result register)。 var siki = ""; // 評価する式。 var errorprint = true; // エラーがあったらエラー内容を表示するか否か。calc()に引数として与えられる。 println("入力された式を計算して結果を表示します。複素数も使えます。空入力で終了します。"); // 冒頭の挨拶。 while(1){ // 繰り返しの指定。 if((siki = input("?")) === ""){ exit "終了いたします。"; } // 式の入力。空なら終了、終わりのことば。 rr = calc(siki, errorprint); // 式の計算。評価できなければエラー表示し常数errorを返す。 if(siki != "error" && rr === error){ // "error"を入力すると当然errorが返るのでそれは除いて、 println("...ごめんあそばせ。修行が足りず評価できませんでした。"); // エラー通知もおくゆかしく。 } else{ println("= ", rr); } // 結果を表示。 } } function CALC_logo(){ // CALCロゴの描画。 screenstyle(""); // スクリーンスタイルは特になし。 newscreen(360, 80); // ロゴスクリーンを作成。 screenmouseall("'CALCタイトルロゴ'"); screenmousedown("'CALC !'"); var col = random() < 0.25 ? "red" : (random() < 0.75 ? "green" : "blue"); // 色。 color(col, col, 0.2, 0.6); // 文字内部と縁取りの描画色と透明度を設定。 pen(2); // 文字の縁取りの太さを設定。 font("normal", "normal", "sans-serif"); // フォントを指定。 var tid = text(0, -10, "CALC !", 0, "left"); // テキスト初期値。サイズ0なので実際はまだ見えない。 for(var size = 0; size <= 80; size += 1){ // アニメーションで文字の位置と大きさ変更。 wait(10); // アニメーションスピードを調整。 tid.text(size/2, size-10, "CALC !", size, "left"); // 先に書いた文字のデータを書き換え。 } CALC_logo_glare(); // さらに輝きを入れてみた。 println(""); } function CALC_logo_glare(){ // CALCロゴを輝かす。 static hpos = 0, gline = null, timer = null; if(hpos == 0){ pen(20); color("white", "", 0.5, 0.5); if(gline === null){ gline = line(-20, 0, -40, 80); } timer = setinterval("CALC_logo_glare();", 20); } // 輝き線を書き変えて移動。 hpos += 10; gline.line(hpos-20, 0, hpos-40, 80); // 終了。設定は戻す。 if(hpos > 400){ hpos = 0; pen(1); color("black", "", 1, 1); gline.line(0,0,0,0); gline = null; clearinterval(timer); } } // End of COMPLEX_CALC(){} # # # # ################################################ # CALCプログラム 2: 多桁高精度計算 ################################################ # 多桁の数値の計算、いわゆる高精度計算をします。 # CALCの多桁計算は単に手計算をそのままシミュレートしただけなので速くはありません。 # 数値は整数、小数点、あるいは指数表記の実数で、残念ながら複素数は使えません。 # # 数字の桁数は整数部は無制限、小数部は割り算等の都合で最大桁数(精度)を制限します。 # 計算は評価や演算の都度、指定精度(小数点以下の桁数)に四捨五入され丸められます。 # ただし指定精度0は整数演算とし、小数点以下は旧来にならい常に切り捨てます。 # # 演算には四則演算"+, -, *, /"のほか剰余算"%"、累乗"**"(累乗数は0か正の整数、 # あるいは小数部はあっても1桁か2桁)、0か正の整数の階乗"!"、優先括弧"()"が使えます。 # 優先順位は"+,-"(加算,減算) -> "*,/,%"(乗算,除算,剰余算) -> "+,-"(符号) # -> "**"(累乗) -> "!"(階乗) -> "()"(優先括弧)の順に右にいくほど高くなります。 # これは数学記法に合わせたもので、符号より累乗が優先順位が高いので"-1**2"は"(-1)**2" # ではなく"-(1**2)"で"-1"です。 # # 函数として平方根 root(x)、累乗根(N乗根) root(N,x) (Nは正整数、最大10程度)が # 使えますが、この平方根、さらには累乗根の計算には少し時間がかかります。 # 実際に手計算するといかにたいへんかわかります。精度やNがあまり大きいとこけます。 # # 数字の最大桁数についてはよくわかりません。javascriptの配列インデックス番号の # 最大値(2**32-1)の4,294,967,295桁(42億桁?)は越えないはずですが、そのはるか前に # 実行タイムアウトやメモリーオーバーなどでこけることを保証いたします。 # function LONG_CALC() { println("多桁の高精度演算を行います。整数演算が基本ですが四則演算については小数も使えます。"); println("四則演算の他に、剰余算(%)、累乗(x**N)、累乗根(root(N,x))、階乗(!)、括弧()が使えます。"); println("
計算精度(小数点以下の桁数)の希望値があれば入力してください
(0以上の整数、100以下くらいが安全)。"); println("無指定ならディフォルト設定の
小数点以下", longseido(), "桁
が適用されます。0は整数演算モードです。"); println("実は浮動小数点モードも選択できます。この場合
24e
のように有効数字1以上にeまたはEをつけて指定してください。"); println("計算内容によっては有効数字の桁数内で限定されて計算される浮動小数点モードのほうがかなり速い場合があります。"); longmode("."); // 計算・表示モードを整数.小数モードに設定。"E"か"e"だと指数モード。 var seido = longseido(); seido = trim(input("計算精度は?", seido)); if(seido && isdigit(seido) && (parseInt(seido)) >= 0){ if(seido == "0"){ // 整数演算。整数演算の場合は旧来にならい切り捨てモードとする。 seido = longseido(seido); longseidocut("floor"); println("計算を整数演算モード(小数点以下切り捨て)に設定しました。"); } else{ // 実数演算。四捨五入モードとする。 if(strpos(seido, "E") > 0){ longmode("E"); } else if(strpos(seido, "e") > 0){ longmode("e"); } seido = (string)max(parseInt(seido), 1); seido = longseido(seido); longseidocut("round"); if(longmode() == "."){ println("計算を整数.小数モード、小数点以下", seido, "桁、四捨五入に設定しました。"); } else{ println("計算を浮動小数点モード、有効数字", seido, "桁、四捨五入に設定しました。"); } } } else{ if(strpos(seido, "E") >= 0){ longmode("E"); } else if(strpos(seido, "e") >= 0){ longmode("e"); } seido = longseido(); if(seido == "0"){ println("計算は整数モードとし、小数点以下は切り捨てします。"); } else if(longmode() == "."){ println("整数.小数モードで計算精度を小数点以下", seido, "桁とし、四捨五入します。"); } else{ println("計算を浮動小数点モード、有効数字", seido, "桁、四捨五入とします。"); } } println("
計算する数式を入力してください
。空で終了します。"); var siki, kekka; // 入力した式と出力する結果。 while(1){ siki = input("式:"); // 式の入力。 if(!siki){ exit "終了します。"; } kekka = longcalc(siki); // 文字列高精度計算。longcalc()はここで作ったのをCALCに組込みさらに改良した。 if(kekka !== error){ println(kekka); if(longmode() == "." && strlen(kekka) > 10 && (parseFloat(kekka) >= 10 || parseFloat(kekka) < 1)){ println("≒ ", longtoE(kekka, "4", "round")); // 桁などわかりやすいように近似値表示もしてあげる。 } } } } // END of LONG_CALC(){} # # # # ################################################ # CALCプログラム 3: 分数計算 ################################################ # 整数の分数を分数のままで四則演算および累乗の計算をします。 # ただし通分して数値が整数範囲を越えると正しい値にならないことがあります。 # だからあまり大きな数は扱えません。でも複素数は使えます。 # function BUNSUU() { // ソース(式)データ。 var Formula = ''; // 分数計算式ソース。 var FPos = 0; // 式の解析ポインタ。 // 分数データテンプレート。アクセス演算子"."でアクセスしやすいよう要素に名前をつけておく。 // 名前の意味は実数部R(Real)虚数部I(Image)の各々分子C(Child)分母M(Mother)。 const DATA = ['RC':0, 'RM':1, 'IC':0, 'IM':1]; // 要素は値0に初期化しておく。 exit bunnsuu(); function bunnsuu(){ var data = new DATA; print('分数の計算をします。少数には直さず分数のままで計算・表示します。\n'); print('数値は"整数±整数/整数"の形で必要に応じて()でくくってください。整数型の複素数も使えます。\n'); print('小数を含む数値は小数部を切り捨てて整数化してから計算しますので正しい答えになりません。\n'); print('可能な演算操作は加減算"+", "-"、乗除算"*", "/"、および整数での累乗"**"、および優先"()"です。\n'); print('式を入力してください。空で終了します。\n'); while(1){ if((Formula = input('式:')) === ''){ exit '終了します'; } FPos = 0; fskipspace(); // 解析ポインタを式の頭に。空白は読み飛ばし。 if(baddsub(data) === error){ return error; } // 演算を開始。 printresult(data); // 結果をなるべくわかりやすく表示。 } exit; } function printresult(data){ var child, mother; // 約分できれば約分する。同時に分母に負号があれば分子に移される。 if(yakubun(data) === error){ return error; } // 一旦分数のままで表示。分子の方が分母より大きな数字かもしれないが。 print('結果は '); print(data.RC, (data.RM != 1 ? '/' + data.RM : '')); // 分母が1なら分数表示はしない。 if(data.IC){ // 虚数成分はあれば表示。 if(data.IC > 0){ print(' +'); }else{ print(' -'); } if(data.IM != 1){ print('(', (data.IC < 0 ? -data.IC : data.IC), '/', data.IM, ')i'); } else{ print((data.IC < 0 ? -data.IC : data.IC), 'i'); } } // 分子の値が大きく整数部が出る場合は整数+分数表示も。 var reformed = false, realint = 0, imageint = 0; // 整数部と分数部に分解を試みる。 if(data.RM != 1 && (data.RC < 0 ? -data.RC : data.RC) > data.RM){ realint = (int)(data.RC / data.RM); data.RC %= data.RM; reformed = true; } if(data.IM != 1 && (data.IC < 0 ? -data.IC : data.IC) > data.IM){ imageint = (int)(data.IC / data.IM); data.IC %= data.IM; reformed = true; } if(reformed){ // 整数部と分数部に分解された。 print(' = '); // 実数部を表示 if(realint > 0){ if(data.RC){ print('(', realint, '+', data.RC, '/', data.RM, ')'); } else{ print(realint); } } else if(realint < 0){ if(data.RC){ print('-(', -realint, '+', -data.RC, '/', data.RM, ')'); } else{ print(realint); } } else{ print(data.RC, (data.RM != 1 ? '/' + data.RM : '')); } // 虚数部はあれば表示 if(imageint > 0){ if(data.IC){ print(' +(', imageint, '+', data.IC, '/', data.IM, ')i'); } else{ print(' +', imageint, 'i'); } } else if(imageint < 0){ if(data.IC){ print(' -(', -imageint, '+', -data.IC, '/', data.IM, ')i'); } else{ print(' ', imageint, 'i'); } } else if(data.IC > 0){ print('+(', data.IC, (data.IM != 1 ? '/' + data.IM : ''), ')i'); } else if(data.IC < 0){ print('-(', -data.IC, (data.IM != 1 ? '/' + data.IM : ''), ')i'); } } } function baddsub(data){ // "+", "-" : 加算減算。 var data2 = new DATA; if(bmuldiv(data) === error){ return error; } while(Formula[FPos] == '+' || Formula[FPos] == '-'){ if(Formula[FPos] == '+'){ FPos++; fskipspace(); if(bmuldiv(data2) === error){ return error; } data.RC = data.RC * data2.RM + data.RM * data2.RC; data.RM = data.RM * data2.RM; data.IC = data.IC * data2.IM + data.IM * data2.IC; data.IM = data.IM * data2.IM; } else { FPos++; fskipspace(); if(bmuldiv(data2) === error){ return error; } data.RC = data.RC * data2.RM - data2.RC * data.RM; data.RM = data.RM * data2.RM; data.IC = data.IC * data2.IM - data2.IC * data.IM; data.IM = data.IM * data2.IM; } if(yakubun(data) === error){ return error; } } return true; } function bmuldiv(data){ // "*", "/" : 掛け算割り算。 var data3 = new DATA; var data4 = new DATA; if(bsign(data) === error){ return error; } while((Formula[FPos] == '*' && Formula[FPos+1] != '*') || Formula[FPos] == '/' || Formula[FPos] == '('){ // ()の前は"*"の省略を許す。 if(Formula[FPos] != '/'){ // 掛け算。 if(Formula[FPos] == '*'){ FPos++; } fskipspace(); if(bsign(data3) === error){ return error; } // data3を使うのは累乗計算と合わせただけ。 data4.RC = data.RC * data3.RC * data.IM * data3.IM - data.RM * data3.RM * data.IC * data3.IC; data4.IC = data.RC * data3.IC * data.IM * data3.RM + data.RM * data3.IM * data.IC * data3.RC; data4.RM = data4.IM = data.RM * data3.RM * data.IM * data3.IM; data.RC = data4.RC; data.RM = data4.RM; data.IC = data4.IC; data.IM = data4.IM; } else{ // 割り算。 FPos++; fskipspace(); if(bsign(data3) === error){ return error; } // もちろん0で割ってはいけない。昔からの決まり。 if(data3.RC == 0 && data3.IC == 0){ return ERROR('0による割り算がおこりました。\n'); } data4.RC = data3.RM * data3.IM * (data.RC * data.IM * data3.RC * data3.IM + data.RM * data.IC * data3.RM * data3.IC); data4.IC = data3.RM * data3.IM * (data.RM * data.IC * data3.RC * data3.IM - data.RC * data.IM * data3.RM * data3.IC); data4.RM = data4.IM = data.RM * data.IM * (data3.RC * data3.RC * data3.IM * data3.IM + data3.RM * data3.RM * data3.IC * data3.IC); data.RC = data4.RC; data.RM = data4.RM; data.IC = data4.IC; data.IM = data4.IM; } if(yakubun(data) === error){ return error; } } return true; } function bsign(data){ // 符号の"+"、"-"。 if(Formula[FPos] == '+'){ FPos++; fskipspace(); if(bpower(data) === error){ return error; } // 特に演算はなし。 } else if(Formula[FPos] == '-'){ FPos++; fskipspace(); if(bpower(data) === error){ return error; } data.RC = -data.RC; // 分子の符号だけ変えればいいかな。 data.IC = -data.IC; } else if(bpower(data) === error){ return error; } return true; } function bpower(data){ // "**" : 累乗。累乗の累乗は右結合(2**2**3 = 2**(2**3))。 var data2 = new DATA; if(bbracket(data) === error){ return error; } if(Formula[FPos] == '*' && Formula[FPos+1] == '*'){ FPos += 2; fskipspace(); if(bsign(data2) === error){ return error; } // 右結合性とするため後ろを先読み処理する。 if(yakubun(data2) === error){ return error; } // 約分と負号の処理。 // 整数以外での累乗は認めない。 if(data2.RM != 1 || data2.IC){ return ERROR('整数以外での累乗は処理できません。\n'); } if(data2.RC == 0){ // 0乗するとなぜかみんな1になるんだそうだ。 data.RC = 1; data.RM = 1; data.IC = 0; data.IM = 1; } else if(data2.RC == 1){ ; } // 1乗はなにもしなくていいみたい。 else if(data2.RC >= 2){ // 2乗以上は乗数-1回掛け算する。 var count; var data3 = copy data; // 掛けていくデータ。dataをそのままセット。 var data4 = new DATA; // 演算結果データ。 for(count = 1; count < data2.RC; count++){ data4.RC = data.RC * data3.RC * data.IM * data3.IM - data.RM * data3.RM * data.IC * data3.IC; data4.IC = data.RC * data3.IC * data.IM * data3.RM + data.RM * data3.IM * data.IC * data3.RC; data4.RM = data4.IM = data.RM * data3.RM * data.IM * data3.IM; data3.RC = data4.RC; data3.RM = data4.RM; data3.IC = data4.IC; data3.IM = data4.IM; } data.RC = data4.RC; data.RM = data4.RM; data.IC = data4.IC; data.IM = data4.IM; } else if(data2.RC < 0){ // -n乗。-での累乗は実質割り算。 var count; var data3 = copy data; // 割っていくデータ。dataをそのままセット。 data.RC = 1; data.IC = 0; data.RM = data.IM = 1; // dataを1にして1を割るところから出発。 var data4 = new DATA; // 演算結果データ。 for(count = 0; count > data2.RC; count--){ data4.RC = data3.RM * data3.IM * (data.RC * data.IM * data3.RC * data3.IM + data.RM * data.IC * data3.RM * data3.IC); data4.IC = data3.RM * data3.IM * (data.RM * data.IC * data3.RC * data3.IM - data.RC * data.IM * data3.RM * data3.IC); data4.RM = data4.IM = data.RM * data.IM * (data3.RC * data3.RC * data3.IM * data3.IM + data3.RM * data3.RM * data3.IC * data3.IC); data.RC = data4.RC; data.RM = data4.RM; data.IC = data4.IC; data.IM = data4.IM; } } if(yakubun(data) === error){ return error; } } return true; } function bbracket(data){ // "()" : くくり演算子。 if(Formula[FPos] == '('){ FPos++; fskipspace(); if(baddsub(data) === error){ return error; } if(Formula[FPos] != ')'){ return ERROR('()が対応していません。もしくは表現が解釈できません。\n'); } FPos++; if(Formula[FPos] == 'i' || Formula[FPos] == 'j'){ // ()は数値と同じ扱いとし虚数単位の直付けを許す。表示もそうしている。 FPos++; var tmp; tmp = data.RC; data.RC = -data.IC; data.IC = tmp; tmp = data.RM; data.RM = data.IM; data.IM = tmp; } fskipspace(); } else if(bvalue(data) === error){ return error; } return true; } function bvalue(data){ // 数値。 var valuestr = '', chr, cpos, invalue; // 値を評価。数値(整数)のはず。ただし現在まだCALCに文字列関係の関数がないのでこんな方法で。 data.RC = data.IC = 0; data.RM = data.IM = 1; // リセット。 while(Formula[FPos]){ invalue = false; for(cpos = 0; (chr = "1234567890eE"[cpos]); cpos++){ // 数値を表現する文字。+-は含めない。 if(chr == Formula[FPos]){ valuestr += chr; if((chr == 'e' || chr == 'E') && Formula[FPos] == '+'){ valuestr += Formula[++FPos]; } // 指数表現。eのあとに"+"があれば取り込む。 FPos++; invalue = true; break; } } if(!invalue){ break; } } fskipspace(); if(valuestr){ data.RC = (int)valuestr; // 数値の文字列を数値に直してセット。ただし整数。 if(data.RC === error){ return ERROR('数値の表現がうまく解釈できません。\n'); } if(Formula[FPos] == 'i' || Formula[FPos] == 'j'){ // 虚数 FPos++; fskipspace(); data.IC = data.RC; data.RC = 0; } } else{ if(Formula[FPos] == 'i' || Formula[FPos] == 'j'){ // 虚数だったらセット。 FPos++; fskipspace(); data.IC = 1; } else { return ERROR('値が解釈できません。\n'); } } return true; } // 分数を約分する。同時に分母に負号があれば分子に移す。 function yakubun(data){ if(data.RM == 0 || data.IM == 0){ return ERROR('分母が0になっています。計算できません。'); } var fugou, sdkys; // 符号と最大公約数。 // まず実数部。 fugou = 1; if(data.RC < 0){ fugou *= -1; data.RC = -data.RC; } // 分子が負なら符号を覚えて正の数に直す。 if(data.RM < 0){ fugou *= -1; data.RM = -data.RM; } // 分母も同様。 if((sdkys = GCD(data.RC, data.RM)) === error){ return error; } // 最大公約数を求める。 if(sdkys > 1){ data.RC /= sdkys; data.RM /= sdkys; } // 1以外で両方割りきれるなら約分。 if(fugou < 0){ data.RC = -data.RC; } // 符号を戻す。 // 虚数部。 fugou = 1; if(data.IC < 0){ fugou *= -1; data.IC = -data.IC; } // 分子が負なら符号を覚えて正の数に直す。 if(data.IM < 0){ fugou *= -1; data.IM = -data.IM; } // 分母も同様。 if((sdkys = GCD(data.IC, data.IM)) === error){ return error; } // 最大公約数を求める。 if(sdkys > 1){ data.IC /= sdkys; data.IM /= sdkys; } // 1以外で両方割りきれるなら約分。 if(fugou < 0){ data.IC = -data.IC; } // 符号を戻す。 return true; } // 最大公約数を求める。 function GCD(n1, n2){ var t; while(n2 != 0){ t = n1 % n2; n1 = n2; n2 = t; } return(n1); } // 式の空白を読み飛ばす。 function fskipspace(){ var fchr; while((fchr = Formula[FPos]) == ' ' || fchr == '\n' || fchr == '\r' || fchr == '\t'){ FPos++; } } // エラー(message)とその場所を表示して常数errorを返す。 function ERROR(message){ var fpos = 0, printstr = ''; printstr += '
'; printstr += message; while(fpos < FPos){ printstr += Formula[fpos++]; } printstr += '◆'; // エラーマーク。 while(Formula[fpos]){ printstr += Formula[fpos++]; } printstr += '
\n'; print(printstr); return error; } } // End of function BUNSUU(){} # # # # ################################################ # CALCプログラム 4: 二次方程式を解く ################################################ # 二次方程式をかの有名な根の公式を使って解きます。 # 標準形式での係数を指定するほかに、式そのものを入力して解くこともできます。 # function EQUATION2() { // Program of CALC : 二次方程式の解。 var Formula = ''; // 二次方程式ソース。 var FPos = 0; // 式の評価位置。 var which; // 入力形式の選択。 print('二次方程式 F1(x) = F2(x) または a・x
2
+ b・x + c = 0を解きます。\n'); print('入力方式にどちらを使用しますか? 番号を入力してください。\n'); print('
空
または
1
:方程式そのものを入力。
2
:係数a, b, cを入力。 それ以外:終了。\n'); which = input('入力方式は ?'); if(which == '' || which == '1'){ equation2_1(); } else if(which == '2'){ equation2_2(); } exit '終了しました。'; // 方式その1:式そのものを入力する。多少語句解析的な処理が必要。 function equation2_1(){ var farray = [0, 0, 0]; // ターゲット=求める係数の配列。 print('xを変数とする二次方程式を入力してください。空を入力すると終了します。\n'); print('使える演算子はイコール"="、四則演算"+,-,*,/"、累乗"**"、かっこ"()"です。\n'); print('数学関数は使えません。方式 2: の係数入力方式では可能ですので必要ならそちらを。\n'); print('xの2乗はx2と表記してもかまいません。またxおよび()の前の"*"(乗算)記号は省略できます。\n'); print('例:
x**2=1
,
x**2-x+1=0
'); print(',
3(2x**2+1)=3x+6
'); print(',
3(x+5)**2-4(2x+1)-6=2(x+1)**2-3
'); print('\n'); // 式の入力。 while(print('\n'), (Formula = input('式:')) != ''){ FPos = 0; fskipspace(); if(getfactor(farray) !== error){ // 式の解析がうまくできて係数が求まったら、 // ... = 0 に変形した式をなるべく見やすく表示。 // 係数0なら項そのものを表示しない、係数1は表示しない、符号はだぶらせないなど。 var before = false; // 前の項がプリントされたか。されてなければ項の頭の"+"はつけない。 print('式は '); if(farray[2]){ // 2次の項。 if(farray[2] == 1){ print('x
2
'); } else if(farray[2] == -1){ print('-x
2
'); } else{ print(farray[2], 'x
2
'); } before = true; } if(farray[1]){ // 1次の項。 if((img)farray[1] || farray[1] >= 0){ print(before ? ' +' : ''); }else{ print(before ? ' ' : ''); } if(farray[1] == 1){ print('x'); } else if(farray[1] == -1){ print('-x'); } else{ print(farray[1], 'x'); } before = true; } if(farray[0]){ // 0次の項。 if((img)farray[0] || farray[0] >= 0){ print(before ? ' +' : ''); }else{ print(before ? ' ' : ''); } print(farray[0]); } print(' = 0 として根の公式を使用して解かれます。\n'); equation2_solve(farray[2], farray[1], farray[0]); // 解の計算と結果の表示。 } } } // 式Formula{F1(x)=F2(x)の形}をa*x**2 + b*x + c = 0の形にしたときの係数a,b,cを取得し引数配列farrayで返す。 // ちょっと改良すれば2次以上の F1(x)=F2(x) を F(x)=0 の形に変形するのにも使えるかな。 function getfactor(farray){ var farray2 = [0, 0, 0]; if(faddsub(farray) === error){ return error; } // 左辺を評価。 if(Formula[FPos] != '='){ print('=がありません。\n'); return error; } ++FPos; fskipspace(); if(!Formula[FPos]){ return ERROR('=の右辺がありません。\n'); } if(faddsub(farray2) === error){ return error; } // 右辺を評価。 farray[2] -= farray2[2]; // = 0の形にするため移項。 farray[1] -= farray2[1]; farray[0] -= farray2[0]; return true; } function faddsub(farray){ // "+", "-" : 加算減算。 var farray2 = [0, 0, 0]; if(fmuldiv(farray) === error){ return error; } while(Formula[FPos] == '+' || Formula[FPos] == '-'){ if(Formula[FPos] == '+'){ FPos++; fskipspace(); if(fmuldiv(farray2) === error){ return error; } farray[2] += farray2[2]; farray[1] += farray2[1]; farray[0] += farray2[0]; } else if(Formula[FPos] == '-'){ FPos++; fskipspace(); if(fmuldiv(farray2) === error){ return error; } farray[2] -= farray2[2]; farray[1] -= farray2[1]; farray[0] -= farray2[0]; } } return true; } function fmuldiv(farray){ // "*", "/" : 掛け算割り算。 var farray2 = [0, 0, 0]; if(fsign(farray) === error){ return error; } while((Formula[FPos] == '*' && Formula[FPos+1] != '*') || Formula[FPos] == '/' || Formula[FPos] == 'x' || Formula[FPos] == '('){ // xと()の直前は"*"の省略を許した。 if(Formula[FPos] != '/'){ if(Formula[FPos] == '*'){ FPos++; } fskipspace(); if(fsign(farray2) === error){ return error; } // 掛け算の場合*の左値右値両方共にxがあってxの3次や4次の項が出てはいけない。2次方程式だから。 if(farray[2] && (farray2[2] || farray2[1]) || farray2[2] && (farray[2] || farray[1])){ return ERROR('掛け算によりxの3次以上の項が生成されました。計算できません。\n'); } farray[2] = farray[2] * farray2[0] + farray[1] * farray2[1] + farray[0] * farray2[2] ; farray[1] = farray[1] * farray2[0] + farray[0] * farray2[1]; farray[0] = farray[0] * farray2[0]; } else{ FPos++; fskipspace(); if(fsign(farray2) === error){ return error; } // 割り算の場合右値にxがあってはいけない...というよりあった場合のやりかたわからない。ごめん。 if(farray2[2] || farray2[1]){ return ERROR('割り算の除数にxが含まれています。計算できません。\n'); } // もちろん0で割ってはいけない。あやうく忘れるとこだった。 if(farray2[0] == 0){ return ERROR('0による割り算がおこりました。\n'); } farray[2] /= farray2[0]; farray[1] /= farray2[0]; farray[0] /= farray2[0]; } } return true; } function fsign(farray){ // 符号の"+"、"-"。 if(Formula[FPos] == '+'){ FPos++; fskipspace(); if(fpower(farray) === error){ return error; } } else if(Formula[FPos] == '-'){ FPos++; fskipspace(); if(fpower(farray) === error){ return error; } farray[2] = -farray[2]; farray[1] = -farray[1]; farray[0] = -farray[0]; } else if(fpower(farray) === error){ return error; } return true; } function fpower(farray){ // "**" : 累乗。累乗の累乗は右結合(2**2**3 = 2**(2**3))。 var farray2 = [0, 0, 0]; if(fbracket(farray) === error){ return error; } if(Formula[FPos] == '*' && Formula[FPos+1] == '*'){ FPos += 2; fskipspace(); if(fsign(farray2) === error){ return error; } // 右結合性とするため後ろを先読み処理する。 // 右値にxの項が入ってはいけない。 if(farray2[2] || farray2[1]){ return ERROR('累乗部分の処理ができません。展開して記載してください。\n'); } // 左側にxを含んだら累乗の値は0か1か2のみ。 if((farray[2] || farray[1]) && !(farray2[0] == 0 || farray2[0] == 1 || farray2[0] ==2)){ return ERROR('累乗の値は0か1か2でなくてはなりません。\n'); } if(farray2[0] == 0){ // 0乗するとなぜかみんな1になるんだそうだ。 farray[2] = 0; farray[1] = 0; farray[0] = 1; } else if(farray2[0] == 1){ ; } // 1乗はなにもしなくていいみたい。 else if(farray2[0] == 2){ // 2乗の場合。累乗演算は誤差が増えるので2乗までは掛け算でやる。 farray[2] = farray[1] * farray[1]; farray[1] = farray[1] * farray[0] * 2; farray[0] = farray[0] * farray[0]; } else{ farray[2] = 0; farray[1] = 0; farray[0] = farray[0] ** farray2[0]; // 数値に限り実行する。 } } return true; } function fbracket(farray){ // "()" : くくり演算子。 if(Formula[FPos] == '('){ FPos++; fskipspace(); if(faddsub(farray) === error){ return error; } if(Formula[FPos] != ')'){ return ERROR('()が対応していません。もしくは表現が解釈できません。\n'); } FPos++; if(Formula[FPos] == 'i' || Formula[FPos] == 'j'){ // ()は数値と同じ扱いとし虚数単位の直付けを許す。 FPos++; farray[2] *= i; farray[1] *= i; farray[0] *= i; } fskipspace(); } else if(fvalue(farray) === error){ return error; } return true; } function fvalue(farray){ // 項または数値。 var valuestr = '', chr, cpos, invalue; if(Formula[FPos] == 'x'){ // xの1乗2乗の項。 FPos++; fskipspace(); if(Formula[FPos] == '2'){ // xの2乗の項。 FPos++; fskipspace(); farray[2] = 1; farray[1] = 0; farray[0] = 0; } else if(Formula[FPos] == '1'){ // xの1乗の項。丁寧に1を書く人がいるかもしれない。 FPos++; fskipspace(); farray[2] = 0; farray[1] = 1; farray[0] = 0; } else{ // 次数指定がなければxの1乗の項。1,2以外の数字を書くとここで処理しなければ次のステップでエラーになる。 farray[2] = 0; farray[1] = 1; farray[0] = 0; } } else{ // 値を評価。数値のはず。ただし現在まだCALCに文字列関係の関数がないのでこんな方法で。 while(Formula[FPos]){ invalue = false; for(cpos = 0; (chr = "123456789.0eEij"[cpos]); cpos++){ // 数値を表現する文字。i,jは虚数単位。+-は含めない。 if(chr == Formula[FPos]){ valuestr += chr; if(chr == 'e' || chr == 'E'){ valuestr += Formula[++FPos]; } // 指数表現。無条件に1文字取込む。eのあとの"+","-"も取込まれる。 FPos++; invalue = true; break; } } if(!invalue){ break; } } fskipspace(); if(valuestr){ farray[2] = 0; farray[1] = 0; farray[0] = (complex)valuestr; // 数値を表す文字列を数値(複素数、もちろん実数も可)に変換。 if(farray[0] === error){ return ERROR('数値の表現がうまく解釈できません。\n'); }; } else{ farray[2] = 0; farray[1] = 0; farray[0] = 0; return ERROR('値が解釈できません。\n'); } } return true; } // 方式その2:係数a,b,cを入力する。 function equation2_2(){ var a, b, c; print('xの二次、一次、定数項の係数を各々ひとつづつ入力してください。値は複素数あるいは数式でもかまいません。\n'); print('単に [Enter] のみで終了します。'); for(;;){ do{ print("\n"); a = input("a : "); if(a === ""){ return; } a = calc(a, 1); }while(a === error); do{ b = input("b : "); if(b === ""){ return; } b = calc(b, 1); }while(b === error); do{ c = input("c : "); if(c === ""){ return; } c = calc(c, 1); }while(c === error); equation2_solve(a, b, c); // 解の計算と結果の表示。 } } // 解の計算と結果の表示。 function equation2_solve(a, b, c){ var x1, x2, t; if(a == 0.0){ if(b == 0.0){ print('残念ですが解けません。\n'); return; } print('一次方程式として解きます。\n'); print('x =
', -c/b, '
\n'); } else{ t = sqrt(b*b - 4*a*c); if(t == 0.0){ x1 = x2 = -b / (2*a); print('式は重根を持ちます。 x
1
= x
2
=
', x1, '
\n'); } else{ x1 = (-b + t) / (2*a); x2 = (-b - t) / (2*a); print('x =
', (-b / (2*a)), '±sqrt(', ((b*b - 4*a*c)/(4*a*a)), ')', ' = ', (-b / (2*a)), '±',(sqrt((b*b - 4*a*c)/(4*a*a))), '
\n'); print('x
1
=
', x1, '
\nx
2
=
', x2, '
\n'); } } } // 式の空白を読み飛ばす。 function fskipspace(){ var fchr; while((fchr = Formula[FPos]) == ' ' || fchr == '\n' || fchr == '\r' || fchr == '\t'){ FPos++; } } // エラー(message)とその場所を表示して常数errorを返す。 function ERROR(message){ var fpos = 0, printstr = ''; printstr += '
'; printstr += message; while(fpos < FPos){ printstr += Formula[fpos++]; } printstr += '◆'; // エラーマーク。 while(Formula[fpos]){ printstr += Formula[fpos++]; } printstr += '
\n'; print(printstr); return error; } } // End of function EQUATION2(){} # # # # ################################################ # CALCプログラム 5: 連立方程式を解く ################################################ # 連立一次方程式 # a11・x1 + a21・x2 + ... + an1・xn = b1 # .... # a1n・x1 + a2n・x2 + ... + ann・xn = bn # の解 x1, x2, . . , xn を求めます。 # どこぞのCプログラムを少し書き直しただけなので何でこれで解けるのかは訊かないでください。 # 計算誤差の関係で本当は整数の答えが整数でなくなる場合がたまにあります。 function RENHOU() { // 元の数と係数。ここに書いて入力をパスしてもよい。以下は例も兼ねて。必要に応じて書き直してね。 var n = 3; // 元の数。 var ab = [ // 係数: a1, a2, ..., b, のくりかえし。値は実数でも複素数でも式でもかまわない。 1, 1, 1, 3+6, 2, 3, -2, 5, 3, -1, 1, 7, ]; keisuu_input(); // 元の数と係数を入力する。 if(!renhou(n, ab)){ // 連立方程式を解く。 println("解が得られませんでした。"); } else{ println("答えが出ました ! おめでとうございます。"); for(var r = 0; r < n; ++r){ println("x"+(r+1)+" = "+ab[(n+1)*r+n]); } } exit "...というわけで終了します。"; function keisuu_input(){ var inputstr, valstr, val; print("
n
元連立方程式 {
a1
・x1 +
a2
・x2 + ... +
an
・xn =
b
}
n
の"); print("元の数nと各式の係数a1 ~ an, bを入力します。\n"); // 元の数入力。 while(1){ //// inputstr = input("単に[Enter]でプログラムに記載の式を実行。元の数nは ? "); n = 0; //// プログラム記載式の実行はなし。 inputstr = input("元の数nは ? "); if(inputstr == ""){ return; } val = calc(inputstr, 1); if(val === error){ print("値が正しく解釈できません。\n"); continue; } if(val != (int)val || val < 1){ print("元の数は正の整数でなくてはなりません。\n"); continue; } n = val; // 入力OK。 break; } // 係数入力。 print("各式ごとの係数a1, a2, ..., bを入力してください。\n"); print("係数の値は実数でも複素数でも式でもかまいません。空なら0とみなされます。\n"); var nk, ns; var kinfo = [], sinfo = [], intable = []; for(nk = 1; nk <= n; nk++){ kinfo[nk] = "a" + nk; } kinfo[nk] = "b"; for(ns = 0; ns < n; ns++){ sinfo[ns] = "式" + (ns+1); } table_input(n+1, n, intable, kinfo, sinfo); for(ns = 0; ns < n; ns++){ for(nk = 0; nk < n+1; nk++){ valstr = intable[ns][nk]; if(!valstr){ val = 0; } else{ val = calc(valstr, 1); if(val === error){ exit "入力が解釈できませんでした。: " + valstr; } val = (complex)val; if(val === error){ exit "数値が解釈できませんでした。: " + valstr; } } ab[(n + 1) * ns + nk] = val; } } } function renhou(n, ab){ if(n < 1 || n != (int)n){ println("元の数は1以上の整数でなくてはなりません。"); return false; } var t, r, c, y, k, val; for(t = 0; t < n; ++t){ /* 対角線要素t行t列に注目 */ /* もしt行t列の値よりt列の大きな行が後ろにあったらtをyにセット */ y = t; val = abs(ab[(n+1)*t+t]); for(r = t+1; r < n; r++){ if(abs( ab[(n+1)*r+t]) > val) { y = r; val = abs(ab[(n+1)*r+t]); } } /* yがセットされていたらt行とy行を入れ替え、精度向上のため */ if(y > t){ y = (n+1)*y; k = (n+1)*t; for(c = 0; c <= n; ++c){ val = ab[k]; ab[k++] = ab[y]; ab[y++] = val; } } /* もしt行t列が0だったら、解はない */ if(ab[(n+1)*t+t] == 0){ return false; /* 解けない */ } /* t行t列を1にする */ val = 1.0 / ab[(n+1)*t+t]; y = (n+1)*t; for(c= 0; c <= n; ++c){ ab[y++] *= val; } /* t行以外のt列を0にする */ for(r = 0; r < n; ++r){ if(r != t) { y = (n+1)*t+t; val = ab[k = (n+1)*r+t]; for(c = t; c <= n; ++c){ ab[k++] -= val * ab[y++]; } } } } return true; /* 解けた */ } // 入力テーブルを作成し入力を配列として得る。得られる配列の要素は文字列。 // clms:項目数、rows:行数、input_aryに答え、ctitle_ary:項目タイトル、rtitle_ary:行タイトル。 // input_aryに値が設定されていれば初期値として入力欄に挿入される。 function table_input(clms, rows, input_ary, ctitle_ary=null, rtitle_ary=null){ var tmn = time(), inn = 0; // 繰り返し動作時の識別用番号。 var printstr, rn, cn, preval; printstr = '
'; for(rn = 0; rn <= rows; rn++){ if(rn == 0 && isarray(ctitle_ary) < 1){ continue; } printstr += '
'; for(cn = 0; cn <= clms; cn++){ if(cn == 0 && isarray(rtitle_ary) < 1){ continue; } if(rn == 0){ printstr += '
'; if(isarray(ctitle_ary) >= 1 && ctitle_ary[cn]){ printstr += ctitle_ary[cn]; } else{ printstr += ' '; } printstr += '
'; } else if(cn == 0){ printstr += '
'; if(isarray(rtitle_ary) >= 1 && rtitle_ary[rn-1]){ printstr += rtitle_ary[rn-1]; } else{ printstr += ' '; } printstr += '
'; } else{ printstr += '
'; preval = ''; if(isarray(input_ary[rn-1]) >= 1 && (input_ary[rn-1][cn-1] || input_ary[rn-1][cn-1] === 0)){ preval = input_ary[rn-1][cn-1]; } printstr += '
'; printstr += '
'; } } printstr += '
'; } printstr += '
'; print(printstr); jseval('document.getElementById("IN'+tmn+'_1").focus();'); jseval(' function setNextFocus(){ var i, elm; for(i=1; i < '+(inn)+'; i++){ elm = document.getElementById("IN'+tmn+'_"+i); if(elm === document.activeElement){ document.getElementById("IN'+tmn+'_"+(i+1)).focus(); return; } } elm = document.getElementById("SUBMIT'+tmn+'"); if(elm !== document.activeElemant){ cmd = \'document.getElementById("SUBMIT'+tmn+'").focus();\'; setTimeout(cmd, 10); ////1; この行を無効にすると[Enter]で入力完了にしない(うっかりEnter防止)。12行下にお断り。いちおう1回の待ちを入れたが。 } } function inputkeyDown(event){ var keyCode = event.keyCode; if(keyCode == 13){ setNextFocus(); } } document.onkeydown = function(event){ inputkeyDown(event); }; '); print(' =>
'); ////1; print('
(うっかりEnter防止のため[Enter]では動作しませんのでクリックしてください。)
'); print('\n'); secretinput(''); // 結果(入力値)を取得。 var value; inn = 0; for(rn = 1; rn <= rows; rn++){ if(isarray(input_ary[rn-1]) < 1){ input_ary[rn-1] = new []; } for(cn = 1; cn <= clms; cn++){ value = jseval('document.getElementById("IN'+tmn+'_'+(++inn)+'").value;'); input_ary[rn-1][cn-1] = value; jseval('document.getElementById("TD'+tmn+'_'+rn+'_'+cn+'").style.width = document.getElementById("TD'+tmn+'_'+rn+'_'+cn+'").getBoundingClientRect().width+"px";'); jseval('document.getElementById("TD'+tmn+'_'+rn+'_'+cn+'").innerHTML = "'+(value ? value : ' ')+'";'); } } jseval('document.getElementById("INTABL'+tmn+'").style.background = "#F0F0F0";'); jseval('document.getElementById("SUBMIT'+tmn+'").disabled = true;'); jseval('document.getElementById("SUBMIT'+tmn+'").style.background = "#F0F0F0";'); jseval('document.getElementById("SUBMIT'+tmn+'").style.cursor = "default";'); } } // End of function RENHOU(){} # # # # ################################################ # CALCプログラム 6: 階乗の計算 ################################################ # 指定の範囲の数(0または正の整数)の階乗(nの階乗: 0! = 1, n! = (n-1)! * n を求めます。 # とりあえず開始値n1に0、終了値n2に1000がセットしてあります。ご希望に合わせて変更してください。 # ただしあまり大きな数を指定してメモリ不足やタイムアウトでこけても知りません。 # ディフォルトでは延々1000の階乗まで計算しますが途中で飽きたら適当に「停止」ボタンで止めてください。 # 10!以上では ≒ で指数表示もします。(数の大きさがわかりやすいように。私親切!。) # function FACTORIAL() { println("階乗を計算し表示します。"); // 開始値、終了値の指定。 var n1 = 0; // 表示開始の値。 var n2 = 1000; // 表示終了の値。 var n1n2 = "1, 1000"; n1n2 = trim(input("計算の開始値(0以上), 終了値(1000程度まで)は?", n1n2)); var nary = calc("["+n1n2+"]"); if(nary[0] >= 0 && nary[0] <= 10000){ n1 = (int)nary[0]; } if(nary[1] >= nary[0] && nary[1] <= 10000){ n2 = (int)nary[1]; } // 長整数計算関数longcalc()を使う。そのための計算モード等の設定。 longmode("."); // 整数.小数モードで計算。実際は整数だけだけど。 longseido("0"); // 精度。小数点以下の評価と計算はなし。整数だから。 longseidocut("floor"); // seido以下切捨て。整数演算方式。 //clear(); // 画面をクリア。 kaijyou(); // 階乗の計算と表示。 exit "完了。"; function kaijyou(){ // 階乗の定義に従って値を計算して表示。 var n, nk; // n! = nk を表す変数。 if(n1 > n2){ n = n1; n1 = n2; n2 = n; } // 小さい方の数をn1、大きい方をn2に。 if(n1 < 0){ println("
負の数の階乗は今は求まりません。
"); return; } n = 0; nk = 1; // 開始値 0! = 1。これは定義。 if(n1 == 0){ // "0! = 1" は定義だから n1 が 0 ならそのまま表示。計算では求まらない。 println("0! = 1"); println(); } // 計算を 1 から開始。表示開始指定の n1 までは表示せず計算するのみ。 for(n = 1; n < n1; n++){ nk = longcalc(nk + "*" + n); } // n1 から n2 までは計算して表示。 for(; n <= n2; n++){ nk = longcalc(nk + "*" + n); println(n, "! = ", nk); if(n >= 10){ println(n, "! ≒ ", longtoE(nk, "4", "round")); } // 大きい数は有効数字4桁四捨五入で指数表示もする。 println(); } } } // End of function FACTORIAL(){} # # # # ################################################ # CALCプログラム 7: 素因数分解 ################################################ # 正の整数 n を素因数に分解して表示します。 # n は最大2147483647(取り扱える正の整数の上限) までの正の整数でなくてはなりません。 function FACTORIZE() { // Program of CALC : 素因数分解。 // 挨拶と案内。 println("正の整数 n を素因数に分解して表示します。"); println("n は2147483647までの正の整数でなくてはなりません。"); println("0か単に[Enter]で終了です。"); while(1){ // 値の入力。 while(1){ var testnumber = input("素因数分解したい数 n は?"); if(testnumber == "0" || testnumber == ""){ println('お約束どおり終了します。'); exit "完了"; } testnumber = calc(testnumber); if(testnumber === error || testnumber != (int)testnumber || testnumber < 0) { println('有効な数値ではありません。計算可能な数値は1以上の整数です。'); continue; } else{ break; } } // 計算開始と結果の表示。 var number = testnumber; // 入力した値は素数表示のため使わずにとっておく。 print(number + " = "); // 先に2で割れるだけ割って残りの因数成分を奇数のみとしておく。 while(number > 2){ if(number % 2 == 0) { number /= 2; print("2 * "); } else{ break; } } // あとは奇数でわりきれるかどうかだけ見ていけばいい。 var divisor = 3; // スタートは3から。何で3なのかは考えてみてくれ。 while( divisor * divisor <= number){ if(number % divisor == 0){ number /= divisor; print( divisor + " * "); } else{ divisor += 2; } } print(number); if(number && number != 1 && number == testnumber){ println(" (素数です)"); }else{ println(''); } } } // End of function FACTORIZE(){} # # # # ################################################ # CALCプログラム 8: 素数の表示 ################################################ # 2から2147483647(取り扱える正の整数の上限)までの素数を表示します。1は特別な数で素数とはしません。 # ただし数と範囲が大きくなるにしたがって相当時間と表示領域をくいます。範囲は10000がいいとこ。 # 素数を求めてどうするのかと問われても別に答えはありません。そこに素数があるからです。 function SOSUU() { // 冒頭のあいさつ。 print("素数計算にようこそ。\n最小2から最大2147483647までの素数を表示します。\n"); print("数が大きかったり範囲が広いととても時間がかかりますのでよくばってはいけません。\n"); print("求める素数の最大は1000000、範囲は10000以内くらいにしておいたほうがいいと思います。\n"); // 変数初期化。 var minimum = 0; // 求める素数の最小値。 var maximum = 0; // 求める素数の最大値。 var testnumber = 0; // テストする数。 var divisor = 0; // 割りきれるか否かのテストで割っていく数。 var sosuucount = 0; // 求まった素数の数。 var issosuu = false; // 素数か否かの判定結果。 // 条件入力。求める素数の最小、最大。 print("最小いくつから最大いくつまでの素数を求めますか?\n"); do{ minimum = input("最小は(2〜2147483647)? "); minimum = (int)minimum; // 入力は文字列なので整数に変換。 if(minimum === error){ println("値が数値として解釈できません。"); } }while(!minimum || !(minimum > 1 && minimum <= 0x7FFFFFFF)); do{ maximum = input("最大は("+minimum+"〜2147483647)? "); maximum = (int)maximum; // 入力は文字列なので整数に変換。 if(maximum === error){ println("値が数値として解釈できません。"); } } while(!maximum || !(maximum >= minimum && maximum <= 0x7FFFFFFF)); print("\n"); var starttime = time(); // 処理開始。 sosuucount = 0; testnumber = minimum; // 計算処理ループに余計な判定や処理を組み込むと遅くなるので事前にできることは事前に処理してしまう。 // 2と3は素数なのでそのまま表示。 while(testnumber <= maximum && testnumber <= 3) { print(testnumber+", "); ++testnumber, ++sosuucount; } // 4以上の偶数は素数でないので+1して次の奇数から。 if(testnumber % 2 == 0) testnumber += 1; // あとは奇数のみを奇数でわりきれるか否か見ていけばよい。 while(1){ // 計算処理ループ。 divisor = 3; // 試し割りに使う数。順次2を足していってこれで割りきれたら素数ではないとする。何で3なのかは考えてくれ。 issosuu = true; // 判定結果。まず素数であるとしておいてdivisorで割りきれたらfalseを設定。最後まで割りきれなかったら素数。 while(divisor * divisor <= testnumber) { // この範囲でtestnumberがdivisorで割りきれるか否かを判定する必要がある。 if(testnumber % divisor == 0){ // 割りきれた。素数ではない。 issosuu = false; break; } else{ // 割りきれなかったらさらに次の割り算判定を試行。ifでbreakしているのでelseは必ずしも必要ではないが。 divisor += 2; } } // testnumberの判定は終了。 if(testnumber <= maximum){ // testnumberが求める最大値以下で素数ならそれを表示しさらに次の数をやる。 if(issosuu){ print(testnumber+", "); ++sosuucount; } testnumber += 2; } else{ // そうでなければ終わり。 break; } } // 処理完了。しめのことばを表示して終了。 if(sosuucount){ print("\n\n以上、"+sosuucount+" 個です。"); // println("処理時間 "+((time()-starttime)/1000)+" 秒。"); }else{ print("残念ながら見つかりませんでした。\n"); } exit "完了!"; } // End of function SOSUU(){} # # # # ################################################ # CALCプログラム 9: 最大公約数 ################################################ # Program of CALC : 最大公約数を求める。 # 技術評論社 コンピュータ・アルゴリズム事典 参考。 function SAIKOU() { print("2つの正の整数 n1, n2 の最大公約数を求めます。\n"); print("n1, n2 を,で区切って入力してください。小数は切り捨てられます。\n"); print("空[Enter]またはへんな値を入力すると終了です。\n"); saikou(); exit "完了"; function saikou(){ var instr, valstr, spos, n1, n2; for (;;) { instr = input("n1, n2 : "); if(instr == ""){ return; } spos = 0; valstr = ""; // n1の取得。 while(instr[spos] && instr[spos] != ","){ valstr += instr[spos++]; } if((n1 = (int)valstr) === error || n1 <= 0) return; ++spos; valstr = ""; // n2の取得。 while(instr[spos] && instr[spos] != ","){ valstr += instr[spos++]; } if((n2 = (int)valstr) === error || n2 <= 0) return; print(n1+" と "+n2+" の最大公約数は
", GCD(n1, n2), "
です。\n\n"); } } function GCD(n1, n2){ var t; while(n2 != 0){ t = n1 % n2; n1 = n2; n2 = t; } return(n1); } } // End of function SAIKOU(){} # # # # ################################################ # CALCプログラム 10: 級数の和 ################################################ # 級数 F(n) の総和を求めます。 # n は整数で、指定の n = n1 から n = n2 までの F(n) の式の値を合計したものを求めます。 // Program of CALC : 級数の総和を求める。 function SOUWA() { var n, Formula, n1, n2; // ここに級数Formula(F(n))と開始点n1を書いて、F(n)とn1の入力をパスしてもよい。 // 参考によく知られた数の級数。 // 円周率3.14159265358979...。収束はとても遅い。n2=100000でもまだまだで誤差る。 //Formula = "4 * (-1)**n / (2 * n + 1)"; n1 = 0; // 自然対数の底2.71828182845905...。収束はとても速い。n2=18で充分。n2は170以下でないとこける。 Formula = "1 / factorial(n)"; n1 = 0; exit souwa(); function souwa(){ var instr, pos, total; while(1){ // 式の入力とチェック。 print("\nn = n1 から n2(n1, n2は整数)までの F(n) の総和 ΣF(n) を求めます。\n"); print("
n
を変数としてF(n) を記述してください。空で終了します。\n例:"); print("
n
... 等差数列。n1=1, n2=1000くらい。\n"); print("
1/factorial(n)
... 自然対数の底eの式。n1=0, n2=10〜20くらい。\n"); instr = input("F(n) = "); if(instr != ""){ pos = 0; while(instr[pos] == " "){ pos++; } // 空白をから送り; if(instr[pos]){ Formula = ""; while(instr[pos]){ Formula += instr[pos++]; } } } else{ exit "終了します。"; } // 開始、終了点の入力とチェック。 if(instr != ""){ pos = 0; n1 = ""; n2 = ""; print("開始, 終了の整数 n1, n2 (n1 <= n2) を入力してください。\n"); instr = input("n1, n2 ? "); while(instr[pos] == " "){ pos++; } while(instr[pos] && instr[pos] != ","){ n1 += instr[pos++]; } if((n1 = calc(n1, 1)) === error){ exit "こけました。"; } while(instr[pos] == ","){ pos++; } if(!instr[pos]){ instr = input("n2 ? "); pos = 0; } } else{ pos = 0; n2 = ""; print("終了の整数 n2 (", n1, " <= n2) を入力してください。\n"); instr = input("n2 ? "); } while(instr[pos]){ n2 += instr[pos++]; } if((n2 = calc(n2, 1)) === error){ exit "こけました。"; } if((n1 = (int)n1) === error || (n2 = (int)n2) === error || n1 > n2){ print("n1, n2 の値が正しくありません。\n", "n1=", n1, ", n2=", n2); exit "こけました。"; } // 計算開始。 if(n2 - n1 >= 100000){ print("計算には相当時間がかかります。\n"); } else if(n2 - n1 >= 10000){ print("計算には少々時間がかかります。\n"); } total = sigma(n1, n2, Formula); // 結果の出力。 print("級数 F(n) = ", Formula, " の\n"); print("n = ", n1, " から ", n2, " までの総和は
", total, "
です。\n"); } } function sigma(n1, n2, formula){ var total; total = 0; for(n = n1; n <= n2; ++n){ total += calc(formula); } return(total); } } // End of function SOUWA(){} # # # # ################################################ # CALCプログラム 11: 数値積分 ################################################ # 式 f(x)を x=a から x=b まで数値積分を行います。 # 複素数で計算するため本来結果が実数でも計算誤差による小さな虚数成分が出てくることがありますが無視してください。 // Program of CALC : 簡易数値積分。 function SEKIBUN() { var x; // 積分変数。積分する関数f(x)のx。変数名は入力する式と合致している必要があります。 var devides = 1024; // 分割数。2の乗数にするとほんの少し誤差を減らせる可能性あり(内部は2進なので)。 exit main(); function main(){ var formula; // 計算式f(x)。 var a,b,s; print("式 f(x) を x = a から b まで数値積分を行います\n"); print(" x を変数として、積分する式 f(x) を入力してください。例: sin(x) + cos(x)\n"); if((formula = input("f(x)=")) == ""){ print("式が空なので終了します。"); return; } while(1){ a = input("x の開始値 a は ? "); if((a = calc(a, 1)) === error){ continue; } break; } while(1){ b = input("x の終了値 b は ? "); if((b = calc(b, 1)) === error){ continue; } break; } s = Sekibun(formula, a, b, devides); print("結果は", s, "です。\n"); } /*簡易数値積分*/ function Sekibun(formula, a, b, n){ var x, s, savex, dx; if(n < 1) n=1; dx = (b - a) / n; x = a; s = calc(formula) / 2.0 * dx; while (--n > 0){ x += dx; s += calc(formula) * dx; } x = b; s += calc(formula) / 2.0 * dx; return(s); } } // End of function SEKIBUN(){} # # # # ################################################ # CALCプログラム 12: 面積計算 ################################################ # 簡単な図形の面積と、ついでにできれば周囲の長さも求めます。 # 取り扱う図形は円、楕円、三角形、四角形(正方形、長方形)、 # 平行四辺形、台形、辺が交差しない多角形 です。 # 図形や入力方法の選択をして図形のパラメータ(長さや角度など)を # 入力すれば結果を表示します。 # 図形のパラメータの入力には変数や数学演算式、数学関数が使えます。 # 変数はa=1.23, b=3.14 などとして値の記憶ができ次回入力に使えます。 # ただし、i,j,p,e,sin,ifなどのいわゆる予約語は変数には使えません。 function AREA() { const deg = PI/180; // 度からラジアンへの角度変換係数。度なら空白なしで40degなどと入力。 var perr = true; // ユーザ入力の評価で細かいエラー表示をするか否か。 var graphicID = []; // 図形グラフィックのID。 var X0; // クリック入力有効無効切替え変数。 exit area_and_length(); function area_and_length(){ var ans; // clear(); sampleGraph(); while(1){ print("\n"); print(""); print("各種図形の面積を計算します。ついでに周囲の長さが求まれば求めます。\n"); print("求める図形の番号を指定してください。該当番号以外か空で終了します。\n"); print("あるいは
希望の図形
か以下の
該当項目
"); print("をクリックしていただいてもかまいません。\n"); print("
1:円
、"); print("
2:楕円
、"); print("
3:三角形
、"); print("
4:正方形,長方形
、"); print("
5:平行四辺形
、"); print("
6:台形
、"); print("
7:(辺が交差しない)多角形
、"); print("
8:その他の形
、"); print("
0:終了
。\n"); enablezukeiselect(); // グラフィック図形からの選択を有効化。 jseval("X0=true"); // 該当項目からの選択を有効化。 ans = calc(input("?"), perr); // 選択番号入力。 jseval("X0=false;"); // 該当項目からの選択を無効化。 disablezukeiselect(); // グラフィック図形からの選択を無効化。 print(""); if(ans == 1){ ofCircle(); } else if(ans == 2){ ofOval(); } else if(ans == 3){ ofTriangle(); } else if(ans == 4){ ofSqure(); } else if(ans == 5){ ofParallerogram(); } else if(ans == 6){ ofTrapezoid(); } else if(ans == 7){ ofPolygon(); } else if(ans == 8){ ofOther(); } else{ return "おわり。"; } } } function ofCircle(){ var ans, val, r, theArea, theLength; while(1){ print("円の面積と周囲長を求めます。空入力で終了します。\n"); print("直径、半径のどちらを指定しますか? 番号を入力するか
項目
をクリックしてください。\n"); print(""); print("
1:直径
、"); print("
2:半径
、"); print("
0:終了(図形選択に戻る)
。"); jseval("X1=true;"); ans = calc(input("?"), perr); jseval("X1=false"); print(""); if(!(ans == 1 || ans == 2)){ return; } if(ans == 1){ print("円の直径を入力してください。\n"); } else{ print("円の半径を入力してください。\n"); } while(1){ if(ans == 1){ if((val = input("直径:")) === ""){ return; } } // 空入力で終了。 else{ if((val = input("半径:")) === ""){ return; } } if((val = calc("["+val+"]", perr)) === error){ // 入力は文字列。評価も兼ねて配列で値を得る。 printKekka("入力が解釈できません!", "", ""); continue; } if(val[0] === undefined){ printKekka("入力が不完全です!", "", ""); continue; } r = (float)val[0]; if(r < 0){ printKekka("負の値はいけません!", "", ""); continue; } break; } if(ans == 1){ theArea = PI * r * r / 4; theLength = PI * r; } else{ theArea = PI * r * r; theLength = 2 * PI * r; } printKekka("...求める面積は ", theArea, " です。"); printKekka("...周囲の長さは ", theLength, " です。\n"); } } function ofOval(){ var ans, val, r1, r2, theArea, theLength; while(1){ print("楕円の面積と周囲長を求めます。空入力で終了します。\n"); print("楕円の長軸,短軸の長さをどちらで指定しますか? 番号を入力するか
項目
をクリックしてください。\n"); print(""); print("
1:直径
、"); print("
2:半径
、"); print("
0:終了(図形選択に戻る)
。"); jseval("X2=true;"); ans = calc(input("?"), perr); jseval("X2=false;"); print(""); if(!(ans == 1 || ans == 2)) return; if(ans == 1){ print("長軸, 短軸の直径を カンマ(,)で区切って入力してください。\n"); if((val = input("長径の直径, 短径の直径:")) === ""){ return; } // 空入力で終了。 } else{ print("長軸, 短軸の半径を カンマ(,)で区切って入力してください。\n"); if((val = input("長軸の半径, 短軸の半径:")) === ""){ return; } // 空入力で終了。 } if((val = calc("["+val+"]", perr)) === error){ // 入力は文字列。評価も兼ねて配列で値を得る。 printKekka("入力が解釈できません!", "", ""); continue; } if(val[0] === undefined || val[1] === undefined){ printKekka("入力が不完全です!", "", ""); continue; } r1 = (float)val[0]; r2 = (float)val[1]; if(r1 < 0 || r2 < 0){ printKekka("負の値はいけません!", "", ""); continue; } // まずは面積。こちらは簡単。 if(ans == 1){ theArea = PI * r1 * r2 / 4; } else{ theArea = PI * r1 * r2; } // 周囲の長さはなぜか難しい。第2種楕円積分とやらを数値積分でやる。 // 参考: http://keisan.casio.jp/exec/user/1495672323 print("数値積分をしますので計算に時間がかかります。しばらくお待ちください。\n"); var timeout = setinterval('print("忙しい! ");', 500); var rc = r2 / r1; // 縦横比率。 var n, nmax = 10000, dn = 1/nmax; // 数値積分の分割パラメータ。 var dl, tl = 0.0; for(n=1; n <= nmax; n++){ // 長径の直径を1としたときの周長を求め周長比率とする。 dl = sqrt((dn**2)+(rc**2 * ((sqrt(1-(dn*(n-1))**2) - sqrt(1-(dn*n)**2))**2))); tl += dl; } tl *= 2; // 直径を使った場合。半径ならば4。 clearinterval(timeout); print("\n"); ////println("周長比率=", tl); // 長径短径の直径が共に1のときは円周率πになるはずだが少しずれる。 if(ans == 1){ theLength = tl * r1; } else{ theLength = tl * r1 * 2; } printKekka("...求める面積は ", theArea, " です。"); printKekka("...周囲の長さはおよそ ", theLength, " です。\n"); } } function ofTriangle(){ var ans, val, a, b, c, h, theArea, theLength; while(1){ print("三角形の面積と、できれば周囲長を求めます。"); print("どの方法で求めますか? 番号を入力するか
項目
をクリックしてください。\n"); print(""); print("
1:底辺と高さ(面積のみ)
、"); print("
2:二辺とそのはさむ角
、"); print("
3:三辺の長さ
、"); print("
0:終了(図形選択に戻る)
。"); jseval("X3=true;"); ans = calc(input("?"), perr); jseval("X3=false;"); print(""); if(!(ans == 1 || ans == 2 || ans == 3)) return; switch(ans){ case 1 : print("底辺, 高さ を カンマ(,)で区切って入力してください。空で終了します。\n"); while(1){ if((val = input("底辺, 高さ:")) === ""){ return; } // 空入力で終了。 if((val = calc("["+val+"]", perr)) === error){ // 入力は文字列。評価も兼ねて配列で値を得る。 printKekka("入力が解釈できません!", "", ""); continue; } if(val[0] === undefined || val[1] === undefined){ printKekka("入力が不完全です!", "", ""); continue; } a = (float)val[0]; h = (float)val[1]; if(a < 0 || h < 0){ printKekka("負の値はいけません!", "", ""); continue; } theArea = a * h / 2; printKekka("...求める面積は ", theArea, " です。"); printKekka("...周囲の長さは求まりません。", "", "\n"); break; } break; case 2 : print("辺1, 辺2 の長さ、間の角度 をこの順にカンマ(,)で区切って入力して下さい。空で終了します。\n"); print("角度の単位は基本ラジアン(0~π)ですが、
度で入力するなら0deg~180degで表記してください
。\n"); while(1){ if((val = input("辺1, 辺2, 角度:")) === ""){ return; } // 空入力で終了。 if((val = calc("["+val+"]", perr)) === error){ // 入力は文字列。評価も兼ねて配列で値を得る。 printKekka("入力が解釈できません!", "", ""); continue; } if(val[0] === undefined || val[1] === undefined || val[2] === undefined){ printKekka("入力が不完全です!", "", ""); continue; } a = (float)val[0]; b = (float)val[1]; c = (float)val[2]; if(a < 0 || b < 0 || c < 0){ printKekka("負の値はいけません!", "", ""); continue; } if(c < 0 || c > PI){ printKekka("計算範囲外の角度です!", "", ""); return; } theArea = a * b * sin(c) / 2; theLength = a + b + sqrt(a*a + b*b - 2*a*b*cos(c)); // 余弦定理というのだそうだ。 printKekka("...求める面積は ", theArea, " です。"); printKekka("...周囲の長さは ", theLength, " です。\n"); break; } break; case 3 : print("辺1, 辺2, 辺3 の長さをカンマ(,)で区切って入力して下さい。空で終了します。\n"); while(1){ if((val = input("辺1, 辺2, 辺3:")) === ""){ return; } // 空入力で終了。 if((val = calc("["+val+"]", perr)) === error){ // 入力は文字列。評価も兼ねて配列で値を得る。 printKekka("入力が解釈できません!", "", ""); continue; } if(val[0] === undefined || val[1] === undefined || val[2] === undefined){ printKekka("入力が不完全です!", "", ""); continue; } a = (float)val[0]; b = (float)val[1]; c = (float)val[2]; if(a < 0 || b < 0 || c < 0){ printKekka("負の値はいけません!", "", ""); continue; } if(c < abs(a - b) || c > (a + b)){ printKekka("三角形になりません!", "", ""); continue; } // ヘロンの公式とやらで計算をする。 var s = (a + b + c) / 2; theArea = sqrt(s * (s - a) * (s - b) * (s - c)); theLength = a + b + c; printKekka("...求める面積は ", theArea, " です。"); printKekka("...周囲の長さは ", theLength, " です。\n"); break; } break; default : return; } } } function ofSqure(){ var val, h, w, theArea, theLength; while(1){ print("正方形あるいは長方形の面積、及び周囲の長さを求めます。\n"); print("縦, 横の長さをカンマ(,)で区切って入力して下さい。空で終了します。\n"); if((val = input("縦, 横:")) === ""){ break; } // 空入力で終了。 if((val = calc("["+val+"]", perr)) === error){ // 入力は文字列。評価も兼ねて配列で値を得る。 printKekka("入力が解釈できません!", "", ""); continue; } if(val[0] === undefined || val[1] === undefined){ printKekka("入力が不完全です!", "", ""); continue; } h = (float)val[0]; w = (float)val[1]; if(h < 0 || w < 0){ printKekka("負の値はいけません!", "", ""); continue; } theArea = h * w; theLength = (h + w) * 2; printKekka("...求める面積は ", theArea, " です。"); printKekka("...周囲の長さは ", theLength, " です。\n"); } } function ofParallerogram(){ var ans, val, a, b, c, h, theArea, theLength; while(1){ print("平行四辺形の面積、およびできれば周囲長を求めます。"); print("どの方法で求めますか? 番号を入力するか
項目
をクリックしてください。\n"); print(""); print("
1:底辺と高さ(面積のみ)
、"); print("
2:二辺とそのはさむ角
、"); print("
0:終了(図形選択に戻る)
。\n"); jseval("X5=true;"); ans = calc(input("?"), perr); jseval("X5=false;"); print(""); if(!(ans == 1 || ans == 2)) return; switch(ans){ case 1 : print("底辺, 高さ をカンマ(,)で区切って入力して下さい。空で終了します。\n"); while(1){ if((val = input("底辺, 高さ:")) === ""){ return; } // 空入力で終了。 if((val = calc("["+val+"]", perr)) === error){ // 入力は文字列。評価も兼ねて配列で値を得る。 printKekka("入力が解釈できません!", "", ""); continue; } if(val[0] === undefined || val[1] === undefined){ printKekka("入力が不完全です!", "", ""); continue; } a = (float)val[0]; h = (float)val[1]; if(a < 0 || h < 0){ printKekka("負の値はいけません!", "", ""); continue; } theArea = a * h; printKekka("...求める面積は ", theArea, " です。"); printKekka("...周囲の長さは求まりません。", "", "\n"); break; } break; case 2 : print("二辺の長さ と その間の角度 をこの順にカンマ(,)で区切って入力して下さい。\n"); print("角度の単位は基本ラジアン(0~π)ですが、
度で入力するなら0deg~180degで表記してください
。\n"); while(1){ if((val = input("辺1, 辺2, 角度:")) === ""){ return; } // 空入力で終了。 if((val = calc("["+val+"]", perr)) === error){ // 入力は文字列。評価も兼ねて配列で値を得る。 printKekka("入力が解釈できません!", "", ""); continue; } if(val[0] === undefined || val[1] === undefined || val[2] === undefined){ printKekka("入力が不完全です!", "", ""); continue; } a = (float)val[0]; b = (float)val[1]; c = (float)val[2]; if(a < 0 || b < 0 || c < 0){ printKekka("負の値はいけません!", "", ""); continue; } if(c < 0 || c > PI){ printKekka("計算範囲外の角度です!", "", ""); continue; } theArea = a * b * sin(c); theLength = (a + b) * 2; printKekka("...求める面積は ", theArea, " です。"); printKekka("...周囲の長さは ", theLength, " です。\n"); break; } break; default : return; } } } function ofTrapezoid(){ var val, a, b, h, theArea; while(1){ print("台形の面積を計算します。\n"); print("平行な2辺の長さ と その間の距離(高さ)をこの順にカンマ(,)で区切って入力して下さい。\n"); if((val = input("上底, 下底, 高さ:")) === ""){ break; } // 空入力で終了。 if((val = calc("["+val+"]", perr)) === error){ // 入力は文字列。評価も兼ねて配列で値を得る。 printKekka("入力が解釈できません!", "", ""); continue; } if(val[0] === undefined || val[1] === undefined || val[2] === undefined){ printKekka("入力が不完全です!", "", ""); continue; } a = (float)val[0]; b = (float)val[1]; h = (float)val[2]; if(a < 0 || b < 0 || h < 0){ printKekka("負の値はいけません!", "", ""); continue; } theArea = (a + b) * h / 2; printKekka("...求める面積は ", theArea, " です。"); printKekka("...周囲の長さは求まりません。", "", "\n"); } } function ofPolygon(){ var n, val, x0, y0, x1, y1, x2, y2, theArea, theLength; while(1){ print("多角形の面積、および周囲の長さを求めます。"); print("ただし辺が交差してはいけません。チェックはしません。\n"); print("ある頂点から順に頂点の座標 x, y を1セットずつカンマ(,)で区切って入力して下さい。\n"); print("全部入力したら
最後空入力
して下さい。\n"); n = 1; while(1){ if((val = input("N0."+n+"座標 x,y:")) === ""){ return; } // 最初の空入力は終り。 if((val = calc("["+val+"]", perr)) === error){ // 入力は文字列。評価も兼ねて配列で値を得る。 printKekka("入力が解釈できません!", "", ""); continue; } if(val[0] === undefined || val[1] === undefined){ printKekka("入力が不完全です!", "", ""); continue; } x0 = (float)val[0]; y0 = (float)val[1]; break; } ++n; x1 = x0; y1 = y0; theArea = 0.0; theLength = 0.0; while(1){ if((val = input("N0."+n+"座標 x,y:")) === ""){ break; } // 空入力で入力完了。 if((val = calc("["+val+"]", perr)) === error){ // 入力は文字列。評価も兼ねて配列で値を得る。 printKekka("入力が解釈できません!", "", ""); continue; } if(val[0] === undefined || val[1] === undefined){ printKekka("入力が不完全です!", "", ""); continue; } x2 = (float)val[0]; y2 = (float)val[1]; theArea += (x1 - x2) * (y1 + y2) / 2; theLength += sqrt((x1 - x2)**2 + (y1 - y2)**2); x1 = x2; y1 = y2; x2 = y2 = ""; ++n; } x2 = x0; y2 = y0; theArea += (x1 - x2) * (y1 + y2) / 2; theLength += sqrt((x1 - x2)**2 + (y1 - y2)**2); theArea = abs(theArea); printKekka("...求める面積は ", theArea, " です。"); printKekka("...周囲の長さは ", theLength, " です。\n"); } } function ofOther(){ print("
今あなたが考えたその図形については宿題とします。がんばって考えてみて下さい。
\n"); } function info0text(info){ text(60, 72, info, "12px", "center"); } function info1text(info){ text(60, 86, info, "12px", "center"); } function info2text(info){ text(60, 100, info, "12px", "center"); } function titletext(title){ text(60, 116, title, "14px", "center"); } function sampleGraph(){ screenstyle("cursor:pointer;"); graphicID[1] = newscreen(120,120); pen(2); // 新しいスクリーンを作り変更する設定を指定 color("blue", "#FEE"); circle(60,36, 30); color("blue", ""); info1text("直径か半径を指定。"); titletext("1.円"); screenmouseall('return "\nえん。まるともいう。";'); screenmouseclick('if(X0){putinput("1");"選択";}else{"";}'); // クリックで番号インプット。 graphicID[2] = newscreen(); pen(); // 引数を省略すると前と同じ指定が使われる。以下同様。 color("blue", "#FEE"); ellipse(60,36, 40,25); color("blue", ""); info1text("長径および短径の "); info2text("直径か半径を指定。"); titletext("2.楕円"); screenmouseall('return "\nだえん。つぶれまるともいう。";'); screenmouseclick('if(X0){putinput("2");"選択";}else{"";}'); graphicID[3] = newscreen(); pen(); color("blue", "#FEE"); path(); moveto(20,55); lineto(60,10); lineto(100,40); closepath(); color("blue", ""); info0text("底辺と高さ、または "); info1text("二辺と挟む角、または"); info2text("三辺の長さを指定。 "); titletext("3.三角形"); screenmouseall('return "\nさんかく。";'); screenmouseclick('if(X0){putinput("3");"選択";}else{"";}'); graphicID[4] = newscreen(); pen(); color("blue", "#FEE"); rect(20,10, 80,50); color("blue", ""); info1text("縦横の長さを指定。"); titletext("4.正方形,長方形"); screenmouseall('return "\nしかく。";'); screenmouseclick('if(X0){putinput("4");"選択";}else{"";}'); graphicID[5] = newscreen(); pen(); color("blue", "#FEE"); path(); moveto(20,60); lineto(80,60); lineto(100,10); lineto(40,10); closepath(); color("blue", ""); info1text("底辺と高さ、または "); info2text("二辺と挟む角を指定。"); titletext("5.平行四辺形"); screenmouseall('return "\nへいこうしへんけい。";'); screenmouseclick('if(X0){putinput("5");"選択";}else{"";}'); graphicID[6] = newscreen(); pen(); color("blue", "#FEE"); path(); moveto(20,60); lineto(100,60); lineto(80,10); lineto(40,10); closepath(); color("blue", ""); info1text("平行な二辺の長さと "); info2text("その間の距離を指定。"); titletext("6.台形"); screenmouseall('return "\nだいけい。";'); screenmouseclick('if(X0){putinput("6");"選択";}else{"";}'); graphicID[7] = newscreen(); pen(); color("blue", "#FEE"); path(); moveto(20,10); lineto(40,30); lineto(70,20); lineto(100,50); lineto(70,40); lineto(30,60);closepath(); color("blue", ""); info1text("頂点の座標を指定、"); info2text("辺が交差しない事。"); titletext("7.多角形"); screenmouseall('return "\nたかっけい。";'); screenmouseclick('if(X0){putinput("7");"選択";}else{"";}'); graphicID[8] = newscreen(); pen(); color("#FEE", "blue"); text(60,60, "?", "70px", "center"); color("blue", ""); info1text("どんな図形でも"); info2text("かまいません。"); titletext("8.その他の形"); screenmouseall('return "\nそのたのかたち。";'); screenmouseclick('if(X0){putinput("8");"選択";}else{"";}'); } function enablezukeiselect(){ // クリックで番号インプット有効化。 for(var gid = 1; gid <= 8; gid++){ graphicID[gid].screenstyle('cursor:pointer;'); } X0 = true; } function disablezukeiselect(){ // クリックでの番号インプット無効化。 for(var gid = 1; gid <= 8; gid++){ graphicID[gid].screenstyle('cursor:default;'); } X0 = false; } function printKekka(header, value, footer){ if(header){ print('
', header, '
'); } if(value || value === 0){ // valueが数値以外("",null,void,undefinedなど)なら値は表示しない。 value = parseFloat(value); // 数字文字列なら数値(実数)に。複素数は実数部のみ。 var yuukouketa = 7; // 表示する有効桁数。一部数値積分を使うのでこんなもん。 var vallog10ceil = ceil(log10(!value ? 1 : (value < 0 ? -value : value))); value = round(value * 10**(yuukouketa - vallog10ceil)) / 10**(yuukouketa - vallog10ceil); print('
', value, '
'); } if(footer){ print('
', footer, '
'); } print('\n'); } } // End of function AREA(){} # # # # ################################################ # CALCプログラム 13: 単位換算 ################################################ # 長さ、面積、体積、重さ、その他の単位の換算をします。 # SI単位系(国際単位系)のほかにヤードポンド法や尺貫法などの古くからの慣用的な単位 # も含みますが、その値は国、団体、用途、時代、習慣等により違う場合が多々あります。 # 温度とお金の換算はしません。 // Program of CALC : Unit Convert ver.2 : 単位の換算。 // 各種単位を自動検知して動作するようにした。 function UNITCONVERT() { // 単位換算テーブル。["単位名", 換算係数, "説明"]。 const len = [ // 長さの換算テーブル。 ["m", 1, "メートル。
SI単位系の長さの基本単位です。"], ["cm", 0.01, "センチメートル。
1/100mです。
cgs単位系の長さの基本単位にもなっています。"], ["mm", 1e-3, "ミリメートル。
1/1000mです。"], ["μm", 1e-6, "マイクロメートル。
1e-6m、1/1000mmです。"], ["nm", 1e-9, "ナノメートル。
1e-9mです。"], ["μ", 1e-6, "ミクロン。
マイクロメートルの別称で1e-6mです。"], ["Å", 1e-10, "オングストローム。
1e-10mです。"], ["km", 1000, "キロメートル。
1000mです。"], ["yd", 0.9144, "ヤード。
0.9144mです。"], ["ft", 0.3048, "フィート。
1/3yd、30.48cmです。"], ["in", 0.0254, "インチ。
1/36 yd, 1/12 ft で、25.4mmです。inchと書く場合もあります。"], ["mil", 2.54e-5, "ミル。
1/1000inです。"], ["mile", 1609.344, "マイル。
約1.6kmです。"], ["間", 1.81818, "けん。
日本の尺貫法の単位で6尺、約1.8mです。"], ["尺", 0.303030, "しゃく。
日本の尺貫法の長さの基本単位で約30cmです。"], ["寸", 0.0303030, "すん。
1/10尺、約3cmです。"], ["ぶ", 0.00303030, "ぶ。漢字で分ですが時間の単位と同じなのでひらがなで。
1/10寸、約3mmです。"], ["町", 109.09, "ちょう。
60間、約109mです。"], ["里", 3927.27, "り。
36町、約3.9kmです。"], ["海里", 1852, "かいり。
1852m、国際海里の定義を採用しています。"], ["光年", 9460730472580800, "こうねん。
光が1年間に進む距離で約9兆4千6百億kmです。"], ]; const area = [ // 面積の換算テーブル。 ["m2", 1, "平方メートル。
1m四方の面積です。"], ["cm2", 1e-4, "平方センチメートル。
1cm四方の面積です。"], ["mm2", 1e-6, "平方ミリメートル。
1mm四方の面積です。"], ["km2", 1e+6, "平方キロメートル。
1km四方の面積で百万平方メートルです。"], ["inch2", 6.4516e-4, "平方インチ。
1インチ四方の面積です。"], ["ft2", 0.09290304, "平方フィート。
1フィート四方の面積です。"], ["yd2", 0.83612736, "平方ヤード。
1ヤード四方の面積です。"], ["ac", 4046.856, "エーカー。
約4千平方メートル(約64メートル四方)の面積です。"], ["a", 100, "アール。
100平方メートルです。"], ["ha", 1e+4, "ヘクタール。
100アール=10000平方メートルです。"], ["坪", 3.3057851, "つぼ。
日本で古くから使われている土地の面積。約3.3平方メートルです。"], ["畝", 99.173553, "せ。
30坪、約99平方メートルです。"], ["反", 991.73553, "たん。
10畝、約992平方メートルです。"], ["町歩", 9917.3553, "ちょうぶ。
10反、約9920平方メートルです。単に町という場合もあります。"], ]; const volume = [ // 体積の換算テーブル。 ["m3", 1, "立方メートル。"], ["cm3", 1e-6, "立方センチメートル。"], ["mm3", 1e-9, "立方ミリメートル。"], ["L", 1e-3, "リットル。"], ["dL", 1e-4, "デシリットル。
1/10リットルです。"], ["mL", 1e-6, "ミリリットル。
1/1000リットル、1シーシーです。"], ["cc", 1e-6, "シーシー。
1/1000リットル、1ミリリットルです。"], ["USbarrel", 0.158987, "米バレル。
ヤード・ポンド法の体積の単位です。
バレルは元来樽の意味で国や用途によって値が変わり、
この値は主に石油の取引で使われるものです。"], ["USgal", 0.003785412, "米ガロン(米国液量ガロン)。
ヤード・ポンド法の体積の単位です。
ガロンは国や用途によって各種の定義があります。"], ["UKgal", 0.00454609, "英ガロン。
ヤード・ポンド法の体積の単位です。
ガロンは国や用途によって各種の定義があります。"], ["日本gal", 0.003785412, "日本ガロン。
ヤード・ポンド法の体積の単位で米ガロンと同じです。
ごく一部の業界でのみ使用されます。"], ["合", 0.0001803907, "ごう。
日本古来の体積の単位で約180ミリリットルです。"], ["升", 0.001803907, "しょう。
日本古来の体積の単位で約1.8リットルです。"], ["斗", 0.01803907, "と。
日本古来の体積の単位で約18リットルです。"], ["石", 0.1803907, "こく。
日本古来の体積の単位で約180リットルです。"], ]; const weight = [ // 重さの換算テーブル。 ["kg", 1, "キログラム。
SI単位系の質量の基本単位で1000gです。"], ["g", 0.001, "グラム。
1/1000kgです。cgs単位系の質量の基本単位にもなっています。"], ["mg", 1e-6, "ミリグラム。
1/1000gです。"], ["μg", 1e-9, "マイクログラム。
1e-6グラムです。"], ["t", 1000, "トン(メートル法トン)。
1000kgです。tonあるいはtonneとも表記されます。"], ["USton", 907.18474, "米トン。ショートトン。
ヤードポンド法の米国での重量単位で約907kgです。"], ["UKton", 1016.04691, "英トン。
ヤードポンド法の英国での重量単位で約1016kgです。"], ["lb", 0.45359237, "ポンド。
ヤードポンド法の重量の単位で約454gです。"], ["oz", 0.028349523125, "オンス。
ヤードポンド法の重量の単位で1/16ポンド、約28gです。
国や用途により値が異なる場合があります。"], ["car", 0.0002, "カラット。
ダイヤモンドなどの宝石の重量を表す単位で0.2gです。"], ["匁", 3.75e-3, "もんめ。
日本の尺貫法の重さの単位で3.75gです。"], ["貫", 3.75, "かん。
日本の尺貫法の重さの単位で3.75kgです。"], ["斤", 0.6, "きん。
日本の尺貫法の重さの単位で通常160匁600g。
時代と地域により異なる場合があります。
また、パンの量目の単位とはちがいます。"], // きん ]; const density = [ // 密度の換算テーブル。 ["kg/m3", 1, "キログラム・パー・立方メートル。
SI単位系での密度の単位です。水の密度はほぼ1000kg/m3です。"], ["g/cm3", 1e+3, "グラム・パー・立方センチメートル。
cgs単位系での密度の単位ですが、いわゆる比重の値に
ほぼ等しく、実生活ではこちらの方がよく使われます。"], ["kg/L", 1e+3, "キログラム・パー・リットル。
g/cm3と同値です。"], ["t/m3", 1e+3, "トン・パー・立方メートル
g/cm3と同値です。"], ]; const times = [ // 時間の換算テーブル。 ["s", 1, "セコンド。
SI単位系およびcgs単位系の時間の基本単位で
日本語で言うと1秒です。"], ["ms", 0.001, "ミリセコンド。
1/1000sです。"], ["μs", 1e-6, "マイクロセコンド。
1e-6sです。"], ["min", 60, "ミニッツ。
60sで日本語で言う1分です。"], ["hr", 3600, "アワー。
3600s, 60minで日本語で言う1時間です。"], ["day", 86400, "デイ。
24hrで日本語で言う1日です。"], ["秒", 1, "びょう。
日本語でsのことですが、生活での時間に
関しては日本語の方がよく使われます。"], ["分", 60, "ふん。
60秒です。"], ["時間", 3600, "じかん。
60分、1/24日です。"], ["日", 86400, "にち。
天動説により太陽が地球を1周する平均の周期です。"], ["月", 31557600/12, "つき。
参考値です。月によって日数が変わるため平均値を表しています。
時間の単位に使うのはあまり適当でないかもしれません。"], ["年", 31557600, "ねん。
地動説により地球が太陽のまわりを1公転する周期です。"], ]; const speed = [ // 速度の換算テーブル。 ["m/s", 1, "メートル・パー・セコンド。
1秒間に1メートル進む速度でほぼ人の歩く速さです。"], ["km/h", 1000/3600, "キロメートル・パー・アワー。
通常日本では毎時何キロメートルという表現で使われます。"], ["kt", 0.5144033, "ノット。
主に船舶や航空機の速度を表す慣用的な単位で
1時間に1海里進む速さ、時速約1.85kmです。"], ["mile/h", 1609.344/3600, "マイル・パー・アワー。
1時間に1マイル進む速度で、約1.6km/hです。"], ]; const flow = [ // 流量(体積流量)の換算テーブル。 ["m3/s", 1, "立方メートル・パー・セコンド。
1秒間に1m3の体積が通過する流量です。"], ["m3/min", 1/60, "立方メートル・パー・ミニッツ。
1分間に1m3の体積が通過する流量です。"], ["L/s", 0.001, "リットル・パー・セコンド。
1秒間に1Lの体積が通過する流量です。"], ["ft3/s", 0.02832, "立方フィート・パー・セコンド。
1秒間に1ft3の体積が通過する流量です。"], ]; const force = [ // 力の換算テーブル。 ["N", 1, "ニュートン。
SI単位系の力の単位で1キログラムの物質に
1m/s2 の加速度を生じさせる力です。"], ["dyn", 1e-5, "ダイン。
cgs単位系の力の単位で1グラムの物質に
1cm/s2 の加速度を生じさせる力です。"], ["kgf", 9.80665, "重量キログラム、キログラム重。
1kgの物質に地球の重力加速度が作用したときの力です。"], ["lbf", 4.44822, "重量ポンド、ポンド重
1lb(ポンド)の物質に地球の重力加速度が作用したときの力です。"], ]; const pressure = [ // 圧力の換算テーブル。 ["Pa", 1, "パスカル。
SI単位系の圧力の単位で1m2に1Nの力が作用する圧力です。"], ["hPa", 1e+2, "ヘクトパスカル。
100Paです。気圧を表すのによく用いられ1気圧は1013hPaです。"], ["MPa", 1e+6, "メガパスカル。
1e6Paで1気圧は約0.1MPaになります。"], ["bar", 1e+5, "バール。
昔つかわれていた圧力の単位で1e+5Paです。"], ["mbar", 1e+2, "ミリバール
昔気圧を表すのによく使われていた圧力の単位でhPaと同値です。"], ["kgf/cm2", 98066.5, "キログラム重・パー・平方センチメートル。
1平方センチメートルに1キログラム重の力が作用する圧力です。"], ["Torr", 133.322, "トル、トール。
水銀柱で圧力を表します。mmHgと同値です。"], ["mmHg", 133.322, "水銀柱ミリメートル、ミリメートル・エイチジー。
水銀柱で圧力(気圧、血圧など)を表し1atmが760mmHgです。"], ["mmH2O", 9.80665, "水柱ミリメートル、ミリメートル・エイチツーオー。
水柱で圧力を表します。"], ["atm", 101325, "気圧
英語読みでアトモスフェアですが気圧という場合が多いです。標準の大気圧です。"], ["psi", 6894.757, "ピーエスアイ、重量ポンド毎平方インチ
lbf/in2のことでヤードポンド法の圧力の単位です。"], // lbf/in2 ]; const energy = [ // エネルギー、仕事量の換算テーブル。 ["J", 1, "ジュール。
SI単位系のエネルギー(仕事量)の単位です。"], ["erg", 1e-7, "エルグ。
cgs単位系のエネルギー(仕事量)の単位です。"], ["Wh", 3600, "ワット・アワー、ワット時
1Wの仕事率で1時間仕事をしたときのエネルギー量です。
電気エネルギーによく用いられます。"], ["kWh", 3.6e+6, "キロワット・アワー、キロワット時
1kWの仕事率で1時間仕事をしたときのエネルギー量です。
電気エネルギーによく用いられます。"], ["kcal", 4184, "キロカロリー。
熱量の単位でほぼ水1kgの温度を1°C上げる熱量ですが
細かくはいくつかの定義や値があります。
栄養学や燃焼工学でよく使用されます。"], ["kgf*m", 9.80665, "キログラム重・メートル
地上で1kgの重量の物を1m持ち上げるのに必要なエネルギー量です。"], ["eV", 1.60218e-19, "エレクトロン・ボルト、電子ボルト。
電子1個を1ボルトの電圧で加速したときに電子が得るエネルギー量です。"], ["BTU", 1055.056, "英熱量
British thermal unitの略で1ポンドの水の温度を
華氏度で1度上げるために必要な熱量と定義されます。
空調システムなどで用いられます。"], ]; const power = [ // 仕事率の換算テーブル。 ["W", 1, "ワット。
SI単位系の仕事率の単位で1秒間に1ジュールの
仕事量(エネルギー)を発生または消費する仕事率です。"], ["kcal/h", 1.162792, "キロカロリー・パー・アワー
1時間に1kcalの熱量を発生あるいは消費する仕事率です。"], ["kgf*m/s", 9.80665, "キログラム重・メートル・パー・セコンド
地上で毎秒1kgの重量の物を1m持ち上げることができる仕事率です。"], ["HP", 745.7, "馬力(英)
その昔お馬さんが継続的に荷馬車を引っ張る際の
平均的な仕事率を基準にしたもので約750ワットです。
イギリス、アメリカで多く使われます。"], ["PS", 735.5, "馬力(仏)
フランスでの馬力の単位で約740Wです。
日本ではこちらが使われることが多いようです。"], ]; const viscosity = [ // 粘度の換算テーブル。 ["Pa*s", 1, "パスカル秒
SI単位系での粘度の単位です。粘度の説明は難しいので省略されます。"], ["P", 0.1, "ポアズ
cgs単位系での粘度の単位です。粘度の説明は難しいので例によって省略されます。"], ["cP", 0.001, "センチポアズ
1/100ポアズです。"], ]; const jiba = [ // 磁場の換算テーブル。 ["A/m", 1, "アンペア・パー・メートル
SI単位系における地場の強さの単位です。"], ["oe", 1000/(4PI), "エルステッド
cgs単位系での地場の単位です。"], ]; const jisoku = [ // 磁束の換算テーブル。 ["Wb", 1, "ウエーバー
SI単位系の磁束の単位です。"], ["Mx", 1e-8, "マクスウェル
cgs単位系の磁束の単位です。"], ]; const jisokumitudo = [ // 磁束密度の換算テーブル。 ["T", 1, "テスラ
SI単位系での磁束密度の単位です。Wb/m2と同じです。"], ["G", 1e-4, "ガウス
cgs単位系での磁束密度の単位です。Mx/cm2と同じです。"], ["kG", 0.1, "キロガウス
1000Gです。"], ]; // 上記のテーブルをさらにマスターテーブルに登録。単位のジャンルと単位表示のため。 const master = [ ["長さ", len], ["面積", area], ["体積", volume], ["質量", weight], ["密度", density], ["時間", times], ["速度", speed], ["体積流量", flow], ["力 ", force], ["圧力", pressure], ["エネルギー・仕事・熱量", energy], ["動力・仕事率", power], ["粘度", viscosity], ["磁場", jiba], ["磁束", jisoku], ["磁束密度", jisokumitudo], ]; // 登録されている単位のリストを表示する。 print(''); function unitlist(){ foreach(master as val){ print('
' + val[0]+' :
'); // 単位の種類。 foreach(val[1] as val2){ // クリックインプットと案内を付加して単位を表示。 print('
' + val2[0] + '
, '); } print('\n'); } print('\n'); } unitconvert(); // 単位換算を実行します。 exit; // 完了。 function unitconvert(){ var inputstr, value, unitstr, unittable, num; print("各種単位の換算をします。換算できる単位は以下のとおりで、"); println("?を入力するか入力欄頭の
単位
をクリックすると再表示します。"); unitlist(); print("数値と単位を入力して下さい(例: 1.2km)。数値は半角文字で、数値に式を使うなら()で囲って。\n"); print("あるいは数値入力後、上記単位表の
単位
をクリックすると単位を入力して換算します。\n"); print("単位のみなら換算係数を表示します。↑↓で以前の入力履歴を呼び出します。空で終了します。\n"); while(1){ // 換算する数値と単位を入力、チェックする。 inputstr = input("数値 と
単位
:"); if((inputstr = trim(inputstr)) == ""){ print("終了します。"); break; } if(inputstr == "?"){ unitlist(); continue; } // 単位リストを表示する。 // 入力文字列を数値部と単位部に分ける。CALCにはまだ正規表現関数がないのでjavascriptのをそのまま使う。 var x = jseval('"'+inputstr+'".match(/^([\+\-]?[\d\.]+(?:[eE][\+\-]?\d+)?)\s*([^\d].*)$/)'); if(!x){ // 普通に数値と単位が取得できないとき。数値の部分に(式)が使われているのかもしれない。 x = jseval('"'+inputstr+'".match(/^(\(.*\))\s*([^\d\)].*)$/)'); } if(x && isarray(x)){ value = x[1]; unitstr = x[2]; value = calc(value); // 数値に直す。 if(value === error){ println("入力された数値が解釈できませんでした。"); continue; } // 再入力。 } else if(jseval('"'+inputstr+'".match(/^([^\+\-\d\.\(].*)$/)')){ // 数値はなく単位のみ。換算係数表示。 value = "1"; unitstr = inputstr; } else{ println("入力が解釈できませんでした。"); continue; } // 再入力。 // 単位に該当するunittableを探す。 unittable = null; foreach(master as val){ foreach(val[1] as val2){ if(val2[0] == unitstr){ unittable = val[1]; break; } } if(unittable){ break; } } if(!unittable){ println("指定の単位が見つからないか入力が解釈できませんでした。"); continue; } // valueに数値、unitstrに単位、unittableに単位換算テーブルが取得できた。 println(value+' '+unitstr); // value を基本単位に換算する。 for(num = 0; unittable[num] && unittable[num][0]; num++){ if(unitstr == unittable[num][0]){ value *= unittable[num][1]; break; } } // 他の単位に換算表示する。 for(num = 0; unittable[num] && unittable[num][0]; num++){ if(unitstr == unittable[num][0]){ continue; } print(" = ", value / unittable[num][1], " ", unittable[num][0], "\n"); } println(); } } } // End of function UNITCONVERT(){} # # # # ################################################ # CALCプログラム 14: カレンダーの作成 ################################################ # Program of CALC : Make Calender. # 指定された年のカレンダーを作成します。 # ただし祝日などは考慮されません。 function CALENDER() { exit calendar(); function calendar(){ var year, month; // clear(); println("指定された年の一年のカレンダーをつくります。"); println("遠い昔やはるかな未来でもちゃんと合っているかどうかは自信がありません。"); print("年を西暦で入力して下さい。"); year = (int)input("西暦 何年 ? "); if(year === error){ exit "年の値が解釈できませんでした。終了します。"; } for(month = 1; month <= 12; month++){ drawmonth(year, month); } return "西暦" + year + "年のカレンダーでした。"; } static printstyle = false; // 1回だけ実行するための実行記憶。(全部で12回呼ばれる。) function drawmonth(year, month){ var tuki_e = ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; var tuki_j = ['', '睦月', '如月', '弥生', '卯月', '皐月', '水無月', '文月', '葉月', '長月', '神無月', '霜月', '師走']; var youbi = ['日', '月', '火', '水', '木', '金', '土']; // カレンダーをテーブルタグを使って書き出します。タグを使うときの注意事項をいくつか。 // タグは終了タグまで一回のprint()で書き出します。 // そうしないとブラウザが勝手に終了タグを追加しておかしな表示になることがあります。 // スタイル指定をするときはタグ名での指定はせずidかclassかあるいはインラインスタイル指定を使ってください。 // そうしないとCALCの表示に影響を与える場合があります。 if(!printstyle){ //
は数が多いのでまとめてスタイル指定しておきます。数字(日付)を右寄せにします。 print(''); printstyle = true; } var printstr; // タグを使う場合終了タグまで一式すべて変数printstrにためてタグを完結させてから書き出します。 // カレンダーの枠とタイトル(何年何月)を書き出します。 printstr = '
'; printstr += '
' + year + '年 ' + month + '月 ' + tuki_j[month] + ' (' + tuki_e[month] + ')
'; // 曜日タイトル(日、月、火、...)を書き出します。 printstr += '
'; var yb; printstr += '
' + youbi[0] + '
'; // 日曜日は赤で。 for(yb = 1; yb < 6; yb++){ printstr += '
' + youbi[yb] + '
'; } // 普通の日はたぶん黒。 printstr += '
' + youbi[yb] + '
'; // 土曜日は土色で。 printstr += '
'; // 月の日数と月の初日の曜日を取得します。 var maxday = monthday(year, month); // 月の日数。 var startweekday = weeknum(year, month, 1); // 初日の曜日。 var weekcycle, weekday, day; // 日付の部分を書き出しを開始します。 printstr += '
'; // 第一週目。途中から日付が入ります。 printstr += '
'; for(day = weekday = 0; weekday < 7; weekday++){ if(weekday >= startweekday){ if(weekday == 0){ printstr += '
' + (++day) + '
'; } else if(weekday == 6){ printstr += '
' + (++day) + '
'; } else{ printstr += '
' + (++day) + '
'; } }else{ printstr += '
'; } } printstr += ''; // 二週目以降。月の終わりまで。 for(weekcycle = 1; weekcycle <= 5; weekcycle++){ printstr += '
'; for(weekday = 0; weekday < 7; weekday++){ if(day < maxday){ if(weekday == 0){ printstr += '
' + (++day) + '
'; } else if(weekday == 6){ printstr += '
' + (++day) + '
'; } else{ printstr += '
' + (++day) + '
'; } }else{ printstr += '
'; } } printstr += '
'; } printstr += '
'; // テーブルを閉めます。 // 表示を書き出します。 print(printstr); wait(100); // 少しwaitをいれるといかにもいっしょうけんめい作っている雰囲気が出ます。 } // 月の日数を求めます。 function monthday(year, month){ if(month == 2){ return( (year%4 == 0 && year%100 != 0 || year%400 == 0) ? 29 : 28); } else if(month == 4 || month == 6 || month == 9 || month == 11){ return(30); } else{ return(31); } } // 指定された年月日の曜日を番号で返します。0が日曜日です。 function weeknum(year, month, day){ var result; if(month <= 2){ --year; /* 1月と2月は閏年の処理の都合上 前年度とみなす。すなわち13月と14月とみなす */ month += 12; } /* 月の処理がポイント。Zeller の公式。 7で割ったあまりが曜日をしめす。0が日曜日。 */ result = (year + (int)(year/4) - (int)(year/100) + (int)(year/400) + (int)((13*month + 8)/5) + day) % 7; if(result < 0){ result += 7; } return(result); } } // End of function CALENDER(){} # # # # ################################################ # CALCプログラム 15: 関数グラフ ################################################ # 与えられた関数のグラフを描きます。 function FUNCTIONGRAPH() { string f; // 描画する関数。 float x0, x1, xd, y0, y1, yd; // 描画範囲と目盛間隔。 # ディフォルト定義。 #どれかひとつを有効に。f入力省略で表示。 f = "sin(x)"; x0 = 0; x1 = 4PI; xd = 1; y0 = -1; y1 = 1; yd = 0.1; // 波:三角関数sin。 // f = "y=0;for(n=1; n<20;n+=2){ y+=sin(x*n)/n; }"; x0 = 0; x1 = 4PI; xd = 1; y0 = -1; y1 = 1; yd = 0.1; // 波の合成:矩形波。 // f = "y=0;for(n=1; n<20;n+=1){ y+=sin(x*n)/n; }"; x0 = 0; x1 = 4PI; xd = 1; y0 = -2; y1 = 2; yd = 0.1; // 波の合成:鋸歯状波。 // f = "tan(x)"; x0 = 0; x1 = 4PI; xd = 1; y0 = -10; y1 = 10; yd = 1; // 三角関数tan。 // f = "-x**2+x"; x0 = 0; x1 = 1; xd = 0.1; y0 = 0; y1 = 0.5; yd = 0.1; // 2次式。いわゆる放物線。 // f = "1/x"; x0 = -10; x1 = 10; xd = 1; y0 = -10; y1 = 10; yd = 1; // 逆数、いわゆる双曲線。 // f = "sqrt(x)"; x0 = -1; x1 = 10; xd = 1; y0 = -1; y1 = 5; yd = 1; // 平方根。 // f = "log(x)"; x0 = 0; x1 = 10; xd = 1; y0 = -5; y1 = 5; yd = 1; // 自然対数log。 // f = "exp(x)"; x0 = 0; x1 = 5; xd = 0.5; y0 = 0; y1 = 100; yd = 10; // 指数関数。 // f = "(1/sqrt(2*PI)) * exp(-x*x/2)"; x0 = -3; x1 = 3; xd = 0.1; y0 = 0; y1 = 0.5; yd = 0.01; // 標準正規分布曲線。 // f = "x / sqrt(3 + x * x)"; x0 = -5; x1 = 5; xd = 1; y0 = -1; y1 = 1; yd = 0.1; // 数値を強引に-1~1に収める関数1。 // f = "2/PI*atan(x)"; x0 = -5; x1 = 5; xd = 1; y0 = -1; y1 = 1; yd = 0.1; // 数値を強引に-1~1に収める関数2。 // 変数定義など準備。 var screenwidth = consolewidth(), screenheight = consoleheight(); int xp0, yp0, xp1, yp1;// 描画範囲に対応するグラフィック上の座標。 int leftmargin = 80, topmargin = 24, rightmargin = 40, bottommargin = 30; xp0 = leftmargin, yp0 = screenheight - bottommargin; // 描画範囲に対応するグラフィック上の座標を計算。 xp1 = screenwidth - rightmargin, yp1 = topmargin; float x, y; // グラフ描画変数。 int xp, yp; // グラフィック座標変数。 // 関数(式)の入力。 //clear(); println("関数 y=f(x) のグラフを描きます。xを変数として f(x) を入力してください。例: sin(x)"); if(f){ println("空を入力するとディフォルト定義された関数グラフを描きます。"); } else{ println("空なら終了します。"); } string ans = input("f(x) ="); if(!ans){ if(f){ drawgraph(); }else{ exit "終了します"; } } else{ f = ans; inputparam(); } exit; function inputparam(){ // 描画範囲の入力。 x0 = x1 = xd = y0 = y1 = yd = null; println("グラフを描くxの開始値 , 終了値 (, 目盛間隔)をカンマ(,)で区切って入力してください。数値、あるいは数式でもかまいません。"); ans = input("x(横)軸の開始値,終了値(,目盛間隔):"); int pos; string valstr = ""; for(pos = 0; ans[pos]; pos++){ if(ans[pos] == ","){ pos++; break; } valstr += ans[pos]; } if(valstr == "" || (x0 = calc(valstr)) === error){ print("開始値が正しく取得できません。"); exit "終了します。"; } valstr = ""; for(; ans[pos]; pos++){ if(ans[pos] == ","){ pos++; break; } valstr += ans[pos]; } if(valstr == "" || (x1 = calc(valstr)) === error){ print("終了値が正しく取得できません。"); exit "終了します。"; } if(x0 == x1){ print("開始値と終了値が同じ値です。グラフは描けません。"); exit "終了します。"; } valstr = ""; for(; ans[pos]; pos++){ valstr += ans[pos]; } if(valstr == "" || (xd = calc(valstr)) === error){ xd = null; } x0 = autoaxis(x0, x1, 2, -1); // 有効数字2桁にする。 x1 = autoaxis(x0, x1, 2, +1); if(xd === null){ xd = autoaxis(x0, x1, 2, 0); } println("開始x=", x0, ", 終了x=", x1, ", 目盛間隔=", xd); println("グラフのy軸の最小値 , 最大値 (, 目盛間隔)をカンマ(,)で区切って入力してください。数値、あるいは数式でもかまいません。"); println("空ですと自動で決めますが文句をいってはいけません。"); ans = input("y(縦)軸の最小値,最大値(,目盛間隔):"); if(ans != ""){ valstr = ""; for(pos = 0; ans[pos]; pos++){ if(ans[pos] == ","){ pos++; break; } valstr += ans[pos]; } if(valstr != "" && (y0 = calc(valstr)) === error){ print("最小値が正しく取得できません。"); exit "終了します。"; } valstr = ""; for(; ans[pos]; pos++){ if(ans[pos] == ","){ ++pos; break; } valstr += ans[pos]; } if(valstr != "" && (y1 = calc(valstr)) === error){ print("最大値が正しく取得できません。"); exit "終了します。"; } if(y0 == y1){ print("最小値と最大値が同じ値です。グラフは描けません。"); exit "終了します。"; } valstr = ""; for(; ans[pos]; pos++){ valstr += ans[pos]; } if(valstr != "" && (yd = calc(valstr)) === error){ yd = null; } } if(y0 === null || y1 === null || yd === null){ // 自動でy軸の範囲を決める。 float ymin = 1.797e308, ymax = -1.797e308; for(xp = xp0; xp <= xp1; xp++){ // Yの最小値、最大値を調べる。 x = xptox(xp); y = calc(f); if(y !== error && y !== NaN && !(img)y){ if(y < ymin){ ymin = y; } if(y > ymax){ ymax = y; } } } if(ymin == ymax){ ymin -= 1; ymax += 1; } // 常数の場合。 ymin = ymin - abs(ymin) * 0.1; // 少し色(余裕幅)をつける。 ymax = ymax + abs(ymax) * 0.1; ymin = autoaxis(ymin, ymax, 2, -1); // 有効数字2桁にする。 ymax = autoaxis(ymin, ymax, 2, +1); if(y0 === null){ y0 = ymin; } if(y1 === null){ y1 = ymax; } if(!yd){ yd = autoaxis(y0, y1, 2, 0); } } println("最小y=", y0, ", 最大y=", y1, ", 目盛間隔=", yd); drawgraph(); } function drawgraph(){ // グラフィックスクリーンを設定する。 screenstyle("border:2px solid silver;"); var sid = newscreen(screenwidth, screenheight); pen(1); color("red", "red"); text((xp0+xp1)/2, 16, "graph of "+f, 16, "center"); color("black", "transparent"); // 軸を描く。 for(x = x0; x <= (x1+xd*0.1); x += xd){ // x軸を描く。+xd*0.1は計算誤差回避のため。 if(round(x / xd) % 10 == 0){ // roundをつかうのは計算誤差丸めのため。 pen(1); color("black"); line(xtoxp(x), yp0, xtoxp(x), yp1); text(xtoxp(x), yp0+20, (str)(round(x/xd)*xd), 16, "center"); } if(round(x / xd) % 5 == 0){ pen(0.5); color("gray"); line(xtoxp(x), yp0, xtoxp(x), yp1); text(xtoxp(x), yp0+20, (str)(round(x/xd)*xd), 16, "center"); } else{ pen(0.5); color("silver"); line(xtoxp(x), yp0, xtoxp(x), yp1); } } for(y = y0; y <= (y1+yd*0.1); y += yd){ // y軸を描く。+yd*0.1は計算誤差回避のため。 if(round(y / yd) % 10 == 0){ // roundをつかうのは計算誤差丸めのため。 pen(1); color("black"); line(xp0, ytoyp(y), xp1, ytoyp(y)); text(xp0-4, ytoyp(y)+6, (str)(round(y/yd)*yd), 16, "right"); } if(round(y / yd) % 5 == 0){ pen(0.5); color("gray"); line(xp0, ytoyp(y), xp1, ytoyp(y)); text(xp0-4, ytoyp(y)+6, (str)(round(y/yd)*yd), 16, "right"); } else{ pen(0.5); color("silver"); line(xp0, ytoyp(y), xp1, ytoyp(y)); } } // グラフを描く。 var started = false; pen(1); color("red"); var xs, ys; for(xp = xp0; xp <= xp1; xp++){ x = xptox(xp); y = calc(f); // グラフを描く。おかしな値は無視。さらにCALCでは複素数OKなのでsqrt(-1)などエラーにならないので検出して無視。 if(y !== error && y !== NaN && !(img)y){ yp = ytoyp(y); if(started){ line(xs, ys, xp, yp); } else{ started = true; } xs = xp; ys = yp; } else{ started = false; } } // 描いたらマウスのx座標でのyの値を表示する。表示する関数を式(文字列)として指定し必要な描画変数を渡す。 sid.screenmousemove('valueinfo("'+f+'",'+x0+','+y0+','+x1+','+y1+','+xp0+','+yp0+','+xp1+','+yp1+')'); } function xtoxp(x){ // xの値に対応するグラフ点xpを求める。 return round(xp0 + (xp1 - xp0) * (x - x0) / (x1 - x0)); } function ytoyp(y){ // yの値に対応するグラフ点ypを求める。 if(y1 - y0){ return round(yp0 + (yp1 - yp0) * (y - y0) / (y1 - y0)); } else{ return (yp0 + yp1) / 2; } } function xptox(xp){ // グラフ点xpに対応するxの値を求める。 return x0 + (x1 - x0) * (xp - xp0) / (xp1 - xp0); } function yptoy(yp){ // グラフ点ypに対応するyの値を求める。 return y0 + (y1 - y0) * (yp - yp0) / (yp1 - yp0); } // グラフ軸の上限値下限値をきりのいいところに自動で決める。かなりいいかげん。 // ketaは有効桁数。mode < 0で下限値、> 0で上限値、0で間隔。 function autoaxis(ymin, ymax, keta=1, mode=1){ var sign = 1, ex10; if(ymax < ymin){ var t = ymin; ymin = ymax; ymax = t; sign = -1;} if(ymax - ymin){ ex10 = floor(log10(abs(ymax - ymin))); } else{ ex10 = 0; } if(mode > 0){ return ceil(ymax * 10**(keta-1-ex10)) * 10**(ex10-keta+1); } else if(mode < 0){ return floor(ymin * 10**(keta-1-ex10)) * 10**(ex10-keta+1); } else{ return sign * 10**(ex10-keta+1); } } // マウスの動きに追従して値を表示する。 function valueinfo(f,x0,y0,x1,y1,xp0,yp0,xp1,yp1){ // この関数をすべてのグラフ(スクリーン)に共通に使用するために表示用タグのidは手動設定で固定する。 // 手動でのidは自動では使われない充分大きな値、90000以上を推奨。 var pointid = 90001, lineid = 90002, text1id = 90003, text2id = 90004; xp = mpx(); x = xptox(xp); y = calc(f); if(y !== error && y !== NaN && !(img)y){ yp = ytoyp(y); } else{ yp = -100; } if(xp >= xp0 && xp <= xp1 && mpy() <= yp0 && mpy() >= yp1){ pen(1,"round","round"); color("green","transparent"); } else{ pen(0,"round","round"); color("transparent","transparent"); } lineid.line(xp, yp0, xp, yp1); pointid.circle(xp, yp, 5); // 値の位置に○を打つ。 font("normal","bold"); if(xp > xp1 - 150){ // 右に寄ったら左側に。 text1id.text(xp-6, yp+5, "y="+y, 16, "right"); text2id.text(xp-6, yp+25, "x="+x, 16, "right"); } else{ text1id.text(xp+6, yp+5, "y="+y, 16, "left"); text2id.text(xp+6, yp+25, "x="+x, 16, "left"); } return f+"のぐらふ"; } } // End of function FUNCTIONGRAPH(){} # # # # ################################################ # CALCプログラム 16: マンデルブロ集合を描く ################################################ # かの有名なフラクタル図形のマンデルブロ集合を描きます。 # ちなみに、 # z = z0; // z0は初期値。 # z = z**n + c: // 漸化式。cによって発散がどう変わるかをプロットします。 function MANDERBROT() { ######## パラメータ変数の定義と仮描画条件(全体図)の設定。パラメータは描画時に変更できます。 var z0 = 0; // zの初期値、通常0。複素数も可。変えると図形が変わる(いじける)。 var n = 2; // zの累乗数、通常2だがそれ以外(遅い!)複素数も可。半端な数を指定するとおもしろい模様が出るが計算は遅い。 var CenterX = -0.5; // 描画位置。x軸(実数軸)の中心点。実数。 var CenterY = 0; // 描画位置。y軸(虚数軸)の中心点。実数。 var Range = 1.5; // 描画範囲幅(片側)。実数。簡略化のため両軸は同じにする。 var LoopMax = 1000; // 発散計算の回数制限。当然多い方が美しい模様が得られやすいがあまり多いと時間がかかる。整数。 var ColorCycle = 0; // 色周期を発散回数何回ごとで回転するか。実数。場所によっては調整(大きく)しないといい模様が出ない。0なら自動。 var Size = 0; // 画像サイズ(ピクセル数、縦横は同じとする)。整数。0または100~数千(マシン能力に依存)、0は自動で画面に合わせる。 var BaseColor = "black"; // 発散しない時の色。普通は黒。 var StartColor = 0; // 色周期の開始位置。0.0~1.0。1.0で1回転し0.0と同じ。 ######## 描画条件例。頭の#は除いてコピーペーストで描画条件に投入。いい模様がでたらここに描画条件からコピペし保存しておくとよい。 # z0=0; n=2; CenterX=-0.5; CenterY=0; Range=1.5; LoopMax=1000; ColorCycle=0; Size=0; // 基本(全体)図 # z0=0; n=2; CenterX=0.2779913; CenterY=0.0080029; Range=1e-6; LoopMax=1000; ColorCycle=8; Size=0; // 渦巻模様。 # z0=0; n=2; CenterX=0.380845649928; CenterY=-0.259538725078; Range=1e-11; LoopMax=1000; ColorCycle=55; Size=0; // 樹状模様。 # z0=0; n=2; CenterX=-0.17589525086; CenterY=1.08662172163; Range=5e-10; LoopMax=1000; ColorCycle=20; Size=0; // 放射マンデル。 # z0=0; n=1.3045; CenterX=0.005; CenterY=-0.3; Range=0.056; LoopMax=1000; ColorCycle=0; Size=0; BaseColor="#DEF"; // モダンアート。遅い!。 ######## 以上、設定。 // 実行 clear(); // 前の画面は消去。画面のマウスアクションが残っていてパラメータはリセットされているのでエラーになる。 println("描画条件を確認し、必要なら変更して[Enter]するか、または「入力」ボタンを押してください。"); println("
描かれた図形上で希望の中心点からマウスプッシュ+ドラッグで次回の新しい描画範囲を設定できます
。"); if(setcookie("mander_cookiecheck", "OK") != "OK"){ println("(ただしcookieが有効の場合)。"); } // cookieが無効なら表示。 println("
空にすると初期条件(全体図)に戻します。
"); var setting; var nextcycle = 1; setcookie("mander_setting", " z0=0; n=2; CenterX=-0.5; CenterY=0; Range=1.5; LoopMax=1000; ColorCycle=0; Size=0;");// 初期値 do{ if((setting = getcookie("mander_setting")) === false){ // 保存されている描画条件があれば読み出し、なければ初期化。 setting = " z0=" + z0 +"; n="+ n + "; CenterX=" + CenterX + "; CenterY=" + CenterY+"; Range=" + Range + ";" + " LoopMax=" + LoopMax + "; ColorCycle=" + ColorCycle + "; Size=" + Size + ";"; } input("描画条件:", setting); // 描画条件をinput欄に表示し確認と編集。 if(!trim(setting)){ // 空なら初期条件に戻す。 setting = " z0=0; n=2; CenterX=-0.5; CenterY=0; Range=1.5; LoopMax=1000; ColorCycle=0; Size=0;"; } calc(setting); // 描画条件を評価、 setcookie("mander_setting", setting, 0); // 次回に備えて保存。 var starttime = time(); mander(); // マンデルの計算と描画。 println("\n
続けるなら(新しい中心位置と範囲をマウスで設定可能)[入力]ボタン。終わるなら[停止]ボタン。
"); nextcycle = 0; nextcycle = secretinput("", nextcycle); if(!nextcycle){ exit "終了"; } }while(nextcycle); exit; function mander(){ // 特に関数化する必要はなかったけどそのままになっている。 // 描画スクリーンを設定。 var img_left = 180, img_top = 10, img_right = 80, img_bottom = 30; // 座標文字の分。 var img_size = max((!Size ? consoleheight() - 72 : Size), 100); screenstyle("border:2px solid silver;cursor:crosshair;"); newscreen(img_left+img_size+img_right, img_top+img_size+img_bottom); // 左と下に座標目盛を表示。 color("#555"); font('','','monospace'); c_line(img_left-10, img_top, img_left-4, img_top); c_text(img_left-12, 5+img_top,(CenterY+Range+""),14,"right"); c_line(img_left-10, img_top+img_size/2, img_left-4, img_top+img_size/2); c_text(img_left-12, 5+img_top+img_size/2, (CenterY+""), 14, "right"); c_line(img_left-10, img_top+img_size, img_left-4, img_top+img_size); c_text(img_left-12, 5+img_top+img_size, (CenterY-Range+""), 14,"right"); c_line(img_left, img_top+img_size+4, img_left, img_top+img_size+10); c_text(img_left, 24+img_top+img_size, (CenterX-Range+""),14,"center"); c_line(img_left+img_size/2, img_top+img_size+4, img_left+img_size/2, img_top+img_size+10); c_text(img_left+img_size/2, 24+img_top+img_size, (CenterX+""), 14, "center"); c_line(img_left+img_size, img_top+img_size+4, img_left+img_size, img_top+img_size+10); c_text(img_left+img_size, 24+img_top+img_size, (CenterX+Range+""), 14,"center"); // イメージ範囲をわかりやすく一度薄く塗りつぶし。 color('#DDD','#DDD'); c_rect(img_left, img_top, img_size, img_size); color("transparent", "transparent", 1, 1); // いったん色を解除(色、透明度共になし)。 // 計算と描画。高速化対応のため直接javascriptで計算・描画ループを回す。 // … 少しずるいがCALCではこんなことも簡単にできるんだよということで大目に見てくれ! // 以下CALCでの処理とjavascriptでの処理が混在していてちとわかりにくいが...。 // jseval()でjavascriptへ処理を投入。(jseval()はjavascriptのeval()を呼び出す。) // 変数やパラメータのセット。CALCからjavascriptにほとんどそのまま移行したので同じ名前を使っているが各々の変数は別物。 jseval("var z0re="+(real)z0+", z0im="+(image)z0+", nre="+(real)n+", nim="+(image)n+";"); jseval("var CenterX="+CenterX+", CenterY="+CenterY+", Range="+Range+";"); jseval("var ColorCycle="+ColorCycle+", LoopMax="+LoopMax+";"); jseval("var BaseColor=\""+BaseColor+"\", StartColor="+StartColor+";"); jseval("var img_left="+img_left+", img_top="+img_top+", img_size="+img_size+";"); // 計算・描画ループを関数としてjavascriptに定義。 jseval(' function manderline(y, cim){ // 画像1ライン分の計算と描画。 var zre, zim, zretmp, zimtmp, cre, absz1, absz2, loop, cf, ccc, r, g, b; var data1=newdata(), data2=newdata(); // CALCの複素数計算関数を使うためのデータコンテナ。 for(var x=0; x
2){ break; } // 発散限界はnの値によらず > 2 としている。 absz1 = absz2; if(nre == 2 && nim == 0){ // 普通にnが2のときの z = z**2 + c; の計算。計算は簡単で速い。 zretmp = zre; zimtmp = zim; zre = zretmp * zretmp - zimtmp * zimtmp + cre; zim = 2 * zretmp * zimtmp + cim; } else{ // n=2でなければしょうがないので汎用複素数計算でやる。またもずるしてすでにあるCALCの複素数計算関数を使う。 data1.rv = zre; data1.iv = zim; // CALC複素数累乗計算関数を使うためのデータコンテナに値をセット。 data2.rv = nre; data2.iv = nim; cx2power(data1, data1, data2); // CALC複素数累乗計算関数。 zre = data1.rv + cre; zim = data1.iv + cim; } } // 色の決定。図形の美しさはほとんどここで決まり。 if(loop >= LoopMax){ color(BaseColor); } // 色をセット。発散しなければ普通は黒だがBaseColorで定義。 else{ // 他は発散の回数に応じて色付け。さらに発散時の速さを推定してグラデーションを付加。 // 今回と直前のzの絶対値から発散回数を修正。強引に小数でのabs(z)が2になった位置cfを推定する。 // これにより色にグラデーションをかける。 cf = loop - (absz2 - 2) / (absz2 - absz1); ccc = ColorCycle ? ColorCycle : 3*Math.sqrt(cf); // 係数の3にさほど大きな意味はない。模様の出方で決めた。 // 色をccc(corrected color cycle)で1回転としてrgbの各値に振り込む。 r = Math.floor(127 * (1 + Math.sin(2*Math.PI * cf / ccc + 2*Math.PI * (0/3 + StartColor)))); g = Math.floor(127 * (1 + Math.sin(2*Math.PI * cf / ccc + 2*Math.PI * (1/3 + StartColor)))); b = Math.floor(127 * (1 + Math.sin(2*Math.PI * cf / ccc + 2*Math.PI * (2/3 + StartColor)))); color("rgb(" +r+ "," +g+ "," +b+ ")"); // 色をセット。 } c_point(img_left + x, img_top + y); // セットした色で点を打つ。 } } // canvasへの描画。描画するcanvasのcontextはCTX(CALCのスクリプトで定義されている)。 function color(col){ CTX.fillStyle = col; } // 色の指定。 function c_point(x, y){ CTX.fillRect(x, y, 1, 1); } // 点(幅と高さが1の四角形)を描く。 '); // 以上javascriptでの1ライン分の計算と描画処理関数。 // 1ライン分ずつ上記関数を呼び出して計算、描画。 for(var y=0; y
2){ break; } // 発散限界はnの値によらず > 2 としている。 absz1 = absz2; if(nre == 2 && nim == 0){ // 普通にnが2のときの z = z**2 + c; の計算。計算は簡単で速い。 zretmp = zre; zimtmp = zim; zre = zretmp * zretmp - zimtmp * zimtmp + cre; zim = 2 * zretmp * zimtmp + cim; } else{ // n=2でなければしょうがないので汎用複素数計算でやる。またもずるしてすでにあるCALCの複素数計算関数を使う。 data1.rv = zre; data1.iv = zim; // CALC複素数累乗計算関数を使うためのデータコンテナに値をセット。 data2.rv = nre; data2.iv = nim; cx2power(data1, data1, data2); // CALC複素数累乗計算関数。 zre = data1.rv + cre; zim = data1.iv + cim; } } // 色の決定。図形の美しさはほとんどここで決まり。 if(loop >= LoopMax){ color(BaseColor); } // 色をセット。発散しなければ普通は黒だがBaseColorで定義。 else{ // 他は発散の回数に応じて色付け。さらに発散時の速さを推定してグラデーションを付加。 // 今回と直前のzの絶対値から発散回数を修正。強引に小数でのabs(z)が2になった位置cfを推定する。 // これにより色にグラデーションをかける。 cf = loop - (absz2 - 2) / (absz2 - absz1); ccc = ColorCycle ? ColorCycle : 3*Math.sqrt(cf); // 係数の3にさほど大きな意味はない。模様の出方で決めた。 // 色をccc(corrected color cycle)で1回転としてrgbの各値に振り込む。 r = Math.floor(127 * (1 + Math.sin(2*Math.PI * cf / ccc + 2*Math.PI * (0/3 + StartColor)))); g = Math.floor(127 * (1 + Math.sin(2*Math.PI * cf / ccc + 2*Math.PI * (1/3 + StartColor)))); b = Math.floor(127 * (1 + Math.sin(2*Math.PI * cf / ccc + 2*Math.PI * (2/3 + StartColor)))); color("rgb(" +r+ "," +g+ "," +b+ ")"); // 色をセット。 } c_point(img_left + x, img_top + y); // セットした色で点を打つ。 } } // canvasへの描画。描画するcanvasのcontextはCTX(CALCのスクリプトで定義されている)。 function color(col){ // 色の指定。 CTX.fillStyle = col; CTX.globalAlpha = 1; } function c_point(x, y){ // 点を打つがサイズが1だとなぜか下地が滲むことがある。計算でのピクセルずれか? CTX.fillRect(x-0.25, y-0.25, 1.5, 1.5); } '); // 以上javascriptでの1ライン分の計算と描画処理関数。 // 1ライン分ずつ上記関数を呼び出して計算、描画。 for(var y=0; y
0: 終了"); println("
1: 庭園の松
"); println("
2: 盆栽の松(傘松仕立て)
"); println("
3: 湯島の白梅
"); println("
4: 旧家のしだれ桜
"); println("
5: 銀座の柳
"); println("
6: けやき公園のケヤキ(時間がかかります)
"); println("
7: 冬枯れのケヤキ(時間がかかります)
"); println("
8: 山寺のもみぢ
"); println("
9: 峠の一本杉(葉っぱの数が多くすさまじく時間がかかります)
"); // println("
10: 花水木
"); treenum = (int)input("樹種の番号:"); if(!treenum){ exit "終了します。"; } else if(treenum == 1){ println("庭園の松を描きます。"); settree("庭園の松"); } else if(treenum == 2){ println("盆栽の松(傘松仕立て)を描きます。ただし剪定は上手ではありませのでなかなかいい形になりません。"); settree("盆栽の松(傘松仕立て)"); } else if(treenum == 3){ println("湯島の白梅を描きます。花は葉っぱを白にしてごまかします。"); settree("湯島の白梅"); } else if(treenum == 4){ println("旧家のしだれ桜を描きます。花は葉っぱをピンクにしてごまかします。"); settree("旧家のしだれ桜"); } else if(treenum == 5){ println("銀座の柳を描きます。"); settree("銀座の柳"); } else if(treenum == 6){ println("けやき公園のケヤキを描きます。かなり時間がかかります。"); settree("けやき公園のケヤキ"); } else if(treenum == 7){ println("冬枯れのケヤキを描きます。かなり時間がかかります。"); settree("冬枯れのケヤキ"); } else if(treenum == 8){ println("山寺のもみぢを描きます。"); settree("山寺のもみぢ"); } else if(treenum == 9){ println("峠の一本杉を描きます。花粉は出しませんのでご安心を。でもすさまじく時間がかかります。"); settree("峠の一本杉"); } // else if(treenum == 10){ println("花水木を描きます。"); settree("花水木"); } else{ println("該当の樹種がありません。仮パラメータを設定しますので適当に変えてください。"); settree(""); } // defaultを設定。 tree(); exit "終了します。"; function tree(){ // 条件入力。 println('\n以下のパラメータを使って樹を育てます。希望に応じて数値を変更してください。'); println('上限下限値が設定されれば標準値に加味して樹の成長はある程度ランダム化されます。'); table_input(3, arylen(param), param, ctitle, ptitle, pinfo); // パラメータの提示と変更、入力。 var starttime = time(); // 描画画面を設定。 screenstyle('border:1px solid gray;'); newscreen(SCRN_Width, SCRN_Height); // 背景。空と地面を描く。 var sp, hp, ep, vp, startcolor, endcolor, r,g,b; sp = 0; hp = SCRN_Height / 1.618; ep = SCRN_Height; // 空。晴天で。 pen(2, "round", "round"); startcolor = [120, 160, 255], endcolor = [160, 200, 255]; for(vp = sp; vp <= hp; vp++){ // r,g,b値は整数でないとだめなブラウザあり。 r = (int)(startcolor[0] + (endcolor[0] - startcolor[0]) * (vp - sp) / (hp - sp)); g = (int)(startcolor[1] + (endcolor[1] - startcolor[1]) * (vp - sp) / (hp - sp)); b = (int)(startcolor[2] + (endcolor[2] - startcolor[2]) * (vp - sp) / (hp - sp)); color('rgb('+r+','+g+','+b+')', 'rgb('+r+','+g+','+b+')'); c_line(0, vp, SCRN_Width, vp); } // 地面。 startcolor = [160, 200, 140], endcolor = [100, 160, 80]; // 上, 下。 for(vp = hp; vp <= ep; vp++){ // r,g,b値は整数でないとだめなブラウザあり。 r = (int)(startcolor[0] + (endcolor[0] - startcolor[0]) * (vp - hp) / (ep - hp)); g = (int)(startcolor[1] + (endcolor[1] - startcolor[1]) * (vp - hp) / (ep - hp)); b = (int)(startcolor[2] + (endcolor[2] - startcolor[2]) * (vp - hp) / (ep - hp)); color('rgb('+r+','+g+','+b+')', 'rgb('+r+','+g+','+b+')'); c_line(0, vp, SCRN_Width, vp); } // 樹を植えて育てて描く。 plant(["x":320,"y":40], ["h":360*random()-180,"v":90], 1); println("\nTime=", ceil((time()-starttime)/60000), "分"); } // まずは樹を植えて育てるデータをそろえる。 function plant(pos=["x":320,"y":40], drc=["h":-90,"v":90], viewfactor=1){ // 育成最後までグローバルに使うパラメータを再設定。 if((AdultAge = param_value("成木年")) <= 0){ // 成木年。 println('\n
成木年の指定値が不適当です。
'); exit "終了します。"; } if((TreeAge = param_value("樹齢")) < 0){ // 樹齢。 println('\n
樹齢の指定値が不適当です。
'); exit "終了します。"; } var heightfactor = param_value("樹高"); if((Height = SCRN_Height * heightfactor) <= 0){ // 樹高。 println('\n
樹高の指定値が不適当です。
'); exit "終了します。"; } if((Width = Height * param_value("樹幅")) <= 0){ // 樹幅。 println('\n
樹幅の指定値が不適当です。
'); exit "終了します。"; } if((Thick = heightfactor * param_value("樹径") * max(total_grow(TreeAge)**0.33, 0.5) * 10) <= 0){ // 幹太さ。係数は見てくれから決めた。 println('\n
樹径の指定値が不適当です。
'); exit "終了します。"; } if(calc('HKAry=['+param[pn."葉形状"][0]+'];', 1) === error){ // 葉形状を配列に。 println('\n
葉形状がデータ変換できません。
');exit; }; if(calc('HaIro1=['+param[pn."葉色枝先"][0]+'];', 1) === error){ // 葉色枝先データを配列に。 println('\n
葉色枝先がデータ変換できません。
');exit; }; if(calc('HaIro2=['+param[pn."葉色枝元"][0]+'];', 1) === error){ // 葉色枝元データを配列に。 println('\n
葉色枝元がデータ変換できません。
');exit; }; store(); // 描画パラメータ退避。 // 樹を描く。 anchor(pos["x"], SCRN_Height-pos["y"]); scale(viewfactor, viewfactor); branch(TreeAge, TreeAge, pos, drc); restore(); // 描画パラメータ戻し。 } // 幹や枝を描く。 // branchge:枝年齢、myage:描画枝の年齢(樹齢→0へと)、pos:枝分枝位置、drc:伸枝方向、 // branchcount:分枝回数(制限用)。 function branch(branchage, myage, pos, drc, branchcount=0){ static branchmax = 4; // 枝の分枝回数制限。枝が増えすぎて描ききれなくなるのを少し予防。 if(branchage < myage || myage < 0){ return; } var twigage = 5; // 若枝として描画する年枝。枝出しを少し多くし3年枝以下には葉っぱをつける。 if(myage <= twigage){ twig(myage, pos, drc); return; } // 枝の仮成長係数。縦横の成長係数、枝向きなど考慮。 var growfactor = param_value("樹高") * max(sind(drc["v"]), 0) + param_value("樹高") * param_value("樹幅")/2 * abs(cosd(drc["v"])); // 成長能力は枝向きで変わるらしい。 var parting = 10 + 2*param_value("古枝分枝度"); // 分割度合い。枝が多いときは分割も細かく。 var partage = 1/partial_grow(TreeAge-myage, TreeAge-myage+1)/parting; // 成長度合いがほぼ均等になるよう分割するが、 partage = min(max(partage, 1), AdultAge/2); // 上限下限は設ける。 if(myage - 1.5 * partage < twigage){ // 若枝描画境界を飛び越えないよう、また少しの残りは入れ込む。 partage = myage - twigage; } var toage = myage - partage; // 区間で成長する画面上の長さ。 var partlen = Height * growfactor * partial_grow(TreeAge-myage, TreeAge-toage); // 枝を伸ばし分枝度に応じて新枝を伸ばす。 var basepos = copy pos, newpos = copy pos; // 新枝の伸びる位置。 var basedrc = copy drc, newdrc = copy drc; // 新枝の伸びる方向。 // 新枝を出す基本枝回り角。 static startturnangle = 0; startturnangle = (startturnangle + 137) % 360; var stepturnangle = 360 / max(param_value("芽出態"), 2); var turnangle, tafactor, modifiedturnangle; // 分枝頻度。徐々に古枝分枝度から若枝分枝度に近づける。 var bsf1 = param_value("古枝分枝度"), bsf2 = param_value("若枝分枝度"); var edadashi = growfactor * (bsf1 + (bsf2-bsf1)*((TreeAge-myage)/TreeAge)); var bthick = Thick, newthick = Thick; // Thick(太さ)を変更するときの記憶用。 var fromwidth, towidth; var edagrow1 = 0, edagrow2 = 0, edagrow3 = 0; var edacolor = "rgb("+EdaIro["r"]+","+EdaIro["g"]+","+EdaIro["b"]+")"; // 枝より奥の側枝。 if(branchcount <= branchmax){ turnangle = startturnangle; do{ if(cosd(drc["h"]+turnangle) < 0 && (edadashi-edagrow1) > random()){ if(total_grow(branchage-myage)/total_grow(branchage) > (param_value("枝上り")-1)/10){ color(edacolor, edacolor); edagrow1 += newbranch(); } } }while((turnangle += stepturnangle) < startturnangle + 360); } // 主幹。ある程度育つまでは消失させない。側枝がないとき消失するとぼうず枝になるのでこのときは主幹を伸ばす。 if(myage <= TreeAge || param_value("直幹性") >= 5*random() || !edagrow1){ if(branchcount > branchmax){ // 分枝制限。枝出しは終りにして葉っぱ枝をつける。ぼうず枝にはしない。 twig(twigage, basepos, basedrc); } else{ // 主幹・枝幹を伸ばす。 // 下垂性考慮。枝元ほど効かす。枝に累積したのを元枝の角度に入れ込む。 var ksfactor = partlen * param_value("下垂性") * min(total_grow(myage),1)**2; basedrc["v"] -= ksfactor * cosd(basedrc["v"]) * 0.2; if(basedrc["v"] < -20){ basedrc["v"] = -20; } // 下方向へは限度を設ける。限度はあるらしい。 // 方向を少しランダマイズすると枝が曲って自然っぽく見える。 var rfactor = partlen * min(max((1 - param_value("直幹性")/10), 0), total_grow(TreeAge-toage))**2; newdrc["v"] = basedrc["v"] + rfactor * (random()-0.5) * 5.0; newdrc["h"] = basedrc["h"] + rfactor * (random()-0.5) * 3.0; // 枝向き考慮。枝先ほど効かす。 var emfactor = partlen * param_value("枝向き"); newdrc["v"] += emfactor * ((branchage-toage+twigage)/branchage)**2 * cosd(newdrc["v"]) * 0.2; if(newdrc["v"] < -20){ newdrc["v"] = -20; } // 下方向へは限度を設ける。限度はあるらしい。 color(edacolor, edacolor); if(edagrow1 && param_value("芽出態") < 2){ // 互生では枝が出たら幹を半分伸ばす。 fromwidth = stickwidth(bthick, myage, twigage); towidth = stickwidth(Thick, (myage=(myage+toage)/2), twigage); bthick = Thick; stick(basepos, (partlen /= 2), basedrc, newdrc, fromwidth, towidth, basepos, basedrc); } fromwidth = stickwidth(bthick, myage, twigage); towidth = stickwidth(Thick, toage, twigage); bthick = Thick; stick(basepos, partlen, basedrc, newdrc, fromwidth, towidth, newpos, newdrc); branch(branchage, toage, newpos, newdrc, branchcount); Thick = bthick; edagrow2++; } } // 枝より手前の側枝。 if(branchcount <= branchmax){ turnangle = startturnangle; do{ if(cosd(drc["h"]+turnangle) >= 0 && (edadashi-edagrow3) >= random()){ if(total_grow(branchage-myage)/total_grow(branchage) > (param_value("枝上り")-1)/10){ color(edacolor, edacolor); edagrow3 += newbranch(); } } }while((turnangle += stepturnangle) < startturnangle + 360); } return; // このあとのfunction定義を関数実行の度に処理しないように。なくてもいいけど。 // 新しい枝を出す。引数はとらず親関数内の変数を直に参照するのでわかりやすいよう関数内に定義。 function newbranch(){ // 枝出し角に枝開き考慮。 var ehfactor = param_value("枝開き") * myage/(0.5*AdultAge+TreeAge); var forkangle = param_value("分枝角") + 10 * ehfactor * sind(basedrc["v"]); forkangle = min(max(forkangle, 15), 110); // 限度はあるらしい。 // 枝出しの枝回り角修正係数。斜めの枝は横に枝を出す傾向を入れ込み修正。 tafactor = haply_value(cosd(drc["v"]), 0.0, 1.0, 2.0); modifiedturnangle = turnangle * (1-tafactor) + (turnangle<0 ? -1 : 1)*(90-sind(drc["v"]-45)*10) * tafactor; growdirection(basedrc, modifiedturnangle, forkangle, newdrc); if(branchcount >= 1){ // 基本剪定。下がり枝、不要な立ち枝はカット。それ以上の形づくりはやらない。 // 下がり枝。 if(newdrc["v"] < -15){ return 0; } // 立ち枝。上伸性がある場合は制限を緩める。 if(newdrc["v"] > 75 + 1.0*(param_value("枝向き")-ehfactor)){ return 0; } // 内向き枝は判定が難しいのでやらない。 } // 枝を伸ばす。枝を出したら太さを振り分ける。 bthick = Thick; Thick = Thick * 0.7 * (1 - (max(Height-Width, 0) / Height)); newthick = sqrt(bthick * bthick - Thick * Thick); fromwidth = stickwidth(Thick, myage, twigage); towidth = stickwidth(Thick, toage, twigage); stick(basepos, partlen, newdrc, newdrc, fromwidth, towidth, newpos, newdrc); branch(toage, toage, newpos, newdrc, branchcount+1); Thick = newthick; return 1; } } // 幹や枝の本体の一節を描く。近似的にだがなるべくなめらかに太さと方向を変える。 // 最終位置を配列newpos、方向をnewdrcに返す。 function stick(basepos, length, fromdrc, todrc, fromwidth, towidth, newpos, newdrc){ if(length <= 0){ newpos = copy basepos; newdrc = copy todrc; return; } // 処理不要。元の値をそのまま返す。 // ループの中での配列アクセスを減らすため単純変数に置き換え。 var bposx = basepos["x"], bposy = basepos["y"]; var fdrch = fromdrc["h"], fdrcv = fromdrc["v"]; var tdrch = todrc["h"], tdrcv = todrc["v"]; var fposx, fposy, tposx, tposy, nposx0, nposy0, nposx1, nposy1, width; nposx0 = bposx; nposy0 = bposy; for(var ln = 0; ln <= length; ln++){ // 幹を描画。 fposx = bposx + ln * sind(fdrch) * cosd(fdrcv); fposy = bposy + ln * sind(fdrcv); tposx = bposx + ln * sind(tdrch) * cosd(tdrcv); tposy = bposy + ln * sind(tdrcv); nposx1 = (fposx * (length-ln) + tposx * ln) / length; nposy1 = (fposy * (length-ln) + tposy * ln) / length; width = (fromwidth * (length-ln) + towidth * ln) / length; pen(width, "round", "round"); c_line(nposx0, SCRN_Height-nposy0, nposx1, SCRN_Height-nposy1); nposx0 = nposx1; nposy0 = nposy1; } newpos["x"] = nposx0; newpos["y"] = nposy0; newdrc["h"] = tdrch; newdrc["v"] = tdrcv; } // 枝の太さを決める。古枝と若枝とのつなぎに少し留意。 // 太さは樹齢に対し0.33乗(立方根)が実感に近い。0.5乗では太くなりすぎる。 function stickwidth(thick, myage, twigage){ if(myage == 0){ return 0.3; } thick = max(thick, 1); return max((thick*myage/TreeAge)*myage**0.33, 0.3*twigage**0.33); } // 若枝を描く。枝出しを普通の枝より少し多くし葉っぱをつける。 function twig(myage, pos, drc){ if(TreeAge < myage || myage < 0){ return; } static jsseted = false; if(!jsseted){ jsset(); jsseted = true; } // 葉っぱを描く関数登録。 var partlen, hncut; // 葉間長さと芽の剪定位置。剪定位置を枝出しの都合上概略計算で先に決めておく。 if(myage == 0){ // 芽が地上まで伸びて最初の葉が開いた状態を樹齢0とする。 partlen = 5; // ちょこっと茎を。 hncut = 1; // 最初の1芽のみ。 } else if(myage <= 3){ // 葉っぱをつける枝については葉数に応じて芽の寸法を確保。 // 枝は葉位置で分割描画する。 partlen = max(abs(param_value("葉寸法")), 0.3) * param_value("葉間隔") * strlen(param[pn."葉形状"][0])/2; // 葉っぱをつける枝については葉数に応じて芽の寸法を確保。 hncut = param_value("葉数") / max(param_value("芽出態"), 1); } else{ partlen = partial_grow(TreeAge-myage, TreeAge-myage+min(1, myage)) * Height * Width / sqrt((Height * cosd(drc["v"]))**2 + (Width * sind(drc["v"]))**2); hncut = 1; } // 新枝を伸ばす度合。 var edadashi = param_value("若枝分枝度") / max(param_value("芽出態"), 1)**2 * 0.3; var basepos = copy pos, newpos = new pos; // 枝や葉の伸びる位置。 var basedrc = copy drc, newdrc = []; // 枝や葉の伸びる方向。 var startturnangle = haply_value(90, 0, 180, 2); // 新枝や葉を出す枝回り角。 var stepturnangle = 360 / max(param_value("芽出態"), 2); var turnangle, forkangle; var edacolor = "rgb("+EdaIro["r"]+","+EdaIro["g"]+","+EdaIro["b"]+")"; // 枝の色。 var emfactor = partlen * (param_value("枝向き")); // 枝向き考慮係数。 var hn, medashi, tafactor, modifiedturnangle; // 葉っぱをつけるか枝を伸ばす。 for(hn = 0; (hn <= hncut || hn < hncut+1 && hncut%1 > random()); hn++){ medashi = param_value("芽出態"); // 枝や葉軸の方向を決める。枝向きを少し多く考慮。 basedrc["v"] += emfactor * cosd(basedrc["v"]) * 0.1; basedrc["v"] = min(max(basedrc["v"], -90), 90); if(cosd(drc["h"]+startturnangle) == 0){ startturnangle += 1; } // 境界条件回避。 // 枝より奥の葉っぱまたは枝。 if(hn >=1){ turnangle = startturnangle; do{ if(cosd(drc["h"]+turnangle) < 0 && (medashi >= 2 || medashi >= 1 && hn % 2 == 0 || medashi % 1 > random())){ forkangle = param_value("分枝角"); // 分枝角。元枝から離れる角度。葉軸の角度にも流用。 if(myage > 1 && edadashi*(hn/hncut)**2 > random()){ // 枝を伸ばす。枝先ほど新枝は出やすいようだ。 // 枝出しの枝回り角修正係数。斜めの枝は横に枝を出す傾向だが新枝はランダム性が強い。 tafactor = haply_value(cosd(drc["v"]), 0.0, 1.0, 1.0); modifiedturnangle = turnangle * (1-tafactor) + (turnangle<0 ? -1 : 1)*(90-sind(drc["v"]-45)*10) * tafactor; growdirection(basedrc, modifiedturnangle, forkangle, newdrc); twig(myage-1, basepos, newdrc); } if(myage <= 3){ happa(); } // 葉っぱをつける。 } }while((turnangle += stepturnangle) < startturnangle + 360); } // 枝先なら新枝を出す。 if(myage > 1 && hn >= hncut){ if(myage > 3 || edadashi > random()){ twig(myage-1, basepos, basedrc); } } else if(hn < hncut){ // あるいは元枝を伸ばす。 if(edadashi > 0){ newpos["x"] += partlen * sind(basedrc["h"]) * cosd(basedrc["v"]); newpos["y"] += partlen * sind(basedrc["v"]); pen(stickwidth(Thick, myage-hn/hncut, myage)); color(edacolor, edacolor); c_line(basepos["x"], (SCRN_Height-basepos["y"]), newpos["x"], (SCRN_Height-newpos["y"])); } } // 枝より手前の葉っぱまたは枝。 turnangle = startturnangle; if(hn >= 1){ do{ if(cosd(drc["h"]+turnangle) >= 0 && (medashi >= 2 || medashi >= 1 && hn % 2 == 1 || medashi % 1 > random())){ forkangle = param_value("分枝角"); if(myage > 1 && edadashi*(hn/hncut)**2 > random()){ // 枝を伸ばす。枝先ほど新枝は出やすいようだ。 // 枝出しの枝回り角修正係数。斜めの枝は横に枝を出す傾向だが新枝はランダム性が強い。 tafactor = haply_value(cosd(drc["v"]), 0.0, 1.0, 1.0); modifiedturnangle = turnangle * (1-tafactor) + (turnangle<0 ? -1 : 1)*(90-sind(drc["v"]-45)*10) * tafactor; growdirection(basedrc, modifiedturnangle, forkangle, newdrc); twig(myage-1, basepos, newdrc); } if(myage <= 3){ happa(); } // 葉っぱをつける。 } }while((turnangle += stepturnangle) < startturnangle + 360); } // 継続判定。 if(hn >= hncut){ break; } // 葉数制限で剪定された。 // 剪定されていなければ次の成長パラメータを設定しさらなる成長を願う。 basepos["x"] = newpos["x"]; basepos["y"] = newpos["y"]; if((startturnangle += (stepturnangle % 180)*0.618) > 180){ startturnangle -= 360; } } return; // このあとのfunction定義を毎回処理しないように。なくてもいいけど。 // 葉っぱを描く。葉っぱの数が多く時間がかかるのでjavascriptで直接描くことにした。少し速くなった。 // 引数はとらず親関数内の変数を直に参照するのでわかりやすいよう関数内に定義。 function happa(){ var hsize = param_value("葉寸法"); if(hsize <= 0){ return; } // 枝先と元で少し色を変える。 var hcolor, hcf; // 葉っぱの寸法、色と色決め変数。 if(myage <= 1){ hcf = (1/(1+0.3*(hncut-hn))); }else{ hcf = 0; } hcf = haply_value(hcf, 0, 1, 0.3); // さらに少しランダマイズ。 hcolor = "rgb(" + floor(HaIro1["r"]*hcf+HaIro2["r"]*(1-hcf)) + "," + floor(HaIro1["g"]*hcf+HaIro2["g"]*(1-hcf)) + "," + floor(HaIro1["b"]*hcf+HaIro2["b"]*(1-hcf)) + ")"; // rgb()が整数でないと誤動作するブラウザあり。 color(hcolor, hcolor); // javascriptの葉っぱ描画関数を呼び出す。引数もすべて文字列としてjseval()で呼び出す。 jseval('\ pos["x"]='+basepos["x"]+'; pos["y"]='+basepos["y"]+';\ basedrc["h"]='+basedrc["h"]+'; basedrc["v"]='+basedrc["v"]+';\ jshappa(pos, '+hsize+', basedrc, '+turnangle+', '+forkangle+');\ '); return; } } // 葉っぱを一枚一枚描くのに時間がかかるのでjavascriptで直接描く関数を登録。 function jsset(){ jseval(' var HKAry = ['+param[pn."葉形状"][0]+']; function sind(angle){ return Math.sin(angle * Math.PI / 180); } function cosd(angle){ return Math.cos(angle * Math.PI / 180); } function asind(value){ return Math.asin(value) * 180 / Math.PI; } function atand(value){ return Math.atan(value) * 180 / Math.PI; } function abs(value){ return value < 0 ? -value : value; } var pos = [], basedrc = []; // 葉っぱを描く。 function jshappa(pos, size, basedrc, turnangle, forkangle){ if(size <= 0){ return; } var hcdrc = []; // 葉柄及び葉軸の角度。 growdirection(basedrc, turnangle, forkangle, hcdrc) var hnmax = ' + arylen(HKAry) + '; var hn, wh, wv, hcpos = [], h1pos = [], h2pos = []; CTX.lineWidth = size; for(hn = 0; hn <= hnmax; hn++){ // 葉柄及び葉の中心線の位置。 hcpos["x"] = pos["x"] + size * hn * sind(hcdrc["h"]) * cosd(hcdrc["v"]); hcpos["y"] = pos["y"] + size * hn * sind(hcdrc["v"]); // 葉の幅の両端の位置。 if(' + param_value("芽出態")+ ' <= 2){ // 葉が互生や対生の樹は葉面が上を向く傾向。但しやりすぎると皆線状になりおもしろくない。 wh = size * (HKAry[hn]-1)/2 * cosd(turnangle) * cosd(basedrc["h"]); wv = size * (HKAry[hn]-1)/2 * sind(turnangle) * cosd(basedrc["v"]) * 0.5; } else{ // 葉面は枝先を向く。 wh = size * (HKAry[hn]-1)/2 * cosd(turnangle) * cosd(basedrc["h"]); wv = size * (HKAry[hn]-1)/2 * sind(turnangle) * cosd(basedrc["v"]); } // 線の寸法が小さいと何も描かないブラウザあり。0.1あれば問題ないようだ。 if(Math.abs(wh) < 0.1){ wh = wh < 0 ? -0.1 : 0.1; } if(Math.abs(wv) < 0.1){ wv = wv < 0 ? -0.1 : 0.1; } h1pos["x"] = hcpos["x"] - wh; h2pos["x"] = hcpos["x"] + wh; h1pos["y"] = hcpos["y"] + wv; h2pos["y"] = hcpos["y"] - wv; CTX.beginPath(); CTX.moveTo(h1pos["x"], ('+SCRN_Height+'-h1pos["y"])); CTX.lineTo(h2pos["x"], ('+SCRN_Height+'-h2pos["y"])); CTX.closePath(); CTX.strokeStyle = GSC_MainColor[G_Num]; CTX.fillStyle = GSC_SubColor[G_Num]; CTX.globalAlpha = GSC_MainOpacity[G_Num]; CTX.stroke(); } } // 元枝からturnangle方向に分枝角forkangleで分枝した枝の角度をnewdrcに返す。 // これはCALCのコードと全く同じものをjavascriptに登録。 function growdirection(basedrc, turnangle, forkangle, newdrc){ // 三次元座標を求める。 var bdh = basedrc["h"], bdv = basedrc["v"]; var x, y, z; // x:左右(右+)、y:上下(上+)、z:前後(手前+)。 x = cosd(forkangle)*sind(bdh)*cosd(bdv) + sind(forkangle)*sind(turnangle)*cosd(bdh) + sind(forkangle)*cosd(turnangle)*sind(bdh)*sind(bdv); y = cosd(forkangle)*sind(bdv) - sind(forkangle)*cosd(turnangle)*cosd(bdv); z = cosd(forkangle)*cosd(bdh)*cosd(bdv) - sind(forkangle)*sind(turnangle)*sind(bdh) + sind(forkangle)*cosd(turnangle)*cosd(bdh)*sind(bdv); // 角度に戻す。0割と計算誤差による範囲外を回避する必要がある。 if(abs(x) < 0.001){ if(z >= 0){ newdrc["h"] = 0; }else{ newdrc["h"] = 180; } } else if(x > 0){ newdrc["h"] = 90-atand(z/x); } else{ newdrc["h"] = -90-atand(z/x); } if(y >= 1){ newdrc["v"] = 90; }else if(y <= -1){ newdrc["v"] = -90; } else{ newdrc["v"] = asind(y); } } '); } // 元枝からturnangle方向に分枝角forkangleで分枝した枝の角度をnewdrcに返す。 // javascriptで葉っぱを描くコードにも同じものが登録されている。 // いきなり角度で計算するのはとてもわかりにくい。いったん三次元座標を求めて再度角度に直す。 function growdirection(basedrc, turnangle, forkangle, newdrc){ // 三次元座標を求める。 var bdh = basedrc["h"], bdv = basedrc["v"]; var x, y, z; // x:左右(右+)、y:上下(上+)、z:前後(手前+)。 x = cosd(forkangle)*sind(bdh)*cosd(bdv) + sind(forkangle)*sind(turnangle)*cosd(bdh) + sind(forkangle)*cosd(turnangle)*sind(bdh)*sind(bdv); y = cosd(forkangle)*sind(bdv) - sind(forkangle)*cosd(turnangle)*cosd(bdv); z = cosd(forkangle)*cosd(bdh)*cosd(bdv) - sind(forkangle)*sind(turnangle)*sind(bdh) + sind(forkangle)*cosd(turnangle)*cosd(bdh)*sind(bdv); // 角度に戻す。0割と計算誤差による範囲外を回避する必要がある。 if(abs(x) < 0.001){ if(z >= 0){ newdrc["h"] = 0; }else{ newdrc["h"] = 180; } } else if(x > 0){ newdrc["h"] = 90-atand(z/x); } else{ newdrc["h"] = -90-atand(z/x); } if(y >= 1){ newdrc["v"] = 90; }else if(y <= -1){ newdrc["v"] = -90; } else{ newdrc["v"] = asind(y); } } // 木の成長曲線としてゴンペルツ成長曲線(修正)を使う。 // 成木年でほぼ0.7の値になる。成長に応じて0から1+αの間の数値を返す。 function total_grow(age){ // age:現在の樹齢。 // ゴンペルツ式は老木になるとほぼ完全に成長が止まる(動物用?)。 // 老木になっても少しづつ成長するよう基礎成長率を加える。 // 係数はそこそこ妥当そうなとこで決めた。本当は樹種によって違うかもしれないが。 var x = age / AdultAge; return (0.05**exp(-2.0 * x) - 0.05) / 0.95 + 0.05 * x; } // 木の成長割合。成長曲線より樹齢fromageからtoage年間に成長した割合を返す。 function partial_grow(fromage, toage){ return total_grow(toage)-total_grow(fromage); } // パラメータの[標準値, 最小値, 最大値]に乱数による偶然性を加味して数値を決める。 function param_value(pname){ var standard, lower, upper; // おかしな値が指定されたら標準値をそのまま返す。標準値が数値解釈できない場合エラー停止。 if((standard = (float)param[pn[pname]][0]) === error){ if(ptitle[pn[pname]]){ println('\n
'+ptitle[pn[pname]]+'の標準値が数値として解釈できません:'+param[pn[pname]][0]+'
'); } else{ println('\n
標準値が数値として解釈できません:'+param[pn[pname]][0]+'
'); } exit; } if((lower = (float)param[pn[pname]][1]) === error){ return standard; } if((upper = (float)param[pn[pname]][2]) === error){ return standard; } return haply_value(standard, lower, upper, 0.5); } // [標準値, 最小値, 最大値]に乱数による偶然性を加味して数値を決める。 // 標準値はほぼ最も出現確率の高い値。comcentrationは標準値への集中度(>=0)。 function haply_value(standard, lower, upper, concentration=1){ // おかしな値が指定されたら標準値をそのまま返す。 if(lower > upper || concentration < 0){ return standard; } // 標準値が範囲を外れて指定されたら標準値を外れた側の範囲値に修正。 if(standard < lower){ standard = lower; }else if(standard > upper){ standard = upper; } // 中心値0、範囲±1で中心値密度の高い分布をrandomで作成する。 // 正規分布は範囲外の値の処理に難があったので使わない。 var value = (2*random()-1) * random()**concentration; // 最初の1個だけに符号必要。 // 分布の範囲と位置を調整。 if(value < 0){ value *= (standard - lower); } else{ value *= (upper - standard); } value += standard; return value; } // 入力テーブルを作成し入力を配列として得る。得られる配列の要素は文字列。 // clms:項目数、rows:行数、input_aryに答え、ctitle_ary:項目タイトル、ptitle_ary:行タイトル。 // input_aryに値が設定されていれば初期値として入力欄に挿入される。 function table_input(clms, rows, input_ary, ctitle_ary=null, ptitle_ary=null){ var tinum = time(); var printstr, rn, cn, preval; var correct = false, corrected = false; printstr = '
'; for(rn = 0; rn <= rows; rn++){ if(rn == 0 && isarray(ctitle_ary) < 1){ continue; } printstr += '
'; for(cn = 0; cn <= clms; cn++){ if(cn == 0 && isarray(ptitle_ary) < 1){ continue; } if(rn == 0){ if(cn == 1){ printstr += '
'; } else{ printstr += '
'; } if(isarray(ctitle_ary) >= 1 && ctitle_ary[cn]){ printstr += ctitle_ary[cn]; } else{ printstr += ' '; } printstr += '
'; } else if(cn == 0){ printstr += '
'; if(isarray(ptitle_ary) >= 1 && ptitle_ary[rn-1]){ printstr += ptitle_ary[rn-1]; } else{ printstr += ' '; } printstr += '
'; } else if(ptitle_ary[rn-1] == "葉形状" || ptitle_ary[rn-1] == "葉色枝先" || ptitle_ary[rn-1] == "葉色枝元"){ //// 特殊要素。 if(cn == 1){ preval = input_ary[rn-1][cn-1]; printstr += '
'; printstr += '
'; printstr += '
'; } } else{ preval = ""; if(isarray(input_ary[rn-1]) >= 1 && (input_ary[rn-1][cn-1] || input_ary[rn-1][cn-1] === 0)){ preval = input_ary[rn-1][cn-1]; } if(cn == 1){ // 標準値。太字にする。 printstr += '
'; printstr += '
'; printstr += '
'; } else{ // それ以外。 printstr += '
'; printstr += '
'; printstr += '
'; } } } if(rn == 0){ printstr += '
備考'; } else{ printstr += '
'; } if(isarray(pinfo) >= 1 && pinfo[rn-1]){ printstr += pinfo[rn-1]; } printstr += '
'; printstr += '
'; } printstr += '
'; print(printstr); jseval('document.getElementById("IN_1_1").selectionStart = document.getElementById("IN_1_1").value.length;'); jseval('document.getElementById("IN_1_1").selectionEnd = document.getElementById("IN_1_1").value.length;'); jseval('document.getElementById("IN_1_1").focus();'); print(' =>
'); secretinput(''); var value = []; for(rn = 1; rn <= rows; rn++){ if(isarray(input_ary[rn-1]) < 1){ input_ary[rn-1] = new []; } for(cn = 1; cn <= clms; cn++){ if(cn > 1 && (ptitle_ary[rn-1] == "葉形状" || ptitle_ary[rn-1] == "葉色枝先" || ptitle_ary[rn-1] == "葉色枝元")){ continue; } //// 特殊要素。 value[cn] = trim(jseval('document.getElementById("IN_'+rn+'_'+cn+'").value;')); if((float)value[cn] !== error){ // 値が数値なら数値範囲をチェック。 value[cn] = (float)value[cn]; if(cn == 2 && value[cn] > value[1]){ // 下限がはずれたら勝手に修正。 value[cn] = value[1]; correct = corrected = true; } if(cn == 3 && value[cn] < value[1]){ // 上限がはずれたら勝手に修正。 value[cn] = value[1]; correct = corrected = true; } } input_ary[rn-1][cn-1] = (string)value[cn]; jseval('document.getElementById("TD'+tinum+'_'+rn+'_'+cn+'").style.width = document.getElementById("TD'+tinum+'_'+rn+'_'+cn+'").getBoundingClientRect().width+"px";'); if(correct){ jseval('document.getElementById("TD'+tinum+'_'+rn+'_'+cn+'").style.color = "red";'); correct = false; } jseval('document.getElementById("TD'+tinum+'_'+rn+'_'+cn+'").innerHTML = "'+value[cn]+'";'); } } jseval('document.getElementById("INTABL'+tinum+'").style.background = "#F0F0F0";'); jseval('document.getElementById("SUBMIT'+tinum+'").disabled = true;'); jseval('document.getElementById("SUBMIT'+tinum+'").style.cursor = "default";'); if(corrected){ print('
:上限あるいは下限値の外れたものがあったので勝手に修正しちゃいました。
'); } println(); } // 樹木パラメータの定義。樹種名を指定。 function settree(name){ switch(name){ default : param[pn."樹高"] = [10.7, null, null]; param[pn."樹幅"] = [0.4, null, null]; param[pn."樹径"] = [1.0, null, null]; param[pn."芽出態"] = [2, null, null]; param[pn."葉数"] = [8, null, null]; param[pn."葉形状"] = ["1,4,7,9,9,7,5,3,1"]; param[pn."葉寸法"] = [1.0, null, null]; param[pn."葉間隔"] = [0.7, null, null]; param[pn."葉色枝先"] = ["'r':0, 'g':160, 'b':0"]; param[pn."葉色枝元"] = ["'r':0, 'g':120, 'b':0"]; param[pn."若枝分枝度"] = [1.5, null, null]; param[pn."古枝分枝度"] = [1.0, null, null]; param[pn."分枝角"] = [45, null, null]; param[pn."枝開き"] = [2, null, null]; param[pn."枝向き"] = [-2, null, null]; param[pn."下垂性"] = [3, null, null]; param[pn."枝上り"] = [3, null, null]; param[pn."直幹性"] = [5, null, null]; param[pn."成木年"] = [30, null, null]; param[pn."樹齢"] = [68, null, null]; EdaIro = ["r":128, "g": 128, "b":128]; break; // 松の形態は非常に幅広く縦に伸びるもの横に広がるもの等いろいろ。 // 遺伝的要素が強いと思われるが詳しいことはわからない。 case "庭園の松" : param[pn."樹高"] = [0.8, null, null]; param[pn."樹幅"] = [0.35, null, null]; param[pn."樹径"] = [1.2, null, null]; param[pn."芽出態"] = [5, null, null]; param[pn."葉数"] = [15, null, null]; param[pn."葉形状"] = ["1,1,1,1,1,1,1,1,1,1,1,1,1,1,1"]; param[pn."葉寸法"] = [0.6, null, null]; param[pn."葉間隔"] = [0.3, null, null]; param[pn."葉色枝先"] = ["'r':40, 'g':160, 'b':0"]; param[pn."葉色枝元"] = ["'r':20, 'g':120, 'b':0"]; param[pn."若枝分枝度"] = [2.0, null, null]; param[pn."古枝分枝度"] = [1.3, null, null]; param[pn."分枝角"] = [45, null, null]; param[pn."枝開き"] = [3, null, null]; param[pn."枝向き"] = [5, null, null]; param[pn."下垂性"] = [9, null, null]; param[pn."枝上り"] = [3, null, null]; param[pn."直幹性"] = [3, null, null]; param[pn."成木年"] = [30, null, null]; param[pn."樹齢"] = [68, null, null]; EdaIro = ["r":160, "g": 128, "b":128]; break; case "盆栽の松(傘松仕立て)" : param[pn."樹高"] = [0.5, null, null]; param[pn."樹幅"] = [1.0, null, null]; param[pn."樹径"] = [2.0, null, null]; param[pn."芽出態"] = [5, null, null]; param[pn."葉数"] = [7, null, null]; param[pn."葉形状"] = ["1,1,1,1,1,1,1,1,1,1,1,1,1,1,1"]; param[pn."葉寸法"] = [0.6, null, null]; param[pn."葉間隔"] = [0.3, null, null]; param[pn."葉色枝先"] = ["'r':40, 'g':160, 'b':0"]; param[pn."葉色枝元"] = ["'r':20, 'g':120, 'b':0"]; param[pn."若枝分枝度"] = [2.5, null, null]; param[pn."古枝分枝度"] = [2.0, null, null]; param[pn."分枝角"] = [45, null, null]; param[pn."枝開き"] = [0, null, null]; param[pn."枝向き"] = [3, null, null]; param[pn."下垂性"] = [5, null, null]; param[pn."枝上り"] = [3, null, null]; param[pn."直幹性"] = [6, null, null]; param[pn."成木年"] = [15, null, null]; param[pn."樹齢"] = [30, null, null]; EdaIro = ["r":160, "g": 128, "b":128]; break; // 花は葉っぱの色を白に変えてごまかしている。 case "湯島の白梅" : param[pn."樹高"] = [0.6, null, null]; param[pn."樹幅"] = [0.8, null, null]; param[pn."樹径"] = [1.0, null, null]; param[pn."芽出態"] = [0.5, null, null]; param[pn."葉数"] = [5, null, null]; param[pn."葉形状"] = ["5,9,9,9,5"]; param[pn."葉寸法"] = [0.8, null, null]; param[pn."葉間隔"] = [2, null, null]; param[pn."葉色枝先"] = ["'r':255, 'g':240, 'b':240"]; param[pn."葉色枝元"] = ["'r':240, 'g':240, 'b':240"]; param[pn."若枝分枝度"] = [0.8, null, null]; param[pn."古枝分枝度"] = [0.5, null, null]; param[pn."分枝角"] = [45, null, null]; param[pn."枝開き"] = [0, null, null]; param[pn."枝向き"] = [3, null, null]; param[pn."下垂性"] = [3, null, null]; param[pn."枝上り"] = [2, null, null]; param[pn."直幹性"] = [1, null, null]; param[pn."成木年"] = [30, null, null]; param[pn."樹齢"] = [150, null, null]; EdaIro = ["r":128, "g": 128, "b":128]; break; // 花は葉っぱの色をピンクに変えてごまかしている。 case "旧家のしだれ桜" : param[pn."樹高"] = [0.8, null, null]; param[pn."樹幅"] = [1.0, null, null]; param[pn."樹径"] = [1.0, null, null]; param[pn."芽出態"] = [3, 1, 3]; param[pn."葉数"] = [30, null, null]; param[pn."葉形状"] = ["3,5,7,9,9,7,5,3"]; param[pn."葉寸法"] = [0.6, null, null]; param[pn."葉間隔"] = [1.0, null, null]; param[pn."葉色枝先"] = ["'r':255, 'g':200, 'b':200"]; param[pn."葉色枝元"] = ["'r':240, 'g':180, 'b':180"]; param[pn."若枝分枝度"] = [1.8, null, null]; param[pn."古枝分枝度"] = [1.5, null, null]; param[pn."分枝角"] = [45, null, null]; param[pn."枝開き"] = [0, null, null]; param[pn."枝向き"] = [-8, null, null]; param[pn."下垂性"] = [3, null, null]; param[pn."枝上り"] = [4, null, null]; param[pn."直幹性"] = [3, null, null]; param[pn."成木年"] = [50, null, null]; param[pn."樹齢"] = [68, null, null]; EdaIro = ["r":128, "g": 128, "b":128]; break; case "銀座の柳" : param[pn."樹高"] = [0.7, null, null]; param[pn."樹幅"] = [0.4, null, null]; param[pn."樹径"] = [1.0, null, null]; param[pn."芽出態"] = [1, null, null]; param[pn."葉数"] = [20, null, null]; param[pn."葉形状"] = ["1,1,2,2,2,2,2,2,1"]; param[pn."葉寸法"] = [1.0, null, null]; param[pn."葉間隔"] = [0.5, null, null]; param[pn."葉色枝先"] = ["'r':0, 'g':180, 'b':0"]; param[pn."葉色枝元"] = ["'r':0, 'g':140, 'b':0"]; param[pn."若枝分枝度"] = [1.0, null, null]; param[pn."古枝分枝度"] = [0.6, null, null]; param[pn."分枝角"] = [30, null, null]; param[pn."枝開き"] = [0, null, null]; param[pn."枝向き"] = [-10, null, null]; param[pn."下垂性"] = [5, null, null]; param[pn."枝上り"] = [4, null, null]; param[pn."直幹性"] = [5, null, null]; param[pn."成木年"] = [20, null, null]; param[pn."樹齢"] = [68, null, null]; EdaIro = ["r":128, "g": 128, "b":128]; break; case "けやき公園のケヤキ" : param[pn."樹高"] = [0.8, null, null]; param[pn."樹幅"] = [0.5, null, null]; param[pn."樹径"] = [1.0, null, null]; param[pn."芽出態"] = [1, null, null]; param[pn."葉数"] = [8, null, null]; param[pn."葉形状"] = ["1,1,3,5,5,3,2,1"]; param[pn."葉寸法"] = [1.0, null, null]; param[pn."葉間隔"] = [0.4, null, null]; param[pn."葉色枝先"] = ["'r':0, 'g':180, 'b':0"]; param[pn."葉色枝元"] = ["'r':0, 'g':140, 'b':0"]; param[pn."若枝分枝度"] = [1.1, null, null]; param[pn."古枝分枝度"] = [0.8, null, null]; param[pn."分枝角"] = [45, null, null]; param[pn."枝開き"] = [-4, null, null]; param[pn."枝向き"] = [-4, null, null]; param[pn."下垂性"] = [5, null, null]; param[pn."枝上り"] = [2, null, null]; param[pn."直幹性"] = [8, null, null]; param[pn."成木年"] = [30, null, null]; param[pn."樹齢"] = [68, null, null]; EdaIro = ["r":128, "g": 128, "b":128]; break; case "冬枯れのケヤキ" : param[pn."樹高"] = [0.8, null, null]; param[pn."樹幅"] = [0.5, null, null]; param[pn."樹径"] = [1.0, null, null]; param[pn."芽出態"] = [1, null, null]; param[pn."葉数"] = [8, null, null]; param[pn."葉形状"] = ["1,1,3,5,5,3,2,1"]; param[pn."葉寸法"] = [-1.0, null, null]; param[pn."葉間隔"] = [0.4, null, null]; param[pn."葉色枝先"] = ["'r':0, 'g':180, 'b':0"]; param[pn."葉色枝元"] = ["'r':0, 'g':140, 'b':0"]; param[pn."若枝分枝度"] = [1.1, null, null]; param[pn."古枝分枝度"] = [0.8, null, null]; param[pn."分枝角"] = [45, null, null]; param[pn."枝開き"] = [-4, null, null]; param[pn."枝向き"] = [4, null, null]; param[pn."下垂性"] = [5, null, null]; param[pn."枝上り"] = [2, null, null]; param[pn."直幹性"] = [8, null, null]; param[pn."成木年"] = [30, null, null]; param[pn."樹齢"] = [68, null, null]; EdaIro = ["r":128, "g": 128, "b":128]; break; // 花は葉っぱの色を赤に変えてごまかしている。 case "山寺のもみぢ" : param[pn."樹高"] = [0.7, null, null]; param[pn."樹幅"] = [1.0, null, null]; param[pn."樹径"] = [1.0, null, null]; param[pn."葉形状"] = ["1,1,7,9,9,7"]; param[pn."芽出態"] = [2, null, null]; param[pn."葉数"] = [12, null, null]; param[pn."葉寸法"] = [1.0, null, null]; param[pn."葉間隔"] = [1.0, null, null]; param[pn."葉色枝先"] = ["'r':255, 'g':40, 'b':20"]; param[pn."葉色枝元"] = ["'r':200, 'g':130, 'b':20"]; param[pn."若枝分枝度"] = [1.5, null, null]; param[pn."古枝分枝度"] = [1.2, null, null]; param[pn."分枝角"] = [45, null, null]; param[pn."枝開き"] = [2, null, null]; param[pn."枝向き"] = [-3, null, null]; param[pn."下垂性"] = [3, null, null]; param[pn."枝上り"] = [4, null, null]; param[pn."直幹性"] = [0, null, null]; param[pn."成木年"] = [30, null, null]; param[pn."樹齢"] = [100, null, null]; EdaIro = ["r":128, "g": 128, "b":128]; break; case "峠の一本杉" : param[pn."樹高"] = [0.8, null, null]; param[pn."樹幅"] = [0.2, null, null]; param[pn."樹径"] = [1.0, null, null]; param[pn."芽出態"] = [5, null, null]; param[pn."葉数"] = [8, null, null]; param[pn."葉形状"] = ["3,3,3,3,2,2,1,1"]; param[pn."葉寸法"] = [1.0, null, null]; param[pn."葉間隔"] = [0.6, null, null]; param[pn."葉色枝先"] = ["'r':0, 'g':160, 'b':0"]; param[pn."葉色枝元"] = ["'r':0, 'g':100, 'b':0"]; param[pn."若枝分枝度"] = [3.0, null, null]; param[pn."古枝分枝度"] = [2.5, null, null]; param[pn."分枝角"] = [30, null, null]; param[pn."枝開き"] = [3, null, null]; param[pn."枝向き"] = [1, null, null]; param[pn."下垂性"] = [9, null, null]; param[pn."枝上り"] = [2, null, null]; param[pn."直幹性"] = [9.8, null, null]; param[pn."成木年"] = [50, null, null]; param[pn."樹齢"] = [100, null, null]; EdaIro = ["r":128, "g": 128, "b":128]; break; // 花は葉っぱの色をピンクに変えてごまかしている。 case "花水木" : param[pn."樹高"] = [0.7, null, null]; param[pn."樹幅"] = [0.6, null, null]; param[pn."樹径"] = [1.0, null, null]; param[pn."芽出態"] = [4, null, null]; param[pn."葉数"] = [4, null, null]; param[pn."葉形状"] = ["1,4,7,9,9,7,4"]; param[pn."葉寸法"] = [0.8, null, null]; param[pn."葉間隔"] = [1.0, null, null]; param[pn."葉色枝先"] = ["'r':240, 'g':120, 'b':160"]; param[pn."葉色枝元"] = ["'r':240, 'g':120, 'b':120"]; param[pn."若枝分枝度"] = [3.0, null, null]; param[pn."古枝分枝度"] = [0.8, null, null]; param[pn."分枝角"] = [45, null, null]; param[pn."枝開き"] = [0, null, null]; param[pn."枝向き"] = [10, null, null]; param[pn."下垂性"] = [7, null, null]; param[pn."枝上り"] = [4, null, null]; param[pn."直幹性"] = [3, null, null]; param[pn."成木年"] = [10, null, null]; param[pn."樹齢"] = [30, null, null]; EdaIro = ["r":128, "g": 128, "b":128]; break; } } } // End of function TREE(){} # # # # ################################################ # CALCプログラム 18: 乱駄無 ################################################ # 永遠にいろんな図形を描き続けます。名づけて「乱堕無」。 # そうは言っても描くのはスクリーンの中だけで、図形や色が # パソコンの外にはみ出ることはありませんのでご安心ください。 # 何の役に立つのかと言えば、まったく何の役にも立ちません。 # これほどしょうもないプログラムもめったにないという見本です。 # CONSOLEをなるべく広げてお楽しみください。 function RANDOMART() { var c1, c2, o1, o2, lw; // 図形の色、透明度、線幅など。 var x, y, w, h, rx, ry; // 図形の位置や高さ、幅、半径など。 // 描画スクリーンの幅と高さ。スクロールバーの幅など無視で画面いっぱいに。 var screenwidth = consolewidth()+20, screenheight = consoleheight()+20; clear(); // 画面を消去。 screenstyle("border:2px solid silver;"); // 外にはみ出ないとは思うけど念のためスクリーンに囲いをつける。 newscreen(screenwidth,screenheight); // 描画スクリーンを作成。 screenmouseall("'乱堕無'"); // スクリーンタイトル。スクリーン上のマウスに表示。 font("normal", "normal", "sans-serif"); // フォントを指定。 var job; while(1){ wait(500); // めまぐるしくない程度のスピードで。 // 古い模様をすこしずつ消していく。わからない程度のスピードで。 pen(0); color("rgba(255,255,255,0.02)", "rgba(255,255,255,0.02)"); c_rect(0,0,screenwidth,screenheight); // ランダムに図形を描く。 job = 100.0*random(); // 図形出現のバランスをとる。ランダムでもバランスは大事。 if(job < 0.5){ drawclear(); } // 時折クリアしたりして。 else if(job < 20){ drawrect(); } // 四角。 else if(job < 30){ drawcircle(); } // まる。 else if(job < 40){ drawellipse(); } // 楕円。 else if(job < 50){ drawpoint(); } // 点。大きさによっては四角になるかも。 else if(job < 55){ drawpolyline(); } // 折れ線。 else if(job < 60){ drawpolygon(); } // 多角形。 else if(job < 70){ drawcurve(); } // 曲線。 else if(job < 90){ drawmoji(); } // 文字。 else{drawEMOJI();} // 絵文字。 } exit; function drawclear(){ // スクリーンをクリアする。 color("white","white"); pen(0); c_rect(0,0,screenwidth,screenheight); } function drawrect(){ // 四角形。 color(rndcolor(),rndcolor()); rndpen(); // 四角を描く。あまり大きくすると画面が塗りつぶされてしまいおもしろくないので調整。以下同様。 c_rect(rndxpos(),rndypos(),(rndwidth()+rndheight())/5,(rndwidth()+rndheight())/5); } function drawcircle(){ // 円。 color(rndcolor(),rndcolor()); rndpen(); c_circle(rndxpos(),rndypos(),(rndwidth()+rndheight())/10); } function drawellipse(){ // 楕円。 color(rndcolor(),rndcolor()); rndpen(); c_ellipse(rndxpos(),rndypos(),(rndwidth()+rndheight())/10,(rndwidth()+rndheight())/10); } function drawpoint(){ // 点を打つ。大きさによっては四角に見える。 color(rndcolor(),rndcolor()); rndpen(); c_point(rndxpos(),rndypos(),(rndwidth()+rndheight())/10,(rndwidth()+rndheight())/10); } function drawpolyline(){ // 折れ線。 color(rndcolor(),'transparent'); rndpen(); c_path(); c_moveto(rndxpos(),rndypos()); var turns = randomint(1,5); while(turns-- > 0){ c_lineto(rndxpos(),rndypos()); } c_drawpath(); } function drawpolygon(){ // 多角形。 color(rndcolor(),rndcolor()); rndpen(); c_path(); c_moveto(rndxpos(),rndypos()); var turns = randomint(2,5); while(turns-- > 0){ c_lineto(rndxpos(),rndypos()); } c_closepath(); c_drawpath(); } function drawcurve(){ // 曲線。 color(rndcolor(),'transparent'); rndpen(); c_path(); c_moveto(rndxpos(),rndypos()); var turns = randomint(2,5); while(turns-- > 0){ c_curveto(rndxpos(),rndypos()); } c_drawpath(); } function drawmoji(){ // 文字を書く。 // 表示する文字。日本語の文字も入れよう。 static moji = "乱堕無無役単遊興♨㊙㊗" +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +"01234567890123456789一二三四五六七八九十" +"#$%&-=^~|@{+*}?/@!#&%$=≡^~|+-*×/÷?∞§〒♪†∫※‥∴…♂♀±◎●◆★↑↓←→〆" +"とりなくこゑすゆめさませみよあけわたるひんかしをそらいろはえておきつへにほふねむれゐぬもやのうち" +"いろはにほへとちりぬるをわかよたれそつねならむうゐのおくやまけふこえてあさきゆめみしゑひもせすん" +"イロハニホヘトチリヌルヲワカヨタレソツネナラムウヰノオクヤマケフコエテアサキユメミシヱヒモセスン" +"以呂波仁保屁止知利奴留遠和加与太礼曽川称奈良武宇為乃於久也末計不古衣天安左畿由女美之恵比毛世寸旡" // あとは適当に漢字など。 +"暗中模索意味深長有為転変栄枯盛衰温故知新花鳥風月起承転結空即是色敬天愛人極楽往生山紫水明酒池肉林" +"酔生夢死誠心誠意相思相愛他力本願沈思黙考津津浦浦天地神明当意即妙難行苦行日進月歩囊中之霧博学多才" +"品行方正不言実行変幻自在暴飲暴食満身創痍未来永劫無為無策滅私奉公門外不出薬籠中物油断大敵妖怪変化" +"落花流水離合集散縷縷綿綿霊魂不滅六根清浄和気藹藹森羅万象千変万化時節到来一念発起無病息災蛍雪之功" +"愛憎大小天国地獄巨乳腰尻蝕快感神仏色香山海珍味酩酊不覚曼荼羅模様今宵河豚食当選確実柿喰鐘鳴法隆寺" +"上人思想中人作品下人金残我無残鉄拳一喝得悟真心頭滅却煩悩退散言不多実行雌雄合体子孫繁栄摩羅不思議" +"滑稽薮蛇風邪退散健康睡眠朝起大事菓子折持参一服茶苦手数学怠惰蔓延色即是空天人五衰神出鬼没意味不明" ; static mojilength = strlen(moji); color(rndcolor(),rndcolor()); rndpen(); c_text(rndwidth(),rndheight(),rndmoji(),rndheight()/4); function rndmoji(){ var pos; if(randomint(1,2) == 2){ return moji[pos=randomint(0,mojilength-2)]+moji[pos+1]; } else{ return moji[randomint(0,mojilength-1)]; } } } function drawEMOJI(){ // 絵文字は文字のようにはいかない。文字列中の絵文字は簡単な操作では取り出せない。 // したがって絵文字は文字列にしないで一文字ずつ。これでうまくいく。 static EMOJI = array( "👀","☔","🍵","☕","🍷","🍜","🍦","🍓","🍒","🍊","⚽","⛅","⛄","⛔","➰","🎁", "⏳","⏰","🎼","💢","💯","🕙","🎀","🗿","🚒","🚶","🏃","🌱","🌿","🗻","🌋","🎐", "🎯","🐜","💀","👽","💩","👻","🔥","💥","🎈","🌻","🎋","🐝","💮","🐑","🐍","🐼", "🐞","🌷","💃","🐣","🐙","🎌","🍺","🌸","🌵","🈲","🎊","🎃","🐢","🎴","🍄","🐟", "🍡","👹","👺","🚫","🐌","🐳","🐬","🎏","🍶","💔","🔰","🚓","🐦","💣","🚧","🐧", "🏆","🍎","🍏","👯","🐲","💐","👿","🍭","🙏","👾","🐎","🍮","🎎","🍢","🌴","🎂", "👄","🍸","🎍","🍹","🍇","🍈","🍉","🍍","🌰","🔧","🍚","🗼","👫","🏁","🎶","👌", "🏯","🍻","💤","🚑","🎲","💌","🐛","🍀","🚀","🌅","👸","🉑","🙋","🐠","🐡","🏮", "🔞","🏡","🎡","🍣","⛵","❌","🌼","🌷","🌹","🐤","🐥","🙆","👶","🐶","🙏","🐗", "🏯","🍂","🍙","🍁","😳","✨","⚡","💕","🐝","🌺","🍆","🌏","🗾","🚥","🐒","🔑"); static EMOJIlength = arylen(EMOJI); // 配列の要素数を返す。 color(rndcolor(),rndcolor()); rndpen(); c_text(rndwidth(),rndheight(),rndEMOJI(),rndheight()/4); function rndEMOJI(){ return EMOJI[randomint(0, EMOJIlength-1)]; } } function rndcolor(){ var r = randomint(0,255); var g = randomint(0,255); var b = randomint(0,255); var a = random(); var rgb = "rgb(" + r+ "," +g+ "," +b+ ")"; var rgba = "rgba(" + r+ "," +g+ "," +b+ "," +a+ ")"; return (random() < 0.5 ? rgb : rgba); } function rndpen(){ var width = randomint(1,min(screenwidth,screenheight)/20); // あまり大きくすると線が四角形になってしまいおもしろくない。 var linecap = ["butt","round","square"][(int)(3*random())]; // 線の端の形状。 var linejoin = ["miter","round","bevel"][(int)(3*random())]; // 折線の繋ぎの形状。 pen(width,linecap,linejoin); } function rndxpos(){ return floor(screenwidth*(1.2*random()-0.1)); // 少し画面外にはみだした形にしないとつまらない。 } function rndypos(){ return floor(screenheight*(1.2*random()-0.1)); } function rndwidth(){ return randomint(1,screenwidth); } function rndheight(){ return randomint(1,screenheight); } function randomint(minimum, maximum){ return floor(minimum + (maximum-minimum+1) * random()); } } // End of function RANDOMART(){} # # # # ################################################ # CALCプログラム : プログラムセレクタ ################################################ # 各種プログラムを選んで実行します。 # [実行]ボタンをクリックして実行したいプログラムを選んでください。 CALC_logo(); // やっぱりまずこれをやらないと... println("実行するプログラムの番号を入力するか、あるいはプログラムタイトルをクリックしてください。"); println("時折[リセット]してメモリを開放し
軽く
してやってください。でないとスピードが落ちてきます。"); //// print(""); //頭でやった。 println("
0: 終了
"); println("
1: 式の計算 :実数および複素数での計算を行います。
"); println("
2: 多桁高精度計算 :多桁実数の高精度計算を行います。
"); println("
3: 分数計算 :分数を分数式のまま四則演算を行います。複素数も使えるみたいです。
"); println("
4: 二次方程式を解く:二次方程式を式または係数を入力して解きます。
"); println("
5: 連立方程式を解く:多元一次連立方程式を解きます。
"); println("
6: 階乗の計算 :指定の範囲の数の階乗を計算し表示します。
"); println("
7: 素因数分解 :指定数の正の整数を素因数分解します。
"); println("
8: 素数の表示 :指定の正の整数の範囲で素数を求めて表示します。
"); println("
9: 最大公約数 :2つの正整数の最大公約数を求めます。
"); println("
10: 級数の和 :指定の範囲で指定の級数の和を求めます。
"); println("
11: 数値積分 :指定関数の指定範囲での定積分を計算します。
"); println("
12: 面積計算 :単純な各種図形の面積、できれば周囲長を計算します。
"); println("
13: 単位換算 :各種の単位の換算をします。
"); println("
14: カレンダーの作成:指定西暦年の曜日カレンダーを作ります。
"); println("
15: 関数グラフ :関数y=f(x)のグラフを描きます。
"); println("
16: マンデルブロ集合:かの有名なマンデルブロ集合をグラフィック画面に描きます。
"); println("
17: マンデルアート展:上記マンデルブロ集合の見どころを額縁に入れて展示してみました。
"); println("
18: 樹を描く :成長パラメータと乱数を使って樹を育てます。
"); println("
19: 乱駄無 :延々と乱れ図形を描き続けるだけの意味不明で無駄なプログラムの見本です。
"); var jobnumber = (int)input("実行プログラムの番号:"); if(!jobnumber){ exit "終了します。"; } else if(jobnumber == 1){ COMPLEX_CALC(); } else if(jobnumber == 2){ LONG_CALC(); } else if(jobnumber == 3){ BUNSUU(); } else if(jobnumber == 4){ EQUATION2(); } else if(jobnumber == 5){ RENHOU(); } else if(jobnumber == 6){ FACTORIAL(); } else if(jobnumber == 7){ FACTORIZE(); } else if(jobnumber == 8){ SOSUU(); } else if(jobnumber == 9){ SAIKOU(); } else if(jobnumber == 10){ SOUWA(); } else if(jobnumber == 11){ SEKIBUN(); } else if(jobnumber == 12){ AREA(); } else if(jobnumber == 13){ UNITCONVERT(); } else if(jobnumber == 14){ CALENDER(); } else if(jobnumber == 15){ FUNCTIONGRAPH(); } else if(jobnumber == 16){ MANDERBROT(); } else if(jobnumber == 17){ MANDERARTS(); } else if(jobnumber == 18){ TREE(); } else if(jobnumber == 19){ RANDOMART(); } else{ exit("該当のプログラムがありません。終了します。"); }
計算用紙 "CalcPad" V1.5
{ご案内}
CALC PROGRAM
プログラム
/
UTF8
SJIS
EUCJP
JIS7
-
/
-
CALC CONSOLE
グラフィック
1
初期化中です....
Simple Calculation Program "CalcPad" V1.5 (
http://kuragane.jp/calcpad.htm
)
CalcPad Information
CalcPadのご案内
◆ 概要 ◆
「計算用紙 CalcPad」(以下CALCと略)は気軽な計算などに軽く使えるよう作成したC言語類似のごく簡単なインタプリタです。
数値は複素数が使えるように拡張してあり、さらに変数、配列、関数、基礎的な制御構造なども使えます。またグラフィック機能もありSVGとcanvasを使って簡単なグラフや絵などを描くことができます。
CALCでは初期の構造化言語の基本機能はほぼ実装しており、プログラムを書いてすぐにその場で実行できますので、ちょっとした仕事の計算、遊びやプログラミングの勉強などには充分使えます。
以下、言語の概略説明です。
◆ 数値 ◆
数値は整数、浮動小数点数、
複素数
(虚数単位として"
i
"または"
j
")が使えます。整数はさらに通常の10進数表記のほかに2進数(例:%1101)、8進数(例:036)、16進数(例:0x3F)などの表記ができます。
◆ 常数 ◆
常数として
p
,
PI
:円周率 通称パイ(3.14159...)、
e
:自然対数の底(2.71828...)、
true
:真偽値の真、
false
:真偽値の偽、
null
:値なし、
error
:一部の演算子や関数がエラー通知に返す値、
void
:無効な(使えない)値(未定義、不定、ゴミ値など)、
undefined
:定義されていない値 が組み込まれています。
◆ 文字列 ◆
文字列は
''
または
""
で囲って表現します。文字列とコメントは内部に日本語を使うことができます。特殊文字として、
\(改行)
:改行打ち消し、
\\
:\そのもの、
\0
:ヌル文字、
\b
:バックスペース、
\t
:タブ、
\n
:改行、
\f
:フォームフィード、
\r
:復帰、
\'
:シングルクオート、
\"
:ダブルクオート、
\xhh
:16進コードhhの文字 が定義されています。
◆ 変数 ◆
変数の宣言の書式は キーワード 変数名[=値], ... ; の形で"=値"はconst宣言およびstatic宣言以外ではなくてもかまいません。
変数の値の型は内容に応じて自動的に設定されます。変数宣言は通常初期化時(static変数やconst常数)あるいは実行時(var変数)に登録されますが、実際に値が代入されて有効になります。
変数名に使用できる文字はアルファベット、数字、アンダスコア("_")ですが、頭はアルファベットまたは"_"で数字は除かれます。
変数名には制御用の語句(ifとかforとかいわゆる予約語)、その他組込の常数名や関数名などは使用できません。また最終的にはjavascriptで処理する場合もありえますのでjavascriptで意味をもつ語も使わないほうが安全です。
変数のスコープは、実行側(function宣言された)関数の引数や関数内でのみ使われる変数および関数内でglobal以外で宣言された変数は関数内ローカル変数となり、通常その関数内とその関数内で定義された関数内でのみ参照できます。それ以外の変数の検索は関数内から順次上位の関数、トップレベルへと行われます。
変数宣言は 宣言キーワード 変数名[=値], ... ; の形で以下のキーワードが使えます。
●
global
: 関数内からグローバル変数を参照します。globalに加えvarやconstなどの宣言をするか"="で値を指定すればグローバル領域に変数がなければ作られますが、関数内からグローバル変数をつくった場合その所有権は関数を離れエラー検知や制御等が充分できません。関数を再定義したり復数の関数で同じ変数を宣言したりするとバグの原因になりやすいです。
●
static
: 静的変数を宣言します。globalをつければ広域、なしで関数内なら局所変数になります。初期値の設定は変数の作成時(通常プログラム初期化時)に一回だけ行われます。すなわち同じ変数名のstatic変数の初期化を複数書いても有効な初期化は一つだけです。宣言時に"="で有効な値を指定する必要があります。いったん作成されたstatic変数はvarで再宣言するとか、delete()で関数ごとそっくり、あるいは個々に消去される場合を除き、関数が再実行されたりあるいは再定義されたり再初期化されても変数と値はそのまま残ります。
●
const
: 常数の作成を宣言します。必ず宣言時に"="で有効な値を指定してください。以後var等で再宣言しない限り値の変更はできません。
●
var
: 型可変型変数の作成を宣言します。なくても新しい変数名に値を代入すれば変数が作られますが最初に書いておくとわかりやすいですし、うっかりすでにあるグローバル変数と混同してしまう等のバグが避けられます。
その他キーワードとしてint, float, complex, string, booleanが使えますがこれらは現在はvarと同じ扱いになります(将来的には型指定型変数を導入しようなどと当初は考えていましたが...)。覚えの意味で使うなら問題ありません。
これらの宣言の属性は変数が新たな変数に代入されても必ずしも継承はされません。
変数の宣言についてはjavascript同様の宣言巻き上げを設定してありますので変数を使うところで都度宣言しても良いのですが、たよりにするとバグのもとになるかもしれません。変数宣言はなるべく関数の最初にやるようにしましょう。
関数の内部変数は実は関数実行終了後もそのまま残っており、メンバーアクセス演算子"
.
"を使って fncname.varname の形でアクセスできます。
また宣言は組み合わせて使うこともできますが、関数の内外や宣言場所含めてすべての組み合わせについて確実に確認したわけでもありません。
◆ 配列 ◆
配列も定義できます。書式は、配列名 = [値1, 値2, ...]; もしくは、配列名 =
array
(値1, 値2, ...);で、キーは指定しなければ0から初期化されます。ary=[[1,2,..],[3,4,..],..];などとして配列の中に配列を入れるのも可能です。[]内または()内は別途キー(必要なら)と値を定義するなら空でもかまいません。値のところは キー
=>
値, あるいは キー
:
値, としてキーと値を指定することも可能です。キーは0か正の整数、もしくは"."を含まない文字列(できるだけ名前文字に準じる)です。
配列要素の追加あるいは変更は 配列名[キー(空なら自動)] = 値; で行います。配列や配列要素の削除にはdelete()をお使いください。配列名はC言語と同じく常に参照値であり、オブジェクトとするjavascriptとは少し異なります。すなわち配列を別の変数に 代入演算子"
=
" で代入した場合や関数の引数として渡した場合、値のコピーではなく配列への参照値が渡されますので受け渡し先で要素を書き換えると元の配列が書き換わります。そうしたくない場合はなるべく最後のほうで "
copy
"を使って参照を解除してください。配列を再定義した場合は前の配列は自動的に消去されます。
◆ 関数 ◆
function
f(...){...}の形で関数を定義できます。定義には必ずfunction宣言が必要で()や{}は省略できません。実行はf(...)の形で行います。
関数については最後の方に少し詳しく書いてありますのでそれを参照してください。
◆ 制御構造 ◆
単純な文(式;)のほかに、制御構造(制御文)として if-else, for、foreach, while, do-while, switch-case, break, continue, return, exit および複合文{ } が使えます。使い方はC言語より派生した多くの言語と同じですので、ごくごく簡単な説明にとどめます。{ 実行式;... }の部分はswitch-caseを除き実行式;が1つだけならば無理に{}はいりませんが書いておいた方がわかりやすいです。
if
(条件式){ 実行式1;... }
else
{ 実行式2;... }
……
条件式が真のとき{ 実行式1;... }、偽のとき{ 実行式2;... }を実行します。条件式は省略できません。elseに続いてifを記述するときは
else if
と区切ってください(elseifとかelifというのは使えません)。
for
(初期化式; 条件式; 再初期化式){ 実行式;... }
……
最初初期化式を実行、続いて条件式が真なら{ 実行式;... }を実行、再初期化式を実行後再度条件式を評価し真なら再び{ 実行式;... }を実行、偽ならforは終わりとなります。forの初期化式、条件式、再初期化式は省略でき、条件式を省略した場合条件は真とみなされます。
foreach
(ary
as
[id
=>
] val){ 実行式;... }
……
配列aryの要素を順次valに取り出します(php方式です)。順番は制御できません。id => があればidに配列のインデックス(文字列)も取り出します。id, valは変数名であれば他の名前でもかまいません。配列の中の配列の値までは取り出しません。
while
(条件式){ 実行式;... }
……
条件式が真なら{ 実行式;... }を実行、偽ならwhileは終わりとなります。whileの条件式は省略できません。
do
{ 実行式;... }
while
(条件式);
……
無条件に一度{ 実行式;... }を実行、その後whileのあとの条件式を評価して真なら再度{ 実行式;... }を実行、偽ならdo-whileは終わりとなります。do-whileの条件式は省略できません。最後のwhile();の";"をお忘れなく。
switch
(比較値){
case
値1: 式1;...;
case
値2: 式2;...;
default
: 式d;...; };
……
比較値を各caseのあとの値と比較し同値ならそのあとの式;から(最後まで)実行します。合う値がなければdefault:のあとから(最後まで)実行します。一部だけ実行するならbreak;で流れを止めます。switch文の比較値と{}は省略できません。
break
;
……
for, foreach, while, do-while, switch-caseで途中で処理を止めコントロールを抜けるのに使用します。
continue
;
……
for, foreach, while, do-whileの途中で以後の処理を止め、再判定して真なら再度ループを実行します。
return
式;
……
関数内ならその関数を、そうでないならCALCを終了し式の値を返します。値を使わないなら式はなくてもかまいません。
exit
式;
……
全体(CALC)の処理を終了(停止)し式の値を返します。式はなくてもかまいません。
{
式; 式; ...
}
……
復数の文をまとめいわゆる複合文を形成します。あるいは制御構造や関数などの適合範囲を規定します。
これらの文や制御構造についてはC言語、php、javascriptなどとほとんど同じですので、概略おわかりになると思います。
◆ 演算子 ◆
以下の演算子が使えます。優先度は数字の大きいほど(下のものほど)高くなります。こちらもC言語、php、javascriptなどとほとんど同じですので、詳しくはそちらの参考書などをご覧ください。
優先度
演算子
書式
結合性
説明
0
式の区切り
;
左→
必ずしも演算子ではありませんが、式を区切っていわゆる文を形成します。式は空でもかまいません。CALCでは単純な文や制御文も値(概ね最後の実行結果)を返します(もともと式や文を書いて最後の結果を表示する計算用紙の動作を踏襲したため)。それでその値を式で使えないこともありませんが、その場合
必ず文や制御構造をそっくり()で囲んで式の中に入れてください
。()で囲わずそのまま入れますと式の処理が途中で中断されたりエラーになったり、場合によってはエラーにならずに式の結果がおかしな値になったりします。
1
順次演算子
,
左→
左から右へ順次式を評価し、最後の式の値をその値とします。空(",,"や",;"など)は許されません。関数などの引数の区切りの","とはちがいます。
2
代入演算子
(単純代入)
=
←右
変数(左辺値)に右辺値を代入します。数値及び文字列、論理値についてはその値が代入され、配列については配列への参照値が代入されます。配列の値そのものを代入したいなら 変数 = copy 配列名; とします。また関数名を変数に代入した場合
実行コードのみが代入され
、内部変数等は実行時に変数名所有で新しく作られます(同様に内部変数もコピーしたいなら 変数 = copy 関数名; とします)。
代入演算子
(演算代入)
+=
,
*=
,
/=
,
%=
,
**=
,
|=
,
^=
,
&=
,
<<=
,
>>=
,
>>>=
←右
変数(左辺値)に左辺値を右辺値で演算した結果を代入します。演算代入の場合演算によっては値は整数型でなくてはならないなどの制限がありますが、これについては該当する演算を参考にしてください。"+="では少なくとも片方が文字列の場合は文字列連結、それ以外では数値演算されます。
3
三項演算子
(条件演算子)
a
?
b
:
c
←右
aが真ならb, 偽ならcを値とします。三項演算子の中にさらに三項演算子を入れる場合は極力()を使ってください。言語によって結合性が異なります。CALCでは a ? b : c ? d : f はC言語やJava,javascript等と同じく a ? b : (c ? d : f) として処理されます。
4
論理和
||
左→
いずれかが真の値ならtrueを、すべて偽の値ならfalseを値とします。偽の値とは数値の0, 空文字列"", 空の配列[], 論理値のfalse, その他null, undefined(未定義), void, errorです。
5
論理積
&&
左→
すべて真の値ならtrueを、いずれかが偽の値ならfalseを値とします。
6
ビットOR
|
左→
ビットごとの論理和(少なくとも一方が1なら1、両方0なら0)をそのビットの値とします。整数にのみ適用できる演算です。
7
ビットXOR
^
左→
ビットごとの排他的論理和(一方だけが1なら1、両方0か1なら0)をそのビットの値とします。整数にのみ適用できる演算です。
8
ビットAND
&
左→
ビットごとの論理積(両方1なら1、少なくとも一方が0なら0)をそのビットの値とします。整数にのみ適用できる演算です。
9
等値比較
==
,
!=
,
===
,
!==
左→
等しいか否かの比較演算を行い結果としてtrueまたはfalseを返します。===, !==は型が等しいか否かの判定も含めますが数値同士なら型は同じとみなされます。たとえば 0 === 0.0 は数値型が違っても型は同じとみなされ値が同じなので真、型比較をしない 0 == false はともに偽の値なので結果は真、 型比較をする 0 === false は型が違うので偽となります。その他型が違う場合の比較は実際やってみて確認してください("0"==false、"1"==1 など)。
10
大小比較
>
,
<
,
>=
,
<=
なし
大小の比較演算を行い結果としてtrueまたはfalseを返します。=がある場合は同値も含めます。実数のみに適用でき複素数には適用できません。
11
ビットシフト
<<
,
>>
,
>>>
左→
左辺の整数を右辺の整数分ビット移動します。<<は左、>>は右、>>>は符号なし整数扱いで右シフトします。左辺値は整数、右辺値は0から31の整数です。
12
加減算
+
,
-
左→
算術演算の +:足し算、-:引き算です。それに加え + 演算は文字列の結合に使用します。
右辺か左辺のいずれかが文字列の場合
もう一方も文字列に変換され結合されます。
13
乗除算
*
,
/
,
%
,
*の省略形
左→
算術演算の *:掛け算、/:割り算、%:剰余算です。
加えて純粋な数値表現(変数,関数などと混同しないもの)あるいは括弧"(式)"に虚数単位iまたはjはもちろん、
eやEの指数表現と混同しない
変数名や括弧"(式)"が空白なしで続いたときは乗算記号"*"が省略されたものとみなします
が、数式計算のなごりですのでその点ご承知おきください。
15
符号
+
,
-
←右
-で数値の正負を反転します。+では変わりませんが演算子の+や++と区別しにくくかえってわかりずらくなりますので+はつけない方がいいでしょう。
16
累乗
**
←右
左辺値を右辺値で累乗した結果を返します。
結合性は右結合
(すなわち2**2**3は2**(2**3)で、64ではなく256)で、さらに符号より優先順位が高く-1**2は1ではなく-1です(数学記法に合わせたものです)。またこの演算子は言語によってかなり優先順位がちがうのでその点にもお気をつけください。関数pow()を使うのが確かかもしれません。
14
キャスト
(型変換)
(int)
,
(float)
(complex)
,
(real)
,
(image)
,
(img)
,
(arg)
,
(string)
,
(str)
←右
データの型を変換します。右辺値は数値もしくは純粋に数値を表す文字列です。文字列を数値に変換する場合に限りうまく変換できないときはエラー停止や途中までの変換でOKとはせず常数errorを返します。CALCには複素数に関する変換があり、(complex):複素数、(real):実数部取出((float)と同じ)、(image),(img):虚数部取出、(arg):複素角です。0の複素角は定義されずエラーになります。(image)と(img)、(string)と(str)は同じです。
17
否定
!
←右
右辺値が数値の0、空文字列""、論理値のfalse、null、未定義変数などの偽の値の場合trueを返し、それ以外falseを返します。
前置増減
++
,
--
←右
"++"で変数に1を加算、"--"で1を減算し、その結果の値を評価値とします。演算対象は変数でその内容は数値でなくてはなりません。
ビット反転
~
←右
ビットを反転した値を返します。右辺値は整数でなくてはなりません。
コピー
new
copy
←右
右辺の配列や関数の内容をそっくりコピーしたものを新しく作成します。配列は中に配列への参照があった場合も参照から値に展開されてコピーされます。関数はその内部変数もそっくりコピーされます。関数をコピーするのは主に関数を構造体などの代わりに使う場合です。
コピーされる内容は実行時点での内容で、定義された時点のものではありません
。主に 変数 = new 配列または関数;、変数 = copy 配列または関数; などの代入形式で使い、単独だとメモリーを浪費するだけであまり意味はありません。"new"と"copy"はまったく同じです。状況に応じて意味のわかりやすい方をお使いください。現在動作対象は配列と関数のみで、それ以外の場合は単に右辺の値が返されます。
18
後置増減
++
,
--
左→
変数の値を評価後"++"で変数に1を加算、"--"で1を減算します。演算対象は変数でその内容は数値でなくてはなりません。
関数実行
()
左→
()内の引数で関数を実行します。
メンバーアクセス
[]
,
.
左→
配列やユーザ関数の中のいわゆるメンバーにアクセスします。主に"[]"は配列、"."は関数内のメンバーにアクセスするのに使います。また文字列からの1文字取り出しには"[]"が使えます("."は使えません)。左辺値は配列名や関数名で、"."の右辺値はメンバー名(単に名前)とみなされますが、"[]"の中は値もしくは変数でもかまいません。メンバー名に名前文字以外(記号等)を含む場合は""か''で囲ってください。
19
カッコ
()
なし
一般的な用法として式の優先順位を変更します。それに加えCALCでは
文を式化して文の値(最後の文の実行結果)を値として取り出します
。これにより式の位置にも文を書くことができます。
◆ 関数 ◆
以下の数学関数が使えます。引数と返り値は、int:整数型実数、float:実数(浮動小数点数または整数)、complex:複素数または実数です。
float
abs
(complex)
絶対値。
float
abs2
(complex)
絶対値の2乗。実はこちらが元でabs()はabs2()の平方根。
complex
sqrt
(complex)
平方根。
complex
sin
(complex)
三角関数 正弦、サイン。三角関数の角度の単位はラジアンです。難しいので詳細省略。(以下同様)
complex
cos
(complex)
三角関数 余弦、コサイン。
complex
tan
(complex)
三角関数 正接、タンジェント。
complex
asin
(complex)
逆三角関数 逆正弦、アークサイン。
complex
acos
(complex)
逆三角関数 逆余弦、アークコサイン。
complex
atan
(complex)
逆三角関数 逆正接、アークタンジェント。
float
sind
(float)
float
cosd
(float)
float
tand
(float)
float
asind
(float)
float
acosd
(float)
float
atand
(float)
上記6つの三角関数で角度の単位は度です。引数の数値は実数でなくてはなりません。
complex
sinh
(complex)
双曲線関数 双曲線正弦、ハイパボリックサイン。
complex
cosh
(complex)
双曲線関数 双曲線余弦、ハイパボリックコサイン。
complex
tanh
(complex)
双曲線関数 双曲線正接、ハイパボリックタンジェント。
complex
log
(complex)
自然対数。
complex
log10
(complex)
10を底とする対数。
complex
exp
(complex)
自然対数の底(e)を底とする指数関数。
complex
exp10
(complex)
10を底とする指数関数。
complex
pow
(complex, complex)
累乗。第一引数を第二引数で累乗します。演算子**と同じ結果を返します。
float
real
(complex)
複素数の実数部。
float
image
(complex)
複素数の虚数部。
float
arg
(complex)
複素数の複素角。単位はラジアン。
float
factorial
(int)
階乗。nの階乗(数学記号n!)はn*(n-1)*(n-2)*...*1。引数は0以上170以下の整数。返り値は整数または実数。
float
min
(float,...)
最小値。引数は1個以上何個でも。実数のみ。複素数にはなぜか使えません。
float
max
(float,...)
最大値。同上。
float
random
()
乱数。0.0から1.0までの間の乱数を返します。引数はありません。
int
ceil
(float)
切り上げ。小数点以下を切り上げます。実数のみ。
int
round
(float)
四捨五入。小数点以下を四捨五入して切り捨てまたは切り上げします。実数のみ。
int
floor
(float)
切り下げ。小数点以下を切り捨てます。実数のみ。
その他に以下の組込み関数が使えます。引数と返り値は、int:整数型実数、float:実数(浮動小数点数または整数)、complex:複素数または実数、str:文字列、bool:論理値、x:文字列あるいは数値、void:無効な値、mix:その他各種 です。
int
parseInt
(x[, n])
基本的には文字列を整数に変換します。nが与えられればn進法での表記と解釈して変換します。演算子の(int)とは少し違い、変換できるところまで変換してあとは無視します。全く変換できないときはNaNを返します。第一引数は数値でもいいようですが、第一引数を文字列で与えた場合と数値で与えた場合とで結果が異なります(parseInt("5.5e-2")は5、parseInt(5.5e-2)は0になります)。処理はjavascriptのparseInt()をそのまま呼んでいます。
float
parseFloat
(x)
基本的には文字列を実数に変換します。演算子の(float)とは少し違い、変換できるところまで変換してあとは無視します。全く変換できないときはNaNを返します。処理はjavascriptのparseFloat()をそのまま呼んでいます。
str
date
()
現在の日時(ローカル表示)を文字列で返します。
int
time
()
現在の内部の時計の値を返します。返り値は整数でミリ秒の単位です。
int
arylen
(array)
int
isarray
(mix)
arylen()は配列の要素数を返します。
空の配列では0を返します。
また配列の中の配列はその要素数にかかわらず(空でも)1と数えます。引数が配列でないならエラーになります。
isarray()は引数が配列か否かの判定をし、配列でないなら−1を、配列ならarylen()と同様にその要素数を返します。判定と要素数の取得をいっぺんにやってしまおうという魂胆です。どちらも文字列も配列とみなしますがなるべく文字列にはstrlen()を使いましょう。絵文字の入った文字列の長さは保証できません。
int
strlen
(str)
引数の文字列の長さを返します。ただし絵文字の入った文字列の長さは保証できません。
int
strpos
(str, str[, pos])
第一引数の文字列の中に含まれる第二引数の文字列の位置を返します。位置posが指定されればそこから検索を開始します。最初に見つかった位置を返しますが見つからなければ-1を返します。文字列先頭の位置が0です。
str
substr
(str, pos[, len])
第一引数の文字列のpos位置からlenの長さの文字列を取り出します。文字列先頭の位置が0でposが負の場合位置は後ろから数えます。lenが指定されないか残りの長さを越えていれば最後までです。
str
trim
(str)
引数の文字列の前後の空白文字を取り除いた文字列を返します。空白文字にはスペースのほかにタブや改行なども含まれますが、javascriptのtrim()をそのまま呼んでおり全角スペースの扱いなどはブラウザによって違いが出ることがあります。
bool
isalpha
(str)
引数の文字(文字列なら最初の1文字)が半角アルファベットの場合trueを、そうでなければfalseを返します。
bool
isdigit
(str)
引数の文字(文字列なら最初の1文字)が半角数字文字(0-9)の場合trueを、そうでなければfalseを返します。
bool
isspace
(str)
引数の文字(文字列なら最初の1文字)が半角の空白文字(" ", "\n", "\r", "\t")の場合trueを、そうでなければfalseを返します。
void
clear
()
コンソール画面を消去します。引数と返り値はありません。
void
print
(x1, x2,...)
void
println
(x1, x2,...)
str
sprint
(x1, x2,...)
str
sprintln
(x1, x2,...)
print(),println()はコンソール画面に引数の内容を出力します。引数は文字列または数値で、0個(実質出力なし)から複数個あればそれらを順次出力します。println()は最後に改行します。途中改行するには"\n"を書きます。返り値はありません。
もし内部にHTMLタグを含めるときは終了タグまで一回で(一個のprint()で)書き出してください
。そうしないとブラウザが勝手に終了タグを追加しておかしな表示になることがあります。
sprint(),sprintln()は出力するかわりにその文字列を返します。
str
input
("msg"[, ans])
str
secretinput
("msg"[, ans])
str
answer
()
input
()はコンソールに入力メッセージ(msg)を表示し、ユーザからの入力文字列を受け取り返り値として返します。結果を受け取る変数ansが指定されていればそれにも代入されます。ansは他の名前でもかまいませんしなくてもかまいません。またansに値(数値または文字列)が設定されていれば入力欄にそれが事前挿入されます。値を事前挿入するだけならansは変数でなく数値や文字列でもかまいません。
受け取るデータは文字列ですので数値が必要なら(float)などのキャスト演算子で数値に変換するか、calc(input("msg"))として式の評価をして使ってください。キャスト演算子やcalc()は共に変換に失敗するとerrorを返しますのでそのチェックもお忘れなく。[Enter]キーまたは「入力」ボタンで入力完了、[↑][↓]キーで入力履歴を呼び出せます。
secretinput
()はinputとほぼ同等ですが入力欄を表示しません。完全にプログラム中からputinput()等で値を入力するのに使用します。"msg"は書式の互換上必要なだけで空("")でかまいません(あっても表示されないので)。
answer
()は直前のinput()の返答を返り値として返します。引数はありません。他の関数で結果をローカル変数に受け取った場合でも取得できます。
str
getinput
()
str
setinput
("str")
str
putinput
("str")
割込動作でプログラム中からinput入力を操作するための関数です。
getinput
()は現在入力欄(input)に入力されている値を取得します。
setinput
()は入力欄(input)にプログラム中から値"str"を入力し表示します。ディフォルト値などをあらかじめ入力しておく等に便利です。もし"str"中に改行"\n"があればそこでインプットは完了します。
putinput
()はインプット入力に"str"を設定し入力を完了させます("str"途中に改行"\n"があったらそこで完了します)。
使い方は、input()のあとに書いても入力待ちになってしまいこれらの関数は実行されませんので、
事前に<tag onclick="putinput(...)">とするとか、screenmouseclick()、settimer()などを使って割込を設定しておく必要があります
。いずれも設定または取得した文字列を返します。
void
wait
(ms)
時間待ちします。時間は
ミリ秒
で0から10000(10秒)までですが0では動作はしません。0以外では実際は動作のオーバーヘッドやタイミングロス分(1~数ms程度)が加算されるため時間はさほど正確ではありません。SVGグラフィックの図形の移動や書き換え等のスピード調整などに使用できます。返り値はありません。
void
sleep
(s)
一眠りします。時間は
秒
で0から100(100秒)までです。時間単位がちがうのみで実質wait()と同じです。返り値はありません。
obj
settimer
("command", time)
void
cleartimer
(obj)
settimer()はタイマーで指定時間(time:単位ms)後にCALCの式または文"command"を実行します。"command"は文字列で指定します。タイマーのIDを返しそのIDを使ってcleartimer(ID)で実行前の指定を解除できます。cleartimer()の引数が空なら設定されているすべてのタイマーを解除します。なおこのタイマー動作は割込で処理されますが割込処理中はブラウザとのやりとりは行われません。そのため一部の関数(input()等)は使えず(エラーになります)、画面書き換え等も割込処理が終了してからの動作となります。いったんsettimer()しますとCALCが正常終了したあとも有効になっていますので必要なければ終了時に解除してください。「リセット」ボタンと「実行」ボタンを押したときは自動的に解除されます。
obj
setinterval
("command", time)
void
clearinterval
([obj])
setinterval()はタイマーで指定時間(time:単位ms)ごとににCALCの式または文"command"を繰り返し実行します。"command"は文字列で指定します。インターバルタイマーのIDを返しそのIDを使ってclearinterval(ID)で実行を解除できます。clearinterval()の引数が空なら設定されているすべてのインターバルタイマーを解除します。なおこのインターバルタイマー動作は割込で処理されますが割込処理中はブラウザとのやりとりは行われません。そのため一部の関数(input()等)は使えず(エラーになります)、画面書き換え等も割込処理が終了してからの動作となります。いったんsetinterval()しますとCALCが正常終了したあとも有効になっていますので必要なければ終了時に解除してください。「リセット」ボタンと「実行」ボタンを押したときは自動的に解除されます。
mix
setcookie
("name", value[, life])
mix
getcookie
("name")
ブラウザにcookieを書込、あるいは読み出しします。"name"はcookie名で文字列1文字以上、valueは値で文字列です(数値でもかまいませんが文字列に変換されます)。lifeは有効期間で0か無指定ならブラウザ終了まで、数値なら秒、あとは文字列で"1day"などと指定できます。指定できる時間単位は"sec", "min", "hour", "day", "week", "month", "year" です。消去するにはlifeに-1を指定します。どちらも読み出した値(文字列です)を返しますがcookieがない場合や無効の場合はfalseを返します(空文字との区別のため)。数値を保存しても読み出し値は文字列になりますのでお気をつけください。
mix
calc
("str"[, errmsg])
引数の文字列をCALCの式や構文としてCALCの処理系で評価します。返り値は評価結果の値ですが、
エラーが起こった場合でもエラー停止はせずに常数errorを返します
。またerrmsgに真の値が設定されていればエラーが起こった場合その内容を画面に出力します。
また同名の関数がjavascriptにも登録してあり、タグ内の onclick="calc('...');" などからcalc内の変数を変えたりすることもできます。こちらは割込で優先処理されます。
str
longcalc
("siki")
str
longmode
("mode")
str
longseido
("keta")
str
longseidocut
("cut")
str
longadd
("n1", "n2")
str
longsub
("n1", "n2")
str
longmul
("n1", "n2")
str
longdiv
("n1", "n2")
str
longmod
("n1", "n2")
str
longpow
("n1", "n2")
str
longfact
("n")
str
longroot
(["n1", ]"n2")
str
longtoE
("n"[, "keta"[, "cut"]])
高精度文字列計算関係の関数です。
引数および返り値はすべて文字列です。
longcalc
()は引数の文字列で表された式を計算し結果を数字文字列で返します。数値文字列は整数、小数点表記実数、指数表記実数(1.234E5など)のいずれでもかまいません。エラーが起こった場合はエラー内容を表示し定数errorを返します。
可能な演算は(1)加算減算"+,-", (2)乗算除算剰余算"*,/,%", (3)符号"+,-"("+"はなくてよい), (4)累乗"**"(累乗数は0か正の数で小数部はあっても1桁か2桁), (5)階乗"!"(0か正の整数に対して一重のみ、!!はエラー), (6)優先括弧の"()"、及び(7)関数root()(rootはlongrootの組込名)です。優先順位は(1)->(7)の順に高くなります。
longmode
()は計算・表示モードを設定します。"mode"が"."ならば整数.小数モード、"E"または"e"なら指数モードです。指数モードでも指数が0の場合のE0はつきません(0だから)。整数.小数モードの方が概ね計算は速いです。
longseido
()は以下の計算での計算精度として有効桁数を指定します。整数.小数モードでは精度は小数部のみに適用され整数部の桁数は無制限です(もともと長整数の計算から出発したのでその互換のため)。指定精度以下の値は
評価や計算の都度
longseidocut()で指定された方法で丸められますので誤差の累積により最後の1,2桁が少し狂うことがあります。精度のディフォルトは24です。引数がなければ現在の値を文字列で返します。
longseidocut
()は指定精度での丸めの方法を指定します。引数は"ceil"(切上げ),"round"(四捨五入),"floor"(切捨て),あるいは切り捨てる上限の数字("0"~"9")です。ディフォルトは"4"(四捨五入)です。
その他
longadd
()から
longfact
()は各々の演算の個別関数で引数は数字文字列、返り値は結果の数字文字列です。
longroot
("n1","n2")は"n2"の"n1"乗根を求めます。平方根の場合のみ第一引数を省略して
longroot
("n2")でもかまいません。
longtoE
()は数字文字列"n"を有効数字"keta"(指定されなければ100)、"cut"で指定された丸めの方法("ceil","round","floor"など、指定されなければ"round")で指数表記(1.234E+5などの形)に変換します。有効数字の指定は整数部含めて適用されます。
計算する数字の桁数(結果も含めて)、精度(有効数字)共にあまり大きいと、特に計算負荷の高い累乗、階乗、root()などの演算でタイムオーバーすることがありますのでご注意。
mix
jseval
("str")
引数の
文字列
をjavascriptの式や構文としてjavascriptの処理系で評価し、結果を数値,文字列,論理値,配列のいずれかで返します。内部ではjavascriptのeval()関数を文字列引数で呼出し、返り値は数値,文字列,論理値,配列のいずれかでなければ文字列に変換します(javascriptのわけのわからないObjectなどを返されても困るからです)。CALCに用意されていない文字列処理、配列処理、あるいは正規表現処理などを実行するのに使えます。CALCを動かすスクリプトレベルでの処理なので名前がかぶったり文法ミスがあったりすると何が起こるかわからず、エラーならまだしも固まって操作不能になることも起こりえますので気をつけてお使いください。
bool
delete
(var[, var ...])
与えられた引数の変数,配列,関数などを消去します。配列,関数の場合はその内部要素もそっくり消去されます。ユーザ定義の変数,配列,関数はもとより
組込の常数や関数も消去できます(虚数単位i,jは特殊な数値であり変数ではないので消去はできません)
…意識的に組込の常数や関数を書き換えたいときのために一切チェックはしないということです。いったん消去したら再定義するまでの間その名前の変数や関数は使えません。離して記述するとわかりにくくなりますので、できるだけ再定義と対でお使いください。
メモリーの開放についてはjavascriptのガーベージコレクションで行われますので実際のメモリー開放とは必ずしもイコールではありませんし、特別大量のメモリーを喰っていない限りdeleteの必要はありません。すべての変数の削除に成功したらtrue,それ以外(変数がないか変数があって削除に失敗したとき)falseを返します。引数がないか変数でない引数(数字や文字列など)を与えたときはエラーになります。いったん消去した変数や関数を元に復活する関数はありません(退避用の変数に保存しておいて戻せばできますが)。
bool
usestrict
(mix)
プログラムチェック用に多少厳密なチェックを有効あるいは無効にします。引数はtrue/false(または1/0)で真の値なら有効、偽の値なら無効となります。有効に設定された場合true、無効に設定された場合falseを返します。
厳密チェックモードを有効にすると次のような場合エラーになります。多少プログラミングは窮屈になりますがバグの発生は抑えられます。
●変数が宣言されずに、あるいは宣言前に使用された。●同じ名前の関数または変数が競合して多重に宣言された(但し宣言がif-elseなどの条件文の中だと必ずしもうまく動作しない場合があります)。●実行関数の引数(仮引数)と同じ名前の変数が関数内で宣言された。●関数に期待されない引数が渡された(例:isalpha()に文字か文字列以外を渡した場合など)。
●ユーザ関数への引数が省略されて呼び出された(引数省略機能を使った)。
検出や動作は必ずしも完璧ではありません。あくまで参考にプログラミングの補助機能としてプログラム作成時だけにお使いください。関数calc()の中では効きません(変数を使うのにいちいちvar宣言とかめんどうなので)。
◆ グラフィック ◆
ほんのおまけでごく簡単なグラフィックを描くこともできます。グラフィックはSVGとcanvasが両方同時に使えます。ただし大量のSVGグラフィックを描くとだんだんグラフィック処理が重くなって遅くなり最後にはブラウザが固まるかこけますのでSVGグラフィックの要素の数(SVG描画関数の呼出回数)は数千が限度でしょう。canvasにはこの制限はありません。細かい点を多数操作するならcanvasをお使いください。
以下の説明は基本的にSVGでの説明で
canvasではタグidは返さずidでの書き換えはできません
。
その他SVGとcanvasでは微妙なちがいが出ることがあります。どちらを使うかは実際に試して決めてください。
以下、グラフィック関係の関数で
頭に"c_"がついているものはcanvasの描画関数
でそれ以外はSVGの描画関数です。色などの描画パラメータはcanvasとSVGとで共通です。寸法の単位はすべてpx(ピクセル)です。
描いたグラフィックはCONSOLE右上の「画像保存」ボタンで画像として保存できますが、ブラウザによってはtextの文字や空白が表示と異なる場合があります。インターネットエクスプローラ11(Trident/7)ではSVG部分は保存されず、またインターネットエクスプローラ10以下ではグラフィックそのものが使えません。その他ブラウザによる違いなどがあり実際にやって確認してください。
グラフィック[SVG保存]ボタンでSVGデータの保存ができますがHTML5ブラウザ向けですのでブラウザ以外の画像ソフトでの表示には多少修正が必要かもしれません。
スクリーンあるいはSVG描画タグは描画条件を変えたり描画内容を変えることができ、その指定はsid.screen(...)やtid.text(...)といったid.fnc()形式で行います。sidやtidは関数が返すスクリーンタグや描画タグのオブジェクト識別idです(実態は単なる整数ですが)。なおいったん投入したグラフィック関係のタグの削除はできません。消したり無効にしたい場合は色を透明にしたり表示範囲外に移動するなどして対応してください。
グラフィック関係の関数では一部の関数(screen(),pendash())を除きユーザ関数と同様に引数の省略機能が動作します。すなわちいったん与えた引数は次回以降省略すると同じ値が使われ同じことを毎回記述せずにすみます。
int
consolewidth
()
コンソール画面の幅を取得します。いちおうスクロールバーや余白の余裕を見た値です(ブラウザによって少し異なります)。コンソール画面のサイズに合わせてグラフィックしたいときに使います。引数はありません。
int
consoleheight
()
コンソール画面の高さを取得します。いちおうスクロールバーや余白の余裕を見た値です(ブラウザによって少し異なります)。コンソール画面のサイズに合わせてグラフィックしたいときに使います。引数はありません。
int [sid.]
mpx
()
int [sid.]
mpy
()
マウスがスクリーン上にあるとして各々そのx座標、y座標を返します(いちいちスクリーン内かどうかまでは調べません)。sidはスクリーンidで、省略された場合はマウスの乗っている(重なっている場合は一番上の)スクリーンです。
sid [sid.]
screenstyle
("style")
SVGとcanvasに共通です。グラフィック画面の外観をCSSスタイルを使って設定または変更します。
newscreen()やscreen()で設定する内部の描画用パラメータとは別です。
引数のstyleはCSSスタイル形式で指定します。初期値は""(空)でこれでよければ特に実行する必要はありません。枠をつけるならscreenstyle("border:2px solid silver;");などとします。sidはスクリーンのid番号ですが、
指定されなければこれからnewscreen()で作成されるスクリーンすべてに、指定されれば(すでにある)その番号のスクリーンにのみ適用します。不要な書き直しを避けるために基本的にはスクリーン作成前に指定してください。
適用したスクリーンのidを返します。
...参考までによく使いそうな"style"設定を例としてあげておきます。スタイルなし:""、背景色:"background:#def;"、枠:"border:2px solid silver;"、幅:"width:400px;"、高さ:"height:300px;"、左マージン:"margin-left:50px;"、上マージン:"margin-top:10px;"、右マージン:"margin-right:8px;"、下マージン"margin-bottom:8px;"、非表示:"visivirity:hidden;"、表示:"visibility:visible;"、はみだし部分も表示する:"overflow:visible;" など。
sid
newscreen
(w,h,zw,zh,dx,dy,so)
SVGとcanvasに共通です。新しいグラフィック画面(領域)を準備し描画対象スクリーンに設定します。引数は、w,h:描画領域の幅,高さ(int)、zw,zh:描画内容の拡大縮小率(float)、dx,dy:描画内容の位置補正(int)、so:canvasとSVGの重ね順で"cs"ならcanvasの上にSVG、"sc"ならSVGの上にcanvasです。
"sc"は重ね合わせた画像の保存がうまくできず、canvasかSVGのいずれか一方しか保存できません。
初期値は w=400, h=300, zw=1.0, zh=1.0, dx=0, dy=0, so="cs" で、以上は与えられた引数の値のみ前の値から変更されます(引数省略機能が動作します)。描画領域外にはみ出た部分は表示されません。作成されたスクリーンのidを返します。作成するスクリーンのidの指定はできません(自動的に1から順番になります)。
sid [sid.]
screen
(w,h,zw,zh,dx,dy,so)
SVGとcanvasに共通です。スクリーンパラメータを変更しそれを使って内容を書き換えますが描画対象スクリーンは変更されません。スクリーンidを指定しない場合現在の描画スクリーンを対象としますがまだスクリーンがなければ特別サービスで新しく作成します。引数はnewscreen()と同じですが、
与えた引数のパラメータのみ変更(再設定)されます(引数省略機能は動作しません)
。すでにあるSVG画像は新しいパラメータで書き直されますが
canvas画像は書き直されません
。変化させる画像とさせない画像の使い分けには利用できます。SVGは画像データによっては書き直しに時間がかかります。変更を適用したスクリーンのidを返します。
sid [sid.]
screenselect
()
SVGとcanvasに共通です。引数はありません。スクリーンidの指定があればそのスクリーンを描画対象スクリーンに変更します。id指定がなければそのまま描画対象スクリーンのidを返しますが、まだスクリーンがなければ0が返されるのでスクリーンの有無の判定もできます。
sid [sid.]
screencopy
()
SVGとcanvasに共通です。引数 はありません。グラフィックスクリーンをコピー表示します。すなわち同じ画像が表示されます。描画対象スクリーンは変更されません。作ったアイコン画像を並べたいときなどにお使いください。sidはnewscreen()で返されたコピー元のスクリーンidで、指定されなければ現在有効なスクリーンです。返り値は新しくコピーして作ったスクリーンのidでidの指定はできません。
sid [sid.]
screenmouseall
('siki')
sid [sid.]
screenmouseover
('siki')
sid [sid.]
screenmouseout
('siki')
sid [sid.]
screenmousemove
('siki')
sid [sid.]
screenmousedown
('siki')
sid [sid.]
screenmouseup
('siki')
sid [sid.]
screenmouseclick
('siki')
SVGとcanvasに共通です。グラフィックスクリーン上でのマウスイベントの処理を指定します。mouseallはすべてのマウスイベントにまとめて設定します。常に同じカーソル表示をする場合などいちいち個別に設定しなくてすみます。 'siki'は
式を文字列として記載しCALCの式として評価されます。解除するなら''(空文字)またはnullを設定します。文字列を返すとマウスがスクリーン内にあるときカーソル位置に表示されます。
なお上記設定によらずグラフィック番号("Graphic1"とか)は常に表示されます(画像保存の番号選択のため)。sidはスクリーンidですが省略されると現在の描画スクリーンに対して設定します。設定したスクリーンのidを返します。なおこの動作は割込で処理されますが割込処理中はブラウザとのやりとりは行われません。そのため一部の関数(input()等)は使えず(エラーになります)、画面書き換え等も割込処理が終了してからの動作となります。
同じスクリーンに複数のイベントを設定した場合ブラウザによってイベントの発生の有無や順序が異なり(特にclick,down,up)なかなか思い通りにならなかったり、またマウスの動きが速いと(特にmoveで)追従や動作しない場合があります。
スクリーン画面をダブルクリックするとマウス座標を表示して10秒間その値を記憶し、その間にプログラムの座標を記入したいところをクリックすればそこに座標が"X,Y"の形で挿入されます。描画関数の引数に位置の値を入れるのに使用します。慣れると描画タグのコピーペーストと組み合わせて楽にタグを書き込めます。10秒以内にスクリーン画面で再度ダブルクリックすると表示と記憶は消されます。座標が記憶されている間はダブルクリックしたマウスカーソルの位置にその旨表示されているので判別が可能です。XとYはスクリーン内部の描画座標、(x,y)は画面上の見かけのピクセル数で、挿入されるのは描画座標のほうです。スクリーンへの拡大縮小や位置補正がかかっていなければこれらは同じで描画座標のみ表示されます。描画タグへの位置補正zoom,rotate,shiftが有効の場合値がずれますのでご注意ください。
以下グラフィック描画パラメータの設定関数です。
SVGとCANVASに共通
で
現在選択されているスクリーンに対し設定
します。
void
pen
(w,"lc","lj")
図形を描く筆を準備します。wは太さ(float)で初期値は1.0、1以下を指定すると色が薄くなりそれらしくみえます。lcは筆先の形状で文字列"butt","round","square"のいずれか(初期値は"butt")、ljは接続部の形状で"miter","round","bevel"のいずれか(初期値は"miter")です。詳しくはSVGのstroke-linecap,stroke-linejoinを検索参照してください。与えられた引数の値のみセット(変更)されます。
void
pendash
([l1,l2,...])
破線を描く設定をします。引数は描画する線の長さ,描画しない線の長さを交互に指定します。
与えた引数のみでセットされます(引数省略機能は動作しません)
。引数が1個か与えられなければ普通の直線にリセットされます。一部のブラウザのcanvasではうまく動作しません。
void
color
("c1","c2",o1,o2)
描画色および色の不透明度を設定します。c1,c2は色を表す文字列で、c1は主色(主に図形の外形、線の色、文字の色)、c2は副色(図形内部や文字の縁取り等)の色です。o1,o2は数値(float)で0.0~1.0の値、o1は主色の不透明度(0:透明,1:不透明)、o2は副色の不透明度です。不透明度は色の方にagba()を使って設定してもかまいませんが両方混ぜて使うと競合して思い通りになりませんのでどちらか一方だけにしておくのがいいです。色の表現は"transparent"(透明)のほか、"white","red","blue"などの色名称(WEB標準色)、3原色16進指定"#000"(黒)~"#FFF"(白)、"#000000"(黒)~"#FFFFFF"(白)、"rgb()"、"rgba()"などが可能です。空文字("")は透明になります。与えられた引数の値のみセット(変更)されます。初期値はc1が黒("black")、c2が透明("transparent")、o1,o2は共に1.0(不透明)ですが副色c2の初期値は色が設定されていないので実際には透明になります(色を指定したとき効果を発揮します)。
void
font
("style","weight","family")
テキストを書くのに使用するフォントのスタイル、太さ、種類を設定します。styleは"normal"(標準体),"italic"(イタリック体),"oblique"(斜体...日本語フォントでは概ねitalicと同じ),"small-caps"(英小文字を小さな大文字で表示しますが無視される場合が多いです)で複数なら間を空白で区切ります。weightは"normal"(標準)、"bold"(太字)または"100"~"900"の太さを表す数字です。familyは書体(フォント)名ですが"monospace","sans-serif","serif","cursive","fantasy"等一般的なフォント指定で指定しておくのが安全そうです(なければPCやブラウザのディフォルトのフォントになるけどむしろもくろみからは遠いかもしれない)。ディフォルトは"normal","normal","monospace"です。与えられた引数の値のみ変更されます。返り値はありません。
void
anchor
(x,y)
void
scale
(zx,zy,zp)
void
zoom
(zx,zy,zp)
void
rotate
(angle)
void
turn
(angle)
void
rotated
(angle)
void
turnd
(angle)
void
translate
(x,y)
void
shift
(x,y)
今後の描画時の描画位置などの補正をします。すでに描いてしまった図形には影響しません。またスクリーンそのものに設定する表示時の拡大縮小や位置の移動とは別です。
anchorはscale,zoom、およびrotate,turnの基準点を設定します。ディフォルト値はどちらも0です。
scale,zoomは拡大縮小で、縮尺は横方向zxと縦方向zyの位置や寸法、縮尺で線が細くあるいは太くなりすぎて見にくくなったときの線幅補正zp(縦横はなし、縮尺された線幅に対して)を指定できます。ディフォルト値はすべて1.0(等倍)です。scaleとzoomは同じです。
rotate,turnは回転で角度の単位はラジアン、時計回りが正の値です。rotated,turndは角度の単位は度(degree)です。ディフォルト値は0(回転なし)です。rotateとturnは同じです。
translate,shiftは横及び縦方向の平行移動で単位はピクセルです。ディフォルト値はどちらも0(移動なし)です。translateとshiftは同じです。translate,shiftは元の座標系で行われ、scale及びrotateの影響は受けません(そうしておかないと計算がめんどうでわけがわからなくなった)。
処理はscale->rotate->translateの順に行われます。いったん設定するとずっと有効になるので必要な処理が終わったら戻してください。save()とrestore()を使えばいちいち値を指定せず戻せます。
斜めの図形を描いたり、すでに書いてしまったコードで一部大きさや位置を変更・修正したり、またSVGではこれを使って図形を書き直すことにより動かしたりすることもできます。
int
save
([idnum])
int
store
([idnum])
int
restore
([idnum])
save()とstore()は上記pen()からrotate()までの関数で設定されたパラメータを保存し、restore()はそれを復元します。saveとstoreは同じです。引数がないなら自動でデータをpush/popしますのでsave()とrestore()は正しく対応していないといけません。正の整数が引数idnumとして指定されればその番号で保存/復元しますがこの場合は自動的なpush/popはしません。手動指定する場合の引数の番号は引数なしの自動採番(1から)とかぶらないようある程度大きな番号を指定するのが確かです。保存された、あるいは復元されたパラメータのid番号を返します。
以下グラフィックの描画関数です。現在選択されているスクリーンに対し描画します。
頭がc_のものはCANVAS用でCANVASスクリーンに描画し、それ以外はSVGスクリーンに描画します。
SVGでは描画タグのid(tid)を返し、tid.line(...)などの形で書き換えができます。tidは1からタグ作成順につけられます。tidは手動での指定もできますが自動のid番号と重ならないような充分大きな値(推奨90000以上)をお薦めします。手動idのタグは自動作成されませんのでタグの作成は tid=90001;tid.line(...); あるいは 90001.text(...); などの形になります。同じ描画関数をスクリーンをまたいで使用するなどでタグidを固定したい場合に手動指定を使用します。
なおいったんグラフィックに投入した描画タグの削除はできません。
tid [tid.]
line
(x1,y1,x2,y2)
void
c_line
(x1,y1,x2,y2)
直線を引きます。x1,y1,x2,y2は各々始点のx座標,y座標、終点のx座標,y座標です。SVGでは描いた図形タグのidを返し、idが指定されればそのidの図形を書き換えます。線の長さは小数でもかまいませんが短いと何も描かないブラウザがあり限界はよくわかりません。0.1くらいあればいいようです。
tid [tid.]
rect
(x,y,w,h,rx,ry)
void
c_rect
(x,y,w,h)
四角形を描きます。x,y,w,hは各々左上のx座標,y座標、四角形の幅,高さ、rx,ryは角の丸さ(SVGのみ)です。SVGでは描いた図形のidを返し、idが指定されればそのidの図形を書き換えます。
tid [tid.]
circle
(x,y,r)
void
c_circle
(x,y,r)
丸(円ともいう)を描きます。x,y,rは各々中心のx座標,y座標、半径です。SVGでは描いた図形のidを返し、idが指定されればそのidの図形を書き換えます。
tid [tid.]
ellipse
(x,y,rx,ry)
void
c_ellipse
(x,y,rx,ry)
楕円を描きます。x,y,rx,ryは各々中心のx座標,y座標、横半径、縦半径です。SVGでは描いた図形のidを返し、idが指定されればそのidの図形を書き換えます。
tid [tid.]
text
(x,y,"text","size","anc")
void
c_text
(x,y,"text","size","anc")
テキストを書きます。xはテキストanc位置のx座標,yはテキスト
ほぼ下
側(フォントのベースライン?)のy座標、textはテキストの内容、sizeは大きさ("14px","12pt","1em","150%"などです。数字だけならピクセルとみなされます)、ancは左右基準位置の指定("start","left":文字の左下、"middle","center":中央下、"end","right":右下)です。テキストには改行は含ませない方が確かです。また表示されるテキストの長さはなぜかSVGとcanvasで、あるいは表示環境によってかなりのちがいが出て尻切れになることがあるので充分余裕をとってください。SVGでは描いた図形のidを返し、idが指定されればそのidの図形を書き換えます。
tid [tid.]
point
(x,y,w,h)
void
c_point
(x,y,w,h)
座標x,yを中心に四角の点を打ちます。x,y,w,hは各々中心のx座標,y座標、幅、高さです。pen()の値は使いません。SVGでは描いた図形のidを返し、idが指定されればそのidの図形を書き換えます。
...ただしSVGで大量の点をうつとだんだんグラフィックデータが重くなり最後にはブラウザが固まるかこけます。数千ポイント程度を上限としてください。
大量に点を打つならcanvas関数c_point()をお使いください。
pid [pid.]
path
()
void
c_path
()
void
c_drawpath
()
path()は多角形、円弧、曲線など多彩な図形を描く手順(いわゆるパス)を設定します。svgでidが指定されなければ、あるいはcanvasでは新しいパスの開始を宣言しますがこの段階ではまだ何も描かれません。色や線の太さなどのパラメータはSVGではパス宣言時、canvasではc_drawpath()実行時に取り込まれ適用されます。
新しいパスの最初のコマンドはmoveto()でなくてはなりません
。SVGでは設定されたパスのidを返し、idが指定されればパスをそのidに切り替え
パラメータを取り込み直して再描画します
。一方canvasの場合(c_path())では(あれば)現行のパスを終了し新しいパスを設定しますが
既存のパスはc_drawpath()を実行しないと何も描画されません
のでお気をつけください。
描画はSVGの場合描画コマンドの都度自動的に行われますが
canvasではc_drawpath()で行われます
。canvasで都度描画しますと透明度を設定した場合上書きで効果が変わってきます(透明度を低く設定しておいて幾度も上書きしてじわっと表示するのには使えますが)。
以下はパスへの描画コマンドですが、いったんパスに投入された個々の描画コマンドの削除や変更はできません。
pid [pid.]
moveto
(x,y)
void
c_moveto
(x,y)
パスの始点として座標x,yを設定します。ペンをそこに持っていくイメージです。
path(),c_path()を宣言したら最初に実行する必要があります。
また描画コマンドの間でもかまいません。SVGでは設定したパスのidを返し、idにpath()で返されたid番号を指定するとそのパスに新しい始点の座標を設定します。
pid [pid.]
lineto
(x,y)
void
c_lineto
(x,y)
パスの始点から座標x,yに直線を引きx,yを新しい始点に設定します。多角形を描くときはlineto()を必要回数続けて最後にclosepath()します。SVGでは描画したパスのidを返し、idにpath()で返されたid番号を指定するとそのパスに描画します。
pid [pid.]
ellipseto
(x,y,rx,ry,ro,sf)
楕円弧を描きますが話が難しいです。パスの始点から座標x,yを終点とし、x軸半径rx、y軸半径ryで、角度ro
(単位は度)
だけ回転した楕円の楕円弧を描きますが、そのような楕円は2つ考えられ、さらに始点から終点まで4つの弧が想定されます(左図参照、交点が始点終点)。だからsfの値0~3でその弧を選びます。半径の指定が小さく指定点を通らないなら半径は自動的に拡大されます。...真円ならともかく傾いた楕円ともなるとパラメータが直感的に設定しにくいのが難です。描画後x,yを新しい始点に設定します。idにpath()で返されたidを指定するとそのパスに描画します。描画したパスのidを返します。
void
c_arc
(cx,cy,r,sa,ea,cc)
円弧を描きます。点(cx,cy)を中心として半径rの円または円弧を描きます。sa,eaは開始終了の円弧の角度で0から2*PI。ccは円弧を描く方向でtrueなら反時計回りになります。線は開始点から円弧のsaにあたる点に引かれ円弧が描かれ円弧の終了点が次のパスの開始点になります。値は返しません。
void
c_arcto
(x1,y1,x2,y2,r)
円弧を描きますがこちらも話が難しいです。始点(x0,y0)からx1,y1への直線とx1,y1からx2,y2への直線に接する半径rの円を考え、始点から円との接点までの直線、その接点からもう一つの直線との接点まで円弧を描きます。canvasの場合は円で楕円にはなりません。値は返しません。
pid [pid.]
curveto
(x,y,df)
void
c_curveto
(x,y,df)
前回のパスを参考にしてパスの始点から座標x,yに曲線を引きx,yを新しい始点に設定します。少ない点でなめらかな曲線にしたいとき使用しますが、あまり細かい範囲で使用したり急に折れ曲がったりすると振動したりしてかえっておかしな描画になることがあります。引数dfはそれを抑えるためのダンパー係数で0.0~1.0を指定できます(ディフォルトは0.5、ダンパーなしが0.0、1.0だとフルダンパーで直線描画になります)。SVGではidにpath()で返されたid番号を指定するとそのパスに描画します。描画したパスのidを返します。
pid [pid.]
closepath
()
void
c_closepath
()
パスの最後の点からmoveto()で設定された最初の点へ直線を引き
図形を閉じます
。path()コマンドを閉じる(終了する)のではありません。多角形を描くときはlineto()を必要回数続けて最後にclosepath()します。SVGではidにpath()で返されたid番号を指定するとそのパスに描画します。描画したパスのidを返します。
str [sid.]
getsvgcode
(sn)
編集・チェック用の関数です。指定された、あるいは無指定なら現在有効なグラフィックスクリーンのSVGコード(タグを表示用に変換したもの)を返します。引数snで単なる整数でのスクリーン指定も受け付けます(sn優先、テストあるいはチェック用)。canvas部分は取り除かれます。printすればSVGグラフィックのコードが表示されます。printして表示されたコードをそのまま他のホームページにコピーペーストしてSVG画像を表示させることもできます。
void
showpoint
(r)
void
c_showpoint
(r)
編集・チェック用の関数です。r>=1なら描画タグの指定点に○(canvasでは□)印を表示します。rは○(□)の大きさ(半径または片側)です。パスのmoveto()は緑、lineto()は青、curveto()は赤、その他のパスは紫、パス以外のタグ(point,c_pointを除く)は茶色で表示されます。
◆ ユーザ定義関数 ◆
その他ユーザ定義関数が使えます。関数定義の書式は function fncname(a,b,...){ 式;...; } で()や{}は省略できません。関数の呼出(実行)は fncname(1,2,...) となります。基本的にはごく一般的な関数の使用法と同じですが、以下にいくつか注意事項を書いておきます。以下引数の()内と関数本体の{}内は省略します。
関数名
いわゆる予約語は使えません。またすでに定義されている常数名や組込関数名は使えません(先にdelete()で消去してしまえば可能ですが)。関数名に使える文字はアルファベット、数字、アンダスコア("_")ですが、頭はアルファベットか"_"で数字は除かれます。(以上変数名と同じです)。
関数の名前は必ずしもなくてもかまいません(いわゆる無名関数。内部的には動作上ひそかに名前をつけてはいますが)。 この場合名前でのアクセスはできませんので、(function(){})()としてその場で実行するか、f=function(){}; として変数に入れ、f()で実行するか、別の関数の返り値 function f1(){ ...; return function(){}; }として親関数を通して f1()() として実行するなどしなくてはならなくなります。
属性の付加
関数を書換禁止にするには const function f(){} とします。変数とちがいvar宣言しなおしての書換はできません。関数内からグローバル関数を作成するには global function f(){} とします。これらの関数の属性に関する構文はconst等の宣言の中で関数を定義するのではなく、関数定義のfunctionに属性を付加するものとして解釈され、属性の適用はその関数定義にのみ一回きりです。
定義位置
関数は通常トップレベルに記載しますが、関数内でに書いてもエラーにはなりません。関数内で定義した関数は通常その関数内でしか使えません(アクセス演算子"."を使えば外からでも可能)。
実は function の書かれた場所のチェックはしていませんので if-else 構文内や、その他の場所でも()で囲って式化してしまえば式の位置にでも書くことは可能かも...でもすべて確認したわけではありませんのでエラーにならずにちゃんと動作すればということで...。
関数の再定義
ユーザ関数を再定義してもエラーにはなりません(厳密チェック(strict)モードではエラーになります)。実行時function定義を通過した時点で関数の実行コードは書き変わりstatic以外の内部変数等は消去されます。特に関数内関数は親関数の実行の度に再定義されます。またglobal宣言で外部に作成した変数や関数はめんどう見ません。通常同じ関数名を使い回す必要はないと思われますし実際にどの関数が動いているのかわからずバグの原因になりやすいのでなるべく再定義はしないようにしましょう。組込関数の再定義はエラーにしています(先にdelete()で消去すれば可能です)。
引数
関数の引数は数値、論理値、文字列、配列等多くの型の使用が可能ですが、voidとundefinedは有効な引数としては認められません。
関数の引数の数は呼出側と実行側で合っているのが基本ですが、合っていない場合必ずしもエラーとはならず次のような動作となります。
・CALCの
ユーザ関数
と
ほとんどの組込グラフィック関数
はいったん与えられた引数を覚えこみます。これを利用して2回目以降省略ができ同じ記述を幾度もせずにすみます。
・関数定義の際引数変数に初期値を与えることもでき、たとえば function f(a,b=0,c=1){} などとするとbやcに呼出側で引数が与えらなければこの初期値が使われます。
・実行側の引数が少ない場合、呼出側の余った引数は単に無視されエラーにはなりません。
・呼出側の引数が少ない場合、関数定義で初期値を与えていなければ初回エラーになりますが、初回引数を与えて正常に実行した後ならば省略しても前の引数が使われエラーになりません。
・引数の省略は順番さえ守れば一部省略一部与えるというのも可能です。優先順位は与えた引数、関数に記述した初期値、いったん与えて記憶された値の順です。
・でもこの
引数省略機能
は必ずしも他の言語では使えませんのでご承知置きください(グラフィックの場合などとても便利だけど)。数学関数などではこの機能は動作せず引数の省略はできません。
・関数引数の()内のNameSpaceは呼出側ではその関数の位置(呼出側関数の外側)、実行関数側(function宣言された側)ではその関数の内部と同じになります。引数()の内部で変数や関数を使う場合にはお気を付けください。さらに実行側関数の仮引数の名前がすでにあるグローバル変数の名前とかぶった場合、うっかり気がつかないと誤動作のもととなりますので仮引数の名前設定にはお気をつけください。
・関数の今回呼出の引数(有効無効判定前)、および実際に使われ記憶された有効な引数は各々argc,argv[]およびargcm,argvm[]に入っていますので可変長引数関数なども書こうと思えば書けます。
関数の初期化
関数の初期化は、プログラム実行開始時のいわゆるプログラム初期化時、実行がfunction定義を通過したとき(関数再定義)、および関数を変数に代入したとき(代入した変数側で)行われます。初期化ではstatic変数を除く関数内変数等はいったん削除され次の関数実行時に変数宣言等が再実行されます。よってfunction定義を実行プログラムの途中に書くとそこで初期化が起こり内部変数が予期せず書き換わることがあるのと無駄な初期化が起こるので、関数定義はなるべく最後に書くのが無難です。関数fを強制的に初期化するにはf=f;とすれば(static変数を除き)初期化されます(変数代入時の初期化)。
関数の実行
通常は関数を function f1(){}で定義し、f1()で実行します。(function f1(){})()で定義してその場で実行することもできます。さらにはfunction f1(){}()でも実行します。(関数定義に直に続けて()を書いた場合。正しい書き方は(function f1(){})()なのですが。)。 その場で一回だけ実行すればいいなら関数名f1はなくてもかまいません。また下記のように変数に代入して実行することもできます。
変数への代入実行
function f1(){} で関数f1を作成しておいてf=f1;で関数f1を変数fに代入し、f()で関数を実行することは可能です。内部変数等は代入時に初期化され、以後代入した変数fの所有としてf1とfの内部変数は分離されます。内部変数までそっくりコピーするなら f = copy f1;とすればその時点の変数名と値がそっくりコピーされますが、やはり変数の所有は分離されます。
返り値
値を返すには return 式; を使います。return 式; の式、あるいはreturnそのものがない場合は値は返しません(正確に言うと無効な値voidを返します)。
◆ コメント ◆
/*と*/で囲まれた範囲、//から行末まで、および#から行末まではコメントとなります。コメント内部には日本語が使えます。/* */は完結していないとエラーになります。コメントのネストは書き方によってはエラーになりますのでしないほうがいいでしょう。単なる"文字列";のみの文も実質コメントと同じですが最後に書けば結果として表示されます。
◆ その他 ◆
セキュリティー上ハードウェアにからむ操作(メモリ操作、ファイル操作、I/O操作など)はできません。
エラー検知についてはよくありそうなエラーはできるだけ検知して表示するようにしていますが必ずしも完璧ではなく、エラー検知できずに誤動作したりおかしな値になることも可能性としてはなきにしもあらずですのでご承知おきください。