アナログ時計

コードは以下。ポイントは、時刻を取得するvar now = new date(); をローカル関数のdgr() とdraw() の2か所で宣言、hand(d, r, w) 関数で針の角度と長さと太さの引数を渡して、短針と長針、秒針を描き分けています。それと前回の回転関数から x → 90-d と回転角を変換しています。setInterval(draw,1000) で1秒ごとに再描画しています。

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

var c=document.getElementById("clock");
var ctx=c.getContext("2d");
window.onload = function() {
  var rads =  function(x) {
    return Math.PI*x/180;
  }
  var coordinate = function () {
    ctx.beginPath();
    ctx.lineWidth = 1;
    ctx.arc(200,200,150,0,rads(360),false);
    ctx.stroke();
  }
  var hand = function(d,r,w) {
    ctx.beginPath();
    ctx.moveTo(200,200);
    ctx.lineTo(200 + r*Math.cos(rads(90-d)), 200 - r*Math.sin(rads(90-d)));
    ctx.closePath();
    ctx.lineWidth = w;
    ctx.stroke();
  }
  var dgr = function(needle) {
      var now = new Date(); 
      if (needle == 'hours') {
        var arg = 30*((now.getHours()%12) + (now.getMinutes()/60));
      }
      else if (needle =='minutes') {
        var arg = 6*now.getMinutes();
      }
      else if (needle =='seconds') {
        var arg = 6*now.getSeconds();
      }
      return arg;
  }
  function draw() {
    var now = new Date();   // (追記:ここの宣言はなくても動いた
    ctx.clearRect(0,0,400,400);
    hand(dgr('hours'),110,4);
    hand(dgr('minutes'),140,2);
    hand(dgr('seconds'),130,1);
    coordinate();
  }
  setInterval(draw,1000);
}

clock

ひかりTVのチューナー交換

先週、ひかりTVのチューナーを交換しました。古いチューナー(PM-700)で録画したUSB-HDDを新しいチューナー(ST-3200)に接続すると、「引き継ぎ処理実行中です。処理完了までこのまましばらくお待ちください。」の画面のまま1時間たっても終わりません。電源を切ろうとしても切れないので、USBケーブルを引き抜いたところ、正常終了しました。恐る恐る、USBケーブルを挿したところ今度はUSB-HDDを認識したようでした。録画した番組を確認したところデータも読み取り可能なようで一安心です。(汗

値の変換

JavaScript では、Number()で数値以外の文字列を数値に変換しようとすると、関数はNaNを返します。NaNをtypeof演算子で判定しようとしても’number’と判定されて上手く行きません。テキストボックスから入力された値をgetElementByIdで受け取るときに、数値か文字列かを判定するには isFinite関数を使って

if (isFinite(arg)) {
  (処理)
}
else {
  alert('半角数値を入力してください!');
  return false;
}

と例外処理します。

変換先
文字列 数値 論理値 オブジェクト
undifined
null
“undifined”
“null”
NaN
0
false
false
TypeError 例外
TypeError 例外
true
false
“true”
“false”
1
0
new Boolean(true)
new Boolean(false)
“”(空文字列)
“1.2”(数値文字列)
“one”
(数値以外の文字列)
0
1.2
NaN
false
true
true
new String(“”)
new String(“1.2”)
new String(“one”)
0
-0
NaN
Infinity
-Infinity
1(有限値, 0以外)
“0”
“0”
“NaN”
“Infinity”
“-Infinity”
“1”
false
false
false
true
true
true
new Number(0)
new Number(-0)
new Number(NaN)
new Number(Infinity)
new Number(-Infinity)
new Number(1)

追記)JavaScript Good Parts 「良いパーツによるベストプラクティス」に興味深い記述がありましたので転載します。
JavaScript は、isNaN 関数を提供しており、これを使えば数値とNaN を区別することができる。

isNaN(NaN)  // true
isNaN(0)    // false
isNaN(‘oops’)  // true
isNaN(‘0’)   // false

isFinite 関数は値が利用可能な数値であるかを調べるもっともよい方法だ。なぜならこの関数は、NaN と Infinity をどちらも false と判断してくれるからである。しかし、残念なことに、isFinite は与えられたオペランドをまず数値に変換しようとしてしまう。したがって調べたい値が数値型ではなかった場合には、これは良いテスト方法とは言えない。もしかしたらあなたは、isNumber 関数を自分で定義したくなるかもしれない。

var isNumber = function isNumber(value) {
  return typeof value === 'number' && isFinite(value);
};

回転の実装

前回の続きです。回転を実装するにあたり、kaiten関数でcanvas をclearRect してテキストボックスに入力した数値を配列にメモ、var draw = function(x)を呼び出して再描画しています。

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

<body>
<th>rotate 関数</th><br>
<canvas id="graph" width="400" height="400"> </canvas><br>
<form>回転角度: <input type="text" id='degree'> <button type='button' onclick="kaiten();">回転</button></form>
<script type="text/javascript">
var i = 0, dgr = [0];
var c=document.getElementById("graph");
var ctx=c.getContext("2d"); 

function rads(x) {
  return Math.PI*x/180;
}
function coordinate() {
  ctx.beginPath();
  ctx.moveTo(0,200);
  ctx.lineTo(400,200);
  ctx.moveTo(200,0);
  ctx.lineTo(200,400);
  ctx.closePath();
  ctx.stroke();
  ctx.beginPath();
  ctx.arc(200,200,150,0,rads(360),false);
  ctx.stroke();
}
var draw = function(x) {
  ctx.beginPath();
  ctx.moveTo(200,200);
  ctx.lineTo(200 + 150*Math.cos(rads(x)), 200 - 150*Math.sin(rads(x)));
  ctx.closePath();
  ctx.stroke();
};
var sum = function(arr) {
  var sum = 0;
  arr.forEach(function(elm) {
    sum += elm;
  });
  return sum;
};
function kaiten() {
  var e = document.getElementById('degree');
  var arg = Number(e.value);
  i += 1;
  dgr[i] = arg;
  ctx.clearRect(0,0,400,400);
  coordinate();
  draw(sum(dgr));
}
coordinate();
</script>
</body>

追記)

var c=document.getElementById("graph");
var ctx=c.getContext("2d");

上記のコンストラクタ変数オブジェクト宣言は、coordinate, draw 関数のローカル内で宣言していましたが、グローバルオブジェクト(正確にはwindowオブジェクトのプロパティ)として宣言しても問題なかったようです。

canvas への描画

時間切れで回転を実装できなかったヘタレです。コードは以下。

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <meta http-equiv="Content-Style-Type" content="text/css" />
  <meta http-equiv="Content-Script-Type" content="text/javascript" />
<style type="text/css">
  canvas {border: 1px solid #999; }
</style>
</head><style type="text/css">
  canvas {border: 1px solid #999; }
</style>
</head>
<body>
<th>rotate 関数</th><br>
<canvas id="graph" width="400" height="400"> </canvas><br>
<form>回転角度: <input type="text" id='degree'> <button type='button' onclick="draw();">回転</button></form>
<script type="text/javascript">
function rads(x) {
  return Math.PI*x/180;
}
function draw(){
  var e = document.getElementById('degree');
  var c=document.getElementById("graph");
  var ctx=c.getContext("2d");
  ctx.beginPath();
  ctx.moveTo(200,200);
  ctx.lineTo(200 + 150*Math.cos(rads(e.value)), 200 - 150*Math.sin(rads(e.value)));
  ctx.closePath();
  ctx.stroke();
}
var c=document.getElementById("graph");
var ctx=c.getContext("2d");
  ctx.beginPath();
  ctx.moveTo(0,200);
  ctx.lineTo(400,200);
  ctx.moveTo(200,0);
  ctx.lineTo(200,400);
  ctx.closePath();
  ctx.stroke();
  ctx.beginPath();
  ctx.arc(200,200,150,0,rads(360),false);
  ctx.stroke();
</script>
</body>
</html>

rotate

SCRIPTタグをBODYタグの中に入れるのがポイントです。

最終話(正常終了)ネタバレ注意!

パーソンオブインタレスト(シーズン5)第12話(実行)・前回のあらすじ

サマリタンにウイルス(ICE-Nine)を仕掛けるためにNSAに侵入したフィンチ。厳重な警備の中、サーバーにウイルスをアップロードすることに成功したが、起動のために音声パスワードを入力する前に捕らえられてしまう。人類の進化のため、選民思想と世界支配を掲げるサマリタンを前に、ブロアと共に密閉空間に閉じ込められ消火設備によって窒息死するところを、ジョンとサミーンはNSAのネットワークに接続できるデジタルデバイスの入手に成功し、マシンからドアのパスワードを得て部屋から脱出したフィンチは、ウイルスの起動に成功する。しかし、それはマシンの死をも意味する。

今週放送された最終話(正常終了)は、連邦準備銀行の地下金庫に保管されたサマリタンのバックアップの起動を阻止するお話。状況は事前のシミュレーションでもマシンの圧倒的不利な予想でしたが、結末は如何に。「誰もが独りで死ぬ。でも、何かを残せたら、助けた人や愛した人が、誰か一人でもその人を覚ええていたのなら人は、 本当には死なない・・・。」のセリフが心に残ります。

javascriptのクロージャ

メモ化された fibonacci.js

var fibonacci = function () {
    var memo = [0, 1];
    var fib = function (n) {
      var result = memo[n];
      if (typeof result !== 'number') {
        result = fib(n -1) + fib(n -2);
        memo[n] = result;
      };
      return result;
    };
    return fib;
}();
for (var i = 0; i <=100; i +=1) {
    document.writeln('// ' + i + ': ' + fibonacci(i) +'<br>');
}

入れ子状になった関数の戻り値を、配列やオブジェクトに記録(メモ化)すると、再帰的な関数の呼び出し回数を大幅に減らすことができます。メモ化についてはクロージャを使ってメモ化 – maru source  で、フィボナッチ数列を例に詳しく解説されていますので割愛して、今回は関数のクロージャについていまいち良く分からなかったので調べてみました。

8.6 クロージャ

最近の多くのプログラム言語と同じように、JavaScript では構文スコープを使います。つまり、関数を実行するときには、関数が定義されたときに有効であった変数スコープを使います。関数が呼び出されたときに有効な変数スコープではありません。(中略) 関数オブジェクトと、関数の変数の名前解決に使われるスコープを組み合わせたものを、コンピュータサイエンスの分野ではクロージャと呼んでいます。
正確に言えば、JavaScript の関数はすべてクロージャです。

ここまでは何を言っているのか良く解りませんね。実際にコードを見てみましょう。
クロージャを理解するためには、まず入れ子型関数の構文スコープについて考えてみましょう。

var scope = "global scope";        // グローバル変数。
function checkscope() {
    var scope = "local scope";        // ローカル変数。
    function f() { return scope; }  // ここでのスコープ中の値が戻される。
    return f();
}
checkscope()                        // => "local scope"

checkscope()関数は、ローカル変数を宣言し、その後、この変数の値を返す関数を定義し呼び出されます。checkscope() を呼び出したときに、”local scope” が戻されることに疑問を抱かないと思います。ここで、このコードを少し変更してみましょう。次のコードでは何が返されるかわかりますか。

var scope = "global scope";        // グローバル変数。
function checkscope() {
    var scope = "local scope";        // ローカル変数。
    function f() { return scope; }  // ここでのスコープ中の値が戻される。
    return f;
}
checkscope()()                        // 何が返されるか?

今回のコードでは括弧がcheckscope()から外に移動しただけです。入れ子型の関数を呼び出して戻りを返すのではなく、入れ子型の関数オブジェクト自身を返しています。括弧をコードの最後の行に動かすことで、入れ子型の関数を定義した場所の外側で呼び出した場合、どうなるでしょう。構文スコープの基本となる規則を思い出してください。JavaScript の関数は、定義されたときに有効なスコープチェーンを使って、実行される、という規則です。入れ子型の関数 f() は、変数 scope の値が “local scope” にバインド(束縛)されているスコープチェーンの下で定義されました。この変数バインドは、 f を実行するときも有効です。どこで呼び出されたとしても、これは変わりません。したがって、先のコードの最後の行が返すのは  “local scope” です。”global scope” ではありません。

先の fibonacci.js の例では入れ子になったローカル 関数、 fib = function (n) がfibonacci(i)で呼び出されて、ローカル変数が返されたというわけです。();

JSONのコールバック関数

配列とオブジェクトの入れ子になったJSON形式を、var json={…}; として試みに以下のコールバック関数を用いて再帰的に読み込ませると、10MB程度のデータ(例えばMTG JSON)でタイムアウト、スタックオーバーフローを起こす件。

【JavaScript】ネストされたJSONのすべての要素にアクセスする

var walkJSON = function walkJSON(data, callback){
     for (var key in data) {
         callback(key, data[key]);
         if (typeof data[key] === "object") {
             walkJSON(data[key], callback);
         }
     }
 }
   walkJSON(json, function(key, value) {
     document.writeln(key + ":" + value);
     document.writeln("<br>");
  });

これはjavascript がブラウザのプログラムである意味、仕様なのかもしれませんが本来は、XMLHttpRequest するべきなのでしょうね、とほほ。

javascriptの2次元配列の生成

JavaScriptではネイティブオブジェクトに対してメソッドを追加して、2次元配列を定義することができます。(ここではArrayオブジェクトのプロトタイプに対して。)

『JavaScript では2次元以上の配列は用意されていない。しかし、C言語とほぼ同じ方法で、配列の配列を作ることができる。』(JavaScript: The Good Parts 良いパーツによるベストプラクティス p.73)

Array.matrix = function (m, n, initial) {
    var a, i, j, mat = [];
      for (i=0; i < m; i += 1) {
        a = [];
        for (j = 0; j < n; j += 1) {
          a[j] = initial;
        }
        mat[i] = a;
      }
      return mat;
}
// ゼロで埋められた 4 X 4 の行列を生成する

var myMatrix = Array.matrix(4, 4, 0);

// 単位行列を生成するメソッド

Array.identity = function (n) {
    var i, mat = Array.matrix(n, n, 0);
    for (i=0; i < n; i += 1) {
      mat[i][i] = 1;
    }
    return mat;
};
myMatrix = Array.identity(4);

配列の出力メソッドがありませんでしたがせっかくなので、自作してみました。

// 生成された行列を書き出すメソッド
Array.output = function (n) {
  var i,j, mat = Array.identity(n);
    for (i=0; i < n; i += 1) {
      for (j=0; j < n; j += 1) {
        document.writeln(myMatrix[i][j]);
      }
      document.writeln("<br>");
    }
    return mat;
};
printMatrix = Array.output(4);

以上のコードは、概ね期待した結果が得られました。

sedコマンド

今回は、以前のエントリーで「ゾンビ探しのスクリプト」をご紹介しましたがその時には理解できなかった、sed コマンドを取り上げます。

sed -e "s/foo/bar/g" ファイル名

意味は、ファイル[ファイル名]の中の文字列を次の条件式”s/foo/bar/g”で、foo をbar に書き換えるということで、

sed 's/^ \{1,\}//g'

の場合は、PID出力の中の先頭にマッチ^する半角スペース の1回以上の繰り返し{1,}をすべて削除する//、という意味になります。

前回のエントリーで、Wordpress のメディアライブラリを表示するadmin-ajax.phpファイルを書き換えるシェルスクリプトは、-i でファイルの上書き保存とバックアップを作るオプションを使って、

sed -i".bak" -e "s/foo/bar/g" admin-ajax.php

実際には、foo とbar に前回のコードを代入して「/(スラッシュ)」と「;(セミコロン)」と「$(ダラー)」をエスケープして、「\n(改行コード)」を追加して

sed -i".bak" -e "s/@header( 'Content-Type: text\/html\; charset=' . get_option( 'blog_charset' ) )\;/if(in_array(\$_POST['action'], array('query-attachments', 'send-attachment-to-editor'))){\n@header('Content-Type: application\/json\; charset=' . get_option('blog_charset'))\;\n}else{\n@header( 'Content-Type: text\/html\; charset=' . get_option( 'blog_charset' ) )\;\n}/g" admin-ajax.php

となります。

参考URL:https://open-groove.net/linux-command/sed-edit-files/
http://qiita.com/takech9203/items/b96eff5773ce9d9cc9b3
http://www.megasoft.co.jp/mifes/seiki/meta.html