分数の表示

分数表示をするには、分母と分子の最大公約数での約分が必要です。ユークリッド互除法という古くからある解法を用いることで、javascript で分数の表示を実装することができます。今のところ、係数入力には整数しか対応していません。

以下、参照  JavaScript 第6版 より

function memoize(f) {
  var cache = {};
  return function() {
    var key = arguments.length + Array.prototype.join.call(arguments,",");
    if (key in cache) return cache[key];
    else return cache[key] = f.apply(this, arguments);
  };
}
function gcd(m,n) {
  var t;
  if (m < n) t=n, n=m, m=t;
  while(n != 0) t=n, n = m%n, m=t;
    return m;
}
function output() {
  var gcdmemo = memoize(gcd);
  var a1= document.getElementById('a').value;
  var b1= document.getElementById('b').value;
  var c1= document.getElementById('c').value;
  var d1= document.getElementById('d').value;
  var p1= document.getElementById('p').value;
  var q1= document.getElementById('q').value;
  var x1= d1*p1-b1*q1, y1 = a1*q1-c1*p1;
  var z = a1*d1-b1*c1, z1 = Math.abs(z);
  if(z1 == 0) {
    document.getElementById('x').innerHTML = "x = " + a1 + 
        "x + " + b1 + "y = " + p1 + " のすべての解です。";
    document.getElementById('y').innerHTML = "y = " + a1 + 
        "x + " + b1 + "y = " + p1 + " のすべての解です。";
    return false;
  }
  if(Math.abs(gcdmemo(x1,z1)) == z1) {
    document.getElementById('x').innerHTML = "x = " + 
        x1/gcdmemo(x1,z);
    if(Math.abs(gcdmemo(y1,z1)) == z1) {
      document.getElementById('y').innerHTML = "y = " + 
        y1/gcdmemo(y1,z);
    }
    else {
      if(z/gcdmemo(y1,z) > 0) {
        document.getElementById('y').innerHTML = "y = " + 
        y1/gcdmemo(y1,z) + "/" + z/gcdmemo(y1,z);
      }
      else {
      document.getElementById('y').innerHTML = "y = " +
         (-1)*y1/gcdmemo(y1,z) + "/" + Math.abs(z/gcdmemo(y1,z));
      }
    }
  }
  else {
    if(z/gcdmemo(x1,z) > 0) {
      document.getElementById('x').innerHTML = "x = " + 
        x1/gcdmemo(x1,z) + "/" + z/gcdmemo(x1,z);
    }
    else {
      document.getElementById('x').innerHTML = "x = " + 
        (-1)*x1/gcdmemo(x1,z) + "/" + Math.abs(z/gcdmemo(x1,z));
    }
    if(Math.abs(gcdmemo(y1,z1)) == z1) {
      document.getElementById('y').innerHTML = "y = " + 
        y1/gcdmemo(y1,z);
    }
    else {
      if(z/gcdmemo(y1,z) > 0) {
        document.getElementById('y').innerHTML = "y = " + 
        y1/gcdmemo(y1,z) + "/" + z/gcdmemo(y1,z);
      }
      else {
        document.getElementById('y').innerHTML = "y = " + 
        (-1)*y1/gcdmemo(y1,z) + "/" + Math.abs(z/gcdmemo(y1,z));
      }
    }
  }
}


追記)分母の符号処理にバグを発見しました。謹んで訂正致します。しかしながら依然、分子か分母またそのいずれもマイナス値だった時のユークリッド互除法の符号処理に疑問があります。

連立1次方程式

いきなり n元1次方程式を解こうとすると、n次行列の逆行列とかLU分解法など、線形代数を本格的にやるハメになるので中学レベルから始めます。

参考URL: 連立方程式 – 高精度計算サイト

function output() {
  var a1= document.getElementById('a').value;
  var b1= document.getElementById('b').value;
  var c1= document.getElementById('c').value;
  var d1= document.getElementById('d').value;
  var p1= document.getElementById('p').value;
  var q1= document.getElementById('q').value;
  var x1= d1*p1-b1*q1, y1 = a1*q1-c1*p1;
  var z = a1*d1-b1*c1, z1 = Math.abs(z);
  if(z1 == 0) {
    document.getElementById('x').innerHTML = "x = " + a1 + "x + " + b1 + "y = " + p1 + " のすべての解です。";
    document.getElementById('y').innerHTML = "y = " + a1 + "x + " + b1 + "y = " + p1 + " のすべての解です。";
    return false;
  }
  else {
    document.getElementById('x').innerHTML = "x = " + x1/z;
    document.getElementById('y').innerHTML = "y = " + y1/z;
    return false;
  }
}

今は、結果を分数で表示できないか試行錯誤中です。

jQuery の記述

jQueryを使ったスライダーの作り方とその仕組み

のコピペでスミマセン。センセンシャル。

jQuery はDOMが操作可能になるまで待機させるために、ready()メソッドに入れ子で記述すると良いとのこと。これだけは伝えたかった。

<script type="text/javascript">
$(document).ready(function() {
・・・(jQueryの記述
});
</script>

https://stuffy.mydns.jp/~gusachan/javascript/slider.html

JavaScript BMIの判定

久々に JavaScript に触ってみましたが、onClick が効かなくて焦りました。コードのタイプミスが原因でした。他にもHTMLタグのid が予約語や変数名と被るとかいった事でもダメみたいですね。

https://stuffy.mydns.jp/~gusachan/javascript/BMI.html

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<script type="text/javascript">
function calcBMI() {
  var shincho = document.getElementById("height");
  var tiju = document.getElementById("weight");
  var hantei = document.getElementById("result");
  var h = shincho.value/100, w = tiju.value;
  var bmi = w/h/h;
  if (bmi < 18.5) {
    hantei.innerHTML = "痩せすぎ";
  }
  else if (bmi < 25) {
    hantei.innerHTML = "普通";
  }
  else if (bmi < 30) {
    hantei.innerHTML = "肥満度1";
  }
  else if (bmi < 35) {
    hantei.innerHTML = "肥満度2";
  }
  else if (bmi <40) {
    hantei.innerHTML = "肥満度3";
  }
  else if (bmi >= 40) {
    hantei.innerHTML = "肥満度4";
  }
}
</script>
</head>
<body>
<form>身長を入力してください。<input type="text" size="4" id="height"> cm<br>
体重を入力してください。<input type="text" size="4" id="weight"> kg<br>
<input type="button" value="計算" onClick="calcBMI()"></form>
判定:<div id="result"></div>
</body>
</html>

タイマー制御

intervalID = setInterval(function, milliseconds) メソッドで繰り返される関数の呼び出しを止めるには、clearInterval(id)に戻り値を指定します。例えば、要素の属性値onclick に

onclick=”clearInterval(intervalID)”

JS のコードに関数を指定して呼び出す方法もありますが入れ子になっていると上手く行かないこともあります。

https://stuffy.mydns.jp/~gusachan/javascript/stopwatch.html

NaNの真実

未定義値の変数を計算、出力しようとすると、NaN(Not a Number)がでます。

typeof NaN === 'Number'   //true (NaNはNumberオブジェクト)
NaN === NaN            //false (NaNは自分自身ではない?)
 NaN ===!NaN          //true (NaNはNaNではないが正解?)
var arg;
var arm = function() {
    arg += 6;
  return arg;
}
alert(arm());

未定義値を代入、参照すると

var arg;
var arm = function() {
    arg += 6;
  return arg;
}
alert(arg);

未定義値(グローバル変数の参照)は、

ドット記法

2.3 オブジェクトのプロパティは、ドット記法もしくはブラケット記法で取得・設定・更新することができます。

// frank オブジェクトを生成

var frank = new Object();
frank.living = true;
frank.age = 56;
frank.gender = 'male';
frank.location = 'Hollywood';
frank.getGender = function() { return frank.gender; };

// プロパティを取得

var str = frank.living + "\n" + frank.age + "\n" + frank.location + "\n" + frank.getGender()
alert(str);

// プロパティを更新

frank.living = false;
frank.age = 99;
frank.gender = 'female';
frank.location = 'Newyork';
frank.getGender = function() { return 'Gender = ' + frank.gender; };
var str = frank.living + "\n" + frank.age + "\n" + frank.location + "\n" + frank.getGender()
 alert(str);

変数の宣言とエラー

今回はJavaScript 第6版』David Flanagan (著),    村上 列 (翻訳)  から予約語、var の省略、値の代入前の変数の参照、変数の上書きなど初歩的なJavaScript の挙動について抜粋しました。(var の語源はvariableからだそうです。)

2.4.1 予約語
JavaScript には予約語がたくさんあります。以下に挙げるような単語は、プログラム中で識別子として使うことはできません。

break delete function return typeof
case do if switch var
catch else in this void
continue false instanceof throw while
debugger finally new true with
default for null try

さらに、以下の単語は、通常のJavaScriptコードでは識別子として使えますが、strictモードの場合は予約語として扱われ識別子には使えません。

implement let private public yield
interface package protected static

JavaScript には、たくさんのグローバル変数や関数があらかじめ定義されています。以下のような定義済みのグローバル変数やグローバル関数と同じ名前を識別子に使うことは避けた方が無難です。

argument encodeURI Infinity Number RegExp
Array encodeURIComponent isFinite Object String
Boolean Error isNaN parseFloat SyntaxError
Date eval JSON parseInt TypeError
decodeURI EvalError Math RangeError undefined
decodeURIComponent function NaN ReferenceError URIError

3.9 変数の宣言

JavaScript で変数の宣言をするには、キーワード var を使います。

var i;
var sum;

複数の変数をカンマ(,)で区切って宣言することもできます。

var i, sum;

for ループやfor/in ループでvar 文を記述することで、ループの一部としてループ変数を宣言することもできます。

for (var i = 0; i < 10; i++) {
  var sum += i;
}
return sum;

もしvar 文で変数の初期値を設定しなかった場合は、宣言された変数は、何か値が代入されるまで undefined(未定義値)になります。

3.9.1 宣言の繰り返しと省略

var 文で宣言されていない変数から値を読みだした場合は、エラーになります。ECMAScript 5 の strict モードでは、宣言されていない変数に対して値を代入してもエラーになります。しかし、歴史的には、つまり非 strict モードでは、宣言されていない変数に対して値を代入した場合、JavaScript はグローバルオブジェクトのプロパティとして変数を作成します。ただし、完全に同じというわけではありません。~(中略)~ つまり、グローバル変数を宣言しなくても問題ないということです。ただし、このようなプログラミングスタイルは決してよいものとは言えません。変数は var を使って宣言するようにしてください。

3.10 変数のスコープ

プログラムのソースコード中における変数の有効範囲をスコープといいます。グローバル変数のスコープは、プログラム全体です。ローカル変数のスコープは、その変数が宣言された関数の中だけに限定されます。関数の中にグローバル変数と同じ名前のローカル変数があった場合は、ローカル変数が優先されます。つまり、グローバル変数と同じ名前でローカル変数や仮引数を宣言すると、グローバル変数が隠されます。

var scope ="global";          // グローバル変数を宣言する。
function checkscope() {
  var scope = "local";        // 同じ名前でローカル変数を宣言する。
  return scope;          // ローカル変数の値が返される。
}
checkscope()                  // => "local"
scope = "global";        // var 文を省略してグローバル変数を宣言する。
function checkscope2() {
  scope = "local";   // あれ!これはグローバル変数を変更したことになる。
  myscope = "local"; // 代入で新たなグローバル変数が暗黙的に宣言。      
  return { scope, myscope }    // 2つの値を返す。
}
checkscope2()                // => {"local","local"}
scope                        // => "local": グローバル変数が変更された。
myscope                // => "local": グローバルな名前空間に追加された。

4.13 関数の定義方法(宣言、式、もしくはコンストラクタ)

// 関数コンストラクタ:最後のパラメータには関数のロジック(関数本体)が入る

var addConstructor = new Function('x',  'y', 'return x + y');

// 関数宣言(function 文)

function addStatement(x, y) {
  return x + y;
}

// 関数式

var addExpression = function(x, y) {
  return x + y;
};

次の例では、foo プロパティをグローバルオブジェクトに格納し、異なるスコープからfoo プロパティにアクセスします。

var foo = 'bar';  // foo はグローバルオブジェクト直下に格納されたグローバル変数
var myApp = function() {  // 関数はスコープを作ることを思い出して
  var run = function() {
    alert(foo);  // 出力: 'bar'
}();
}
myApp();

アナログ時計2

前回のアナログ時計は文字盤が無く、時刻も分かりにくかったのでcanvasに目盛りと文字を追加してみました。

目盛りは、hand関数を利用して円の中心から放射状に60本、幅3px と1px の針を引いて、一回り(10px)小さい半径の円を白で塗りつぶして、らしく見えてきました。時刻を表す文字は、ギリシア数字で適当に配置しています。

https://stuffy.mydns.jp/~gusachan/javascript/clock4.html

参考URL: http://honttoni.blog74.fc2.com/blog-entry-195.html

document.write とinnerHTMLの違い

サンプルは、サイトのリファラをdocument.referrer から取得するスクリプトです。

if (document.referrer.indexOf("http://www.google.co.jp/search?") == 0 {
  var args = document.referrer.substring(ref.indexOf("?")+1).split("&");
  for (var i = 0; i < args.length; i++) {
    if (args[i].substring(0,2) == "q=" ) {
      document.write("<p>Google検索からようこそ!");
      document.write("検索キーワードは:" + 
                 unescape(args[i].substring(2)).replace('+', ' ');
    break;
    }
  }
}

15.10.2

JavaScript の document.write メソッドを使って現在のドキュメントに HTML を出力できるのは、ドキュメントの解析中の間なので注意してください。<scirpt>要素の中のトップレベルのコードから document.write() が呼び出せるのは、このスクリプトがドキュメントの解析の一部として実行されるからです。関数中で document.write() を呼び出すように定義し、イベントハンドラからこの関数を呼び出した場合、予想外の動作をします。このようなメソッドの呼び出しでは、現在のドキュメントが消去され、このドキュメントに含まれていたスクリプト自体も消去されます。~(中略)~ document.write() メソッドは、最近のコードではあまり使われていません。innerHTMLプロパティなどの DOM 機能のほうが、ドキュメントにコンテンツを追加するには便利だからです。

15.5.1

Element の  innerHTML プロパティを読みだせば、マークアップを含んだ文字列形式で、要素のコンテンツが返されます。要素の innerHTML プロパティ に値を設定すると、 Web ブラウザのパーサーが呼び出され、要素の現在のコンテンツは、新たに設定した文字列に対応する表現で上書きされます。名前は、 innerHTML ですが、 innerHTML は HTML 要素だけでなく XML 要素も使えます。
Web ブラウザは、HTML の解釈が得意です。このため、 innerHTML に対して解釈が必要となる値を指定したとしても、かなり効率的に処理されれるのが普通です。ただし、+= 演算子を使って、テキストを少しずつ何度も innerHTML に追加していくことはあまり効率的ではありません。シリアライズ処理と解釈処理を何度も行うことになるからです。

document.write()メソッドも.innerHTMLプロパティも一長一短がありますが、innerHTML の方が融通が利いて危険性も少ないということでしょうか。最新のブラウザではすべて実装されていているそうです。