![]() |
![]() |
![]() |
Scheme 版 と Python 版 の関数電卓に続いて今回は JavaScript 版です。
紫藤のページの関数電卓は、
実は、JavaScript もこの条件をみたし、Python と同じように関数電卓スクリプトを書くことができます。 JavaScript は安っぽい言語と思われがちですが、実はいけている言語です。 詳しくは
を見てください。 スクリプトのソースコードを下に示します。[calc.js]
001: /** 002: * 電卓スクリプト 003: * JavaScript 版 004: * **/ 005: 006: 007: 008: // map を定義 009: if (!Array.map) { 010: if (!Array.prototype.map) { 011: Array.prototype.map = function (callback, thisObject) { 012: var length = this.length; 013: var result = new Array(length); 014: for (var i = 0; i < length; i++) 015: result[i] = callback.call(thisObject, this[i], i, this); 016: return result; 017: }; 018: } 019: Array.map = function (array, callback, thisObject) { 020: return Array.prototype.map.call(array, callback, thisObject); 021: }; 022: } 023: 024: 025: 026: 027: // 演算子の優先度 028: P_BPM=0; 029: P_BMD=1; 030: P_UPM=2; 031: P_POW=3; 032: P_FUN=4; 033: P_LHB=5; 034: P_CST=6; 035: 036: 037: 038: 039: function Operator(name, fun, priority) 040: /** 041: * 演算子クラスのコンストラクタ 042: * **/ 043: { 044: this.name=name; 045: this.fun=fun; 046: this.priority=priority; 047: } 048: 049: 050: // 文字列に変換 051: Operator.prototype.toString=function(){return this.name}; 052: 053: // 関数を呼び出す 054: Operator.prototype.call=function(){ 055: if (this.priority==P_CST) return this.fun; 056: else if(this.is_unary()) return this.fun(arguments[0]); 057: else return this.fun(arguments[0], arguments[1]); 058: } 059: 060: // this が other より優先度が大きいとき true を返す 061: Operator.prototype.is_higher=function(other){ 062: return other==null || this.priority > other.priority || (this.is_reverse() && this.priority==other.priority) 063: }; 064: 065: // 定数か? 066: Operator.prototype.is_const=function(){return this.priority == P_CST}; 067: 068: // 関数か? 069: Operator.prototype.is_func=function(){return this.priority == P_FUN}; 070: 071: // 単項演算子か? 072: Operator.prototype.is_unary=function(){return this.priority == P_UPM || this.priority == P_FUN}; 073: 074: // fact か? 075: Operator.prototype.is_lhb=function(){return this.priority == P_LHB}; 076: 077: // 逆順の演算子か? 078: Operator.prototype.is_reverse=function(){ 079: return this.priority == P_FUN || 080: this.priority == P_POW || 081: this.priority == P_UPM 082: }; 083: 084: 085: function fact(n) 086: { 087: for( var m=i=1; i<n; m*=++i); 088: return m; 089: } 090: 091: 092: function permutation(m0, n0) 093: /** 094: * 順列 095: * **/ 096: { 097: for (var a=i=m0, n=m0-n0; i>n; a*=--i); 098: return a; 099: } 100: 101: 102: function combination(m,n) 103: /** 104: * 組み合わせ 105: * **/ 106: { 107: return permutation(m,n)/fact(n); 108: } 109: 110: 111: 112: // 演算子のリスト 113: LS_OP = new Object(); 114: LS_OP['+'] = new Operator('+', function (x,y){return x+y;}, P_BPM); 115: LS_OP['-'] = new Operator('-', function (x,y){return x-y;}, P_BPM); 116: LS_OP['*'] = new Operator('*', function (x,y){return x*y;}, P_BMD); 117: LS_OP['/'] = new Operator('/', function (x,y){return x/y;}, P_BMD); 118: LS_OP['%'] = new Operator('%', function (x,y){return x%y;}, P_BMD) 119: LS_OP['<<'] = new Operator('<<', function(x,y){return x*Math.pow(2,y);}, P_POW); 120: LS_OP['>>'] = new Operator('>>', function(x,y){return x/Math.pow(2,y);}, P_POW); 121: LS_OP['@+'] = new Operator('@+', function (x){return x;}, P_UPM); 122: LS_OP['@-'] = new Operator('@-', function (x){return -1*x;}, P_UPM); 123: LS_OP['**'] = new Operator('**', Math.pow, P_POW); 124: LS_OP['^'] = LS_OP['**']; 125: LS_OP['exp']= new Operator('exp', Math.exp, P_FUN); 126: LS_OP['log']= new Operator('log', Math.log, P_FUN); 127: LS_OP['log10']= new Operator('log10', function(x){return Math.log(x)/Math.log(10)}, P_FUN); 128: LS_OP['sin']= new Operator('sin', Math.sin, P_FUN); 129: LS_OP['cos']= new Operator('cos', Math.cos, P_FUN); 130: LS_OP['tan']= new Operator('tan', Math.tan, P_FUN); 131: LS_OP['asin']= new Operator('asin', Math.asin, P_FUN); 132: LS_OP['acos']= new Operator('acos', Math.acos, P_FUN); 133: LS_OP['atan']= new Operator('atan', Math.atan, P_FUN); 134: LS_OP['!']= new Operator('!', fact, P_LHB); 135: LS_OP['P']= new Operator('P', permutation, P_BMD); 136: LS_OP['C']= new Operator('C', combination, P_BMD); 137: LS_OP['pi']= new Operator('pi', Math.PI, P_CST); 138: LS_OP['e']= new Operator('e', Math.E, P_CST); 139: 140: 141: 142: function add_backslash(s) 143: /** 144: * 必要に応じてバックスラッシュを挿入します。 145: * **/ 146: { 147: var s1=''; 148: var ls=s.split(''); 149: for (var i=0, n=ls.length; i<n; ++i){ 150: var c=ls[i]; 151: s1+= (c.match(/W/)? '\' : '') + c; 152: } 153: return s1; 154: } 155: 156: 157: function make_regstr_each(s0) 158: /** 159: * Operator から正規表現用の文字列を作ります 160: * **/ 161: { 162: var op=LS_OP[s0]; 163: return add_backslash(s0) + 164: (op.is_const() ? '(?=\W|$)' : 165: op.is_func() ? '(?=\s|\()' : ''); 166: } 167: 168: 169: 170: 171: 172: 173: // 式の要素の正規表現, 演算子のリストから自動生成。無名関数を作ってすぐに呼び出す。 174: ROBJ= function() 175: { 176: var ls= new Array(); 177: for (var s in LS_OP) 178: ls.push(s); 179: ls.sort(function(x,y){return y.length - x.length}) 180: 181: return new RegExp('(\()|(\d+(?:\.\d+)?)|(' + ls.map(make_regstr_each).join('|') + ')' ); 182: }(); 183: 184: 185: 186: function find_corresponding(s0) 187: /** 188: * 対応する閉じカッコの位置を返す 189: * **/ 190: { 191: var count=0; 192: for (var i=0, n=s0.length; i<n; ++i){ 193: var c=s0.charAt(i); 194: if(c=='(') ++count; 195: else if(c==')') --count; 196: if(count==0) return i; 197: } 198: //return -1; 199: throw Error('cannot parse input'); 200: } 201: 202: 203: 204: function parse(s0) 205: /** 206: * 数式の文字列を解析して数値と演算子からなるリストを返す 207: * **/ 208: { 209: var ls=[]; 210: 211: while(1){ 212: s0=s0.replace(/^s+|s+$/, ''); 213: if (! s0) break; 214: var mobj=s0.match(ROBJ); 215: if (! mobj) throw Error('cannot parse input'); 216: if(mobj[1]){ 217: var idx=find_corresponding(s0); 218: // print (idx); 219: ls.push(parse(s0.slice(1, idx))); 220: s0=s0.substring(idx+1); 221: }else{ 222: if(mobj[2]) 223: ls.push(parseFloat(mobj[2])); 224: else{ 225: var sop=mobj[3]; 226: if ((sop=='+' || sop=='-') && 227: (ls.length==0 || 228: (ls[ls.length-1].constructor==Operator && 229: ! ls[ls.length-1].is_lhb()))) 230: sop='@'+sop; 231: 232: ls.push(LS_OP[sop]); 233: } 234: s0 = s0.substring(mobj[0].length); 235: } 236: } 237: return ls; 238: } 239: 240: 241: 242: function find_op(ls) 243: /** 244: * 次に計算する演算子の位置を返す 245: * **/ 246: { 247: var op=null; 248: var idx=-1; 249: for(var i=0, n=ls.length; i<n; ++i){ 250: var item=ls[i]; 251: if(item.constructor == Operator && item.is_higher(op)){ 252: idx=i; 253: op=item; 254: } 255: } 256: return idx; 257: } 258: 259: 260: function append_result(ls_before, result, ls_after) 261: { 262: ls_before.push(result); 263: return ls_before.concat(ls_after); 264: } 265: 266: 267: function calc(ls) 268: /** 269: * ls を計算して結果を返す 270: * **/ 271: { 272: switch(ls.constructor){ 273: case Number: return ls; 274: case Operator: return ls.call(); 275: case Array: 276: if(ls.length==1) 277: return calc(ls[0]); 278: else if(ls.length>1){ 279: var op_idx=find_op(ls); 280: var op=ls[op_idx]; 281: if(op.is_const()) 282: return calc(append_result(ls.slice(0,op_idx), op.call(), ls.slice(op_idx+1))); 283: 284: else if(op.is_lhb() && op_idx > 0) 285: return calc(append_result(ls.slice(0,op_idx-1), 286: op.call(calc(ls[op_idx-1])), 287: ls.slice(op_idx+1))); 288: 289: else if(op.is_unary() && op_idx < ls.length-1) 290: return calc(append_result(ls.slice(0,op_idx), 291: op.call(calc(ls[op_idx+1])), 292: ls.slice(op_idx+2))); 293: 294: else if(op_idx>0 && op_idx<ls.length-1) 295: return calc(append_result(ls.slice(0,op_idx-1), 296: op.call(calc(ls[op_idx-1]), calc(ls[op_idx+1])), 297: ls.slice(op_idx+2))); 298: else 299: throw Error('cannot calculate!'); 300: } 301: default: 302: throw Error('funny'); 303: } 304: } 305: 306: 307: 308: function calculate() 309: /** 310: * 文字列を電卓風に eval して返します 311: * **/ 312: { 313: var ans_el=document.getElementById('answer'); 314: try{ 315: var ans_val=calc(parse(document.form1.edit1.value)); 316: }catch(e){ 317: var ans_val="式が不正です"; 318: } 319: var ans_node=document.createTextNode(ans_val.toString()); 320: if(ans_el.firstChild) 321: ans_el.replaceChild(ans_node, ans_el.firstChild); 322: else 323: ans_el.appendChild(ans_node); 324: 325: // submit 処理を途中で中断するため 326: return false; 327: } 328: 329: 330: function show_operators() 331: /** 332: * 演算子や定数の一覧を出力します 333: * **/ 334: { 335: var ls= new Array(); 336: for (var s in LS_OP) ls.push(s); 337: document.write( ls.join(', ')); 338: }[calc.html]
000: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 001: 002: <html lang="ja"> 003: <head> 004: <meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS"> 005: <link rel="icon" href="../images/shido.png" type="image/png"> 006: <script type="text/javascript" src="calc.js"> 007: </script> 008: <title>JavaScript 電卓</title> 009: </head> 010: <body style='margin:30pt'> 011: 012: <h1 style='font-size:120%'>JavaScript 電卓</h1> 013: 014: <form name='form1' onsubmit='return calculate()'> 015: <table> 016: <tr> 017: <td> 018: <input type='text' size=40 name='edit1' id='edit' style='font-size:120%;font-family:monospace'> 019: </td> 020: <td> 021: <span> 022: <input type="button" value="=" onclick='calculate()'> 023: </span> 024: </td> 025: <td> 026: <span style='margin-left:15pt;' id='answer' style='font-size:120%;font-family:monospace'> </span> 027: </td> 028: </tr> 029: </table> 030: </form> 031: <div style='margin-top:20pt'> 032: <pre> 033: 使える演算子: 034: <script type="text/javascript"> 035: show_operators(); 036: </script> 037: </pre> 038: </div> 039: </body></html> 040: 041:
![]() |
![]() |
![]() |