2016年7月8日金曜日

[PHP]ノロノロ関数、array_pushは極力使うな・count()よりもisset()

今回はPHPのお話です。
PHPには「これでもか」と多くの配列関数があります。

その中でも大変遅い関数がありますので紹介しようと思います。

array_push()

コイツは遅い。ため息が出るほど遅いです。

そもそもarray_push()は、配列の最後に値を追加する関数です。

$hoge = array('ね', 'うし', 'とら');

array_push($hoge, 'う', 'たつ');
/*
[
    "ね",
    "うし",
    "とら",
    "う",
    "たつ"
]
*/
でもこうするくらいだったら、
$hoge[] = 'う';
$hoge[] = 'たつ';
こうしたほうが早いです。ネット情報によると、10倍近く速度が違うらしいです。
ここまで差が出ると、中では一体何をしてるんだと疑いたくなります(^_^;;)。買い物を頼んだら1週間かかって帰ってくるようなものです。
array_push()は追加後の要素数を返しますが、それならcount()を使って個別に調べたほうがいいと思います。

count()

count()は目立って遅い関数ではありません。しかし、使い方によっては速度面で損をしているかも。
count()は配列の要素数を返します。でも、場合によっては要素数を知らなくても済むことだってあるのです。
例えば、
//こうするくらいだったら、
if(count($hoge) === 0){
//こうしたほうが早い
if(isset($hoge[0])){

//また、こうするくらいだったら、
if(count($hoge) >= 6){
//この方が早い
if(isset($hoge[5]){
isset()は関数のような見た目をしていますが、実は違います。これ、if()while()と同じ、言語構造なのです。当然、呼び出しのオーバーヘッドがない分早いです。
ちなみに、empty()も言語構造です。

まとめ

他にもネット上にはいろいろな情報が転がってます。array_search()は遅い、型が事前にわかってるなら==より===のが速い、intval()strval()よりキャストした方がいい、など。

ただ、時と場合によりけりです。具体的な配列の要素数を知りたいなら上で紹介した方法よりもcount()の方がましでしょう。確かにPHPには遅い関数や構文が多いですが(そもそも言語自体遅い)、場面に応じて使い分けられるようにしましょう。

2016年3月31日木曜日

プログラミングに「メモ帳」を使うな?BOM・改行コードについて考える


お久しぶりです。chiko ryoです。

さて今回は、Windowsにデフォでついてくる「メモ帳」でテキストファイルを保存したときに頭を悩ませる「BOM」と「改行コード」について考えてみようと思います。

メモ帳でUTF-8を使うとBOMがついてくる

メモ帳で「名前を付けて保存」すると嫌でもBOMが付いてきます。BOMなしUTF-8を得るには何かしらのツールが必要になります。
そもそもBOMというのは、「このファイルの文字コードはUTF-8ですよー」という意味の制御コードです。文字化け防止にもなるいい奴なのですが、プログラミングでは邪魔になります。
<meta charset="utf-8">
<?php
  echo file_get_contents(/* BOMつきUTF-8空ファイル */);
?>
<div style="background-color:#000;color:#fff;">上に隙間があるはずです。開発者ツールでみると制御記号が入っているのがわかります。</div>
黒いdivの上に変な隙間があるのがお分かりいただけるかと思います。これがBOMの仕業です。

BOM対策

対策としては、
  • メモ帳を使わない(他のソフトを使う)
  • 保存後に手作業で削除
あたりでしょうか。メモ帳ではBOMなしUTF-8が保存できないのでこうすることになります。

メモ帳では改行コードがCR+LFになる

これもなかなかの問題です。

Windowsでは基本的に改行コードはCR+LFです。きっとWindowsで作成したテキストがMac(CR派)やLinux(LF派)でも表示されるように、という配慮(であってほしい)です。

ただ、文字コードがCRだけ/LFだけのテキストファイルをメモ帳で開こうとすると、
メモ帳は「改行はCR+LF」と思っている → LF(CR)が単独でくる → メモ帳は改行だと思わない
といった具合で改行が潰れます。

改行コード問題への対策

  • メモ帳を使わない(他のソフトを使う)
  • 変換する
メモ帳以外のソフトとしては、いろいろと選択肢があります。ちなみに私はサクラエディタを使っています。
また、改行コード問題に関しては、moreコマンドを使えばWindows標準で変換できるようです。詳しくは テキスト・ファイルの行末コードを変更するとかの情報が参考になると思います。

いかがでしょうか。今回はメモ帳のBOMと改行コードについてここまで論じてきましたが、結論としてはMSが強制メモ帳ではできないので他の策を講じる、ということになりそうです。

2015年11月3日火曜日

Javascriptでピタゴラス数を求める

普段管理人が通う学校の教師の提案で、「ピタゴラス数」を求めるプログラムを作ってみました。

ピタゴラス数って何?って方はWikipedia - ピタゴラス数をご覧ください。

ここでのmnは、ピタゴラス数の面積を求めるのに使用する自然数(m > n)とします。

では、さっそく実物を。

mまたはnの最大値 

処理結果

mの値 nの値 斜辺(m2 + n2) 他の一辺(m2 - n2) 他の一辺(2mn) (参考) 面積 (参考) mとnのG.C.D

参考程度にソースコードをはっておきます。

<!--
  <script>~</script>間はコメントアウトを省略
  しています。
-->
<script type="text/javascript">
function tagainiso(){
  // 剰余
  var r;
  var tagainiso_n = n;
  var tagainiso_m = m;
  gcd = 1;

  while ((r = tagainiso_m % tagainiso_n) != 0) {
    tagainiso_m = tagainiso_n;
    tagainiso_n = r;
  }
  gcd = tagainiso_n;
  if(gcd != 1) {
      n = n / gcd;
      m = m / gcd;
  }
}

function pitagras(){
  // 乱数その1
  m = Math.floor(Math.random()*document.getElementById("max").value + 1);
  //その2
  n = Math.floor(Math.random()*document.getElementById("max").value + 1);

  if(m < n) {  // m >= nにする
    var taihi = m;  //一時退避
    m = n;
    n = taihi;
  }
  tagainiso();
  //テーブル取得
  var table = document.getElementById("area");
  //行追加
  var tr = table.insertRow(-1);
  //セル挿入
  tr.insertCell(-1).innerHTML = m;
  tr.insertCell(-1).innerHTML = n;
  tr.insertCell(-1).innerHTML = m * m + n * n;
  tr.insertCell(-1).innerHTML = m * m - n * n;
  tr.insertCell(-1).innerHTML = 2 * m * n;
  tr.insertCell(-1).innerHTML = ( (m * m - n * n) * 2 * m * n ) / 2;
}
</script>

<p><b>m</b>または<b>n</b>の最大値&nbsp;<input type="number" size="5" id="max" value="50"></p>
<p><button onclick="pitagras()">実行</button></p>
<p>処理結果<br>
<table id="area" border="1">
  <tr>
    <td>mの値</td>
    <td>nの値</td>
    <td>斜辺(m<sup>2</sup> + n<sup>2</sup>)</td>
    <td>他の一辺(m<sup>2</sup> - n<sup>2</sup>)</td>
    <td>他の一辺(2mn)</td>
    <td>(参考) 面積</td>
  </tr>
</table>
</p>

作り方を知りたい方は、コメントいただければ解説します。

2015年10月29日木曜日

Javascriptで最大公約数を求める(ユークリッドの互除法)

今回は、数学的なお話。ユークリッドの互除法をJavascriptでしてみようと思います。

まず...
ユークリッドの互除法とは?という方へ。

ユークリッドの互除法は、簡単に最大公約数を求めるための計算法です。説明より例を示した方がいいかと思いますので、例題を一つ。
(例題) 1071と1029の最大公約数を求めよ。

  • 1071 を 1029 で割った余りは 42
  • 1029 を 42 で割った余りは 21
  • 42 を 21 で割った余りは 0
  • よって、答えは 42

では、これをプログラムに書き起こしてみましょう。関数名は最大公約数の略であるG. C. D.としました。

function gcd(m, n) {
  // 剰余
  var r;

  if(m < n) {  // m >= nにする
    r = m;  //一時退避
    m = n;
    n = r;
  }

  while ((r = m % n) != 0) {
    m = n;
    n = r;
  }

  return n;
}

2015年10月28日水曜日

C#で外部スイッチとやり取りする

パソコンと(2極)スイッチで通信する方法を考えてみました。(動作未確認)
※2極スイッチとは、簡単なON/OFFだけを切り替えるスイッチのこと。

そんなとき問題になるのは、

  • パソコンとスイッチをどうやって繋ぐか
  • ソフト側でどうやってON/OFFを認識するか
です。

まず考えたのは、USBで接続する方法。USBポートでスイッチと接続できないかと。しかし、これではドライバを作らないといけないし、スイッチ側に自己紹介する機能を持たせなければやり取りは不可能です。

次に考えたのがシリアルポートを使う方法です。1ビットのシリアル接続が可能なら、ON/OFFも可能なのではないか。
結論から先に書くと、「できました」。

RS-232C規格のシリアルポートを使った具体的なしくみです。
まず、DTR線の状態をONにしておきます。スイッチはDTR-(スイッチ)-DSRの順で接続しておきます。スイッチがONになると、DTRから出力された12Vの電流がDSRに流れることになります。スイッチをOFFにしたときは-12Vが正確に入力されるわけではありませんが、電流0はFalse扱いをうけるようです。

その理論(?)をもとに、C#でコードを書いてみました。

using System;

// シリアルポートクラスがある名前空間
using System.IO.Ports;

namespace myPort
{
  public class myPort1
  {

    // シリアルポートクラスのインスタンス
    SerialPort port = new SerialPort("COM1");

    // ポートをオープンする
    port.Open();

    // DTR線を出力
    port.DtrEnable = true;

    // ONかOFFかは次の値で確認する
    port.DsrHolding;

    // 例
    private void checkSwich(){
      if(port.DsrHolding)  // ON
      {
        MessageBox.Show("スイッチはONです。");
      }
      else
      {
        MessageBox.Show("スイッチはOFFです。");
      }
    }
  }
}

これで、ON/OFFの2極スイッチとパソコンとでやり取りができるようになりました。

なお、この記事を書くにあたって、どこでも発車ベルヘルプ - スイッチのつなぎ方を参考にいたしました。
ありがとうございました。

なお、この記事で記載された内容は、管理人(chiko ryo)では動作確認を行っておりません。くれぐれも自己責任でお願いします。また、このサンプルは著作権フリーですが、これを参考にしていかなる損害が出たとしても管理人(chiko ryo)は責任を負いませんのでご容赦ください。

2015年10月27日火曜日

JavascriptでINIファイルを解読する その3

JavascriptでINIファイルを解読するシリーズの第3弾です。

前回のJavascriptでINIファイルを解読する その2でお約束していた通り、INIを読み込んで連想配列に返す関数を完成させることができました。

さっそく、ソースをはります。

function myReadInI(myFname){

    var i = 0; // プロパティiはカウンタです。
    var t = null; // コメント除去に使用します。
    var ts = 0;   // これもです。
    var sec = null; //処理中のセクションを記録
    var pal = null; //処理中のパラメータ名を記録
    var returnData = {}; //返り値
 
    // オブジェクト生成
    var objFileSys = new ActiveXObject("Scripting.FileSystemObject");
 
    var objTextStream = objFileSys.GetFile(myFname).OpenAsTextStream(1, 0);
    /*読み込みモードを使用、文字コードはUnicodeです。(Unicode文字が扱えるようにするため)*/
 
    var data = new Array(); // 空の配列
 
    // ファイルが最後まで読み込まれるまでループ  読み込み
    while (!objTextStream.AtEndOfStream) {
 
        t = objTextStream.ReadLine();
        ts = t.indexOf(";"); // コメントを探す
        if(ts == -1){ // コメントがない
            data[i] = t;
        }else{ // コメントあり
            data[i] = t.substring(0, ts);
        }

        //解読

        if(data[i] != ""){  //空行でないか?

            if(data[i].indexOf("[") == 0){  //セクション行

                sec = data[i].slice(1, -1);    //セクション名を抽出する

                returnData[sec] = {}  //さらにパラメータ名の2次元連想配列が作れるようにする

            }else{                          //パラメータ行

                if(data[i].indexOf("=") != -1){  // "="を含む行であることを確認する

                    /* パラメータ名と値を分割(pal[0]がパラメータ名、pal[1]が値) */
                    pal = data[i].split("=");

                    /* 連想配列に代入する */
                    returnData [sec] [pal[0]] = pal[1];
                }

            }

        }
        i++;
    }

    return returnData;

}

前回からの変更点として、

  1. 引数が一つになった
  2. 戻り値が2次元連想配列になった
  3. 若干軽くなった
  4. 何度もファイルを読み込みなおす必要がなくなった
があげられます。また、空行があってもそのあとの行も読み込むようになったほか、セクションの閉じカッコ"]"の省略を禁止しました。


使い方
・引数

引数は1つ必要です。INIファイルまでのパス(絶対パス・相対パス)を文字列で渡します。
・戻り値
INIを解析した結果の2次元連想配列を返します。設定内容は(戻り値)["(セクション名)"]["(パラメータ名)"]で取得できます。

おそらくこのシリーズは今回で終わりとなります。
解説は要望があれば行います。ご希望であればコメントをお願いします。

Javascriptでテキストファイルを読み込む

近年、Javascriptがブームになりつつあります。FileAPIなどの拡張機能なども充実してきました。
そこで、IE独自のActiveXコントロールを使って色々なことをしてみましょう。

今回は(次回があるといいですね)テキストファイルの読み込みをしてみようと思います。

ファイル操作関連のことは、次のインスタンスを多くの場合使用することになります。

var hogehoge = new ActiveXObject("Scripting.FileSystemObject");

WSHの場合は、以下のようになります。

Set hogehoge = WScript.CreateObject("Scripting.FileSystemObject");

これで、ScriptingFileSystemObjectクラスのインスタンスを取得できました。ファイル操作は主にこいつを使って行います。

とりあえずファイルをテキストファイル(これ超重要)として開きます。
ファイルは開かないと何も始まりません。

テキストファイルを開くには、基本的にOpenTextFile()関数を使用します。(他にもありますが使い勝手が悪い)

var fs = new ActiveXObject("Scripting.FileSystemObject");
var file = fs.OpenTextFile("開くテキストファイルのパス");

このときOpenTextFile関数が返した値(ここではfile変数)で開いたファイルに対して様々な操作を行なえるようになります。

また、開いたファイルは用が済んだら閉じましょう。

file.Close();

/* ここからは任意 */
//解放
fs = null;
file = null;

テキストファイルを1行読み込む(1行読み込み)


1行だけ読み込むにはReadLine()関数を使用します。
/*
  あらかじめテキストファイルを開いておく
  var fs = new ActiveXObject("Scripting.FileSystemObject");
  var file = fs.OpenTextFile("開くテキストファイルのパス");
*/

/* 1行目のみ読み込む */
text[0] = file.ReadLine();

/* 2行目を読み込む */
text[1] = file.ReadLine();
ReadLine関数は、呼び出すたびに1行ずつ読み込みますので大変便利です。

テキストファイルを文字数を指定して読み込む


文字数を指定して読み込むには、Read()関数を使用します。
/*
  あらかじめテキストファイルを開いておく
  var fs = new ActiveXObject("Scripting.FileSystemObject");
  var file = fs.OpenTextFile("開くテキストファイルのパス");
*/

/* ファイルの先頭から6文字読み込む */
text = file.Read(6);

テキストファイルをすべて読み込む


テキストファイルをまとめて読み込むには、ReadAll()関数を使用します。
/*
  あらかじめテキストファイルを開いておく
  var fs = new ActiveXObject("Scripting.FileSystemObject");
  var file = fs.OpenTextFile("開くテキストファイルのパス");
*/

/* テキストファイルをすべて読み込む */
text = file.ReadAll();
ReadAll()関数は便利ですが、メモリを多く消費します。ですので極力使わず、ReadLine()関数をうまく使うようにしましょう。
var text = "";
/* ファイルが最後まで読み込まれるまでループ */
while (!file.AtEndOfStream){
  text += file.ReadLine();
}
AtEndOfStreamプロパティにはテキストファイルが終端に達しているかを示す真偽値が入っています。終端ならtrue、そうでなければfalseです。
他にも似たようなプロパティAtEndOfLineがありますが、これは空行に到達すると終端に達していないにも関わらずtrueになってしまいます。よってここでAtEndOfLineを使うと、空行混じりのファイルが読み込めなくなります。AtEndOfStreamを使うようにしましょう。

いかがだったでしょうか。意外と簡単だったと思います。Scripting.FileSystemObjectではファイルへの書き込みや追記も可能です。次回(あれば)はテキストファイルへの書き込みのお話をしたいと思います。

注意
今回使用したScripting.FileSystemObjectは、ActiveXコントロールを必要とします。ActiveXコントロールはHTAなどのセキュリティレベルの低い状態のIEまたはデジタル署名ずみのコンテンツをIEで動作させるときのみ使用できます。一般的なWebページを作成する際は、FileAPIなどを使うようにしてください。
また、私が作ったサンプルに著作権なんてあるわけがありません。ご自由に転載・引用・二次配布などしていただいて構いません。ただし、それらのサンプルでエラーが起こったり、データが消えたり、マシンが火を吹いたとしても私(chiko ryo)は一切の責任を負いませんのでご注意ください。