2012年6月16日土曜日

for文とforeach文の不思議な違い その2

こんにちは、matsuiです。

前回foreachの挙動を改めて確認しました。

確認用のサンプルプログラムを作って、確認した限りでは、

「foreachは配列自体を操作するわけではなく、配列のコピーを作成して処理します。foreachループ内で配列の要素を削除したり挿入したりしても、ループではその要素を反映しません。」

というプログラミングPHP(第2版)の解説を裏付けるような挙動をしていました。

今回のテーマ


今回は「配列のコピーを作成して処理する」という個所にフォーカスをあててみます。

前回はプリミティブ型の配列を用意して、サンプルプログラムとして動作させました。

今回はクラス型の配列で挙動を確認してみたいと思います。

目的は「配列のコピーは、どのようなレベルでコピーされるのか」を確認することです。

クラス型の配列ということはオブジェクトへの参照が含まれることになります。

参照型(オブジェクトへの参照)のコピーを考える際にディープコピー、シャローコピーという問題が出てきます。

foreachが内部で行っている「配列のコピー」はシャロー(浅い)なのでしょうか、ディープ(深い)なのでしょうか。


実験


まずは、配列の内部コピーが行われないfor文を使って配列内部ポインタがどの用に動作しているかを確認します。

for文バージョンサンプルプログラムソース

===========================================
class SampleObject
{
}
$obj1 = new SampleObject();
$obj2 = new SampleObject();
$obj3 = new SampleObject();

// パターン
$arrayList = array(
    $obj1,
    $obj2,
    $obj3,
);

echo "配列の全体を表示" . PHP_EOL;
var_dump($arrayList);
reset($arrayList);
echo PHP_EOL;

for ($i = 0;$i < count($arrayList);$i++) {
   
    echo "----- loop {$i} start ------" . PHP_EOL;
    echo "配列内部ポインタが指す要素の次の要素を取得" . PHP_EOL;
   
    $pointer = next($arrayList);
    var_dump($pointer);
   
    if ($pointer === false) {
        break;
    }
   
    echo "配列内部ポインタを最後にセット" . PHP_EOL;
    var_dump(end($arrayList));
    echo PHP_EOL;
   
}
===========================================

for文バージョン実行結果

===========================================
配列の全体を表示
array(3) {
  [0]=>
  object(SampleObject)#1 (0) {
  }
  [1]=>
  object(SampleObject)#2 (0) {
  }
  [2]=>
  object(SampleObject)#3 (0) {
  }
}

----- loop 0 start ------
配列内部ポインタが指す要素の次の要素を取得
object(SampleObject)#2 (0) {
}
配列内部ポインタを最後にセット
object(SampleObject)#3 (0) {
}

----- loop 1 start ------
配列内部ポインタが指す要素の次の要素を取得
bool(false)
===========================================
関数の説明はphpの公式サイトを見て頂くとして、サンプルプログラムとしては私の意図通り配列内部ポインタを移動させた結果ループ回数が3回から2回に変わっています。



次はforeachバージョンのサンプルプログラムです。
 

foreach文バージョンサンプルプログラム

===========================================
class SampleObject
{
    public $test = null;
}

$obj1 = new SampleObject();
$obj2 = new SampleObject();
$obj3 = new SampleObject();
$obj4 = new SampleObject();

// パターンA

$arrayList = array(
    $obj1,
    $obj2,
    $obj3,
    $obj4,
);

echo "配列の全体を表示" . PHP_EOL;
var_dump($arrayList);

echo "foreach 直前 current" . PHP_EOL;
var_dump(current($arrayList));

echo PHP_EOL;
echo "****** foreach start ******" . PHP_EOL;
echo PHP_EOL;

$count = 0;

foreach ($arrayList as $value) {
    $value->test = "sample string.";
  
    echo "------ loop ${count} start ------" . PHP_EOL;
    echo "foreach 内部直後 current" . PHP_EOL;
    var_dump(current($arrayList));
  
    echo "foreach 内部直後 next" . PHP_EOL;
    var_dump(next($arrayList));
    echo PHP_EOL;
  
    echo "foreach で処理している対象の配列の要素ポインタを配列の最後に動かす." . PHP_EOL;
    var_dump(end($arrayList));
    $count++;
    echo PHP_EOL;
}

echo "****** foreach end ******" . PHP_EOL;
echo PHP_EOL;
var_dump($arrayList);
===========================================

状況がわかリ易いように要素数とvar_dumpによる出力を増やしましたが、
基本的にはfor文バージョンと同じように、配列内部ポインタを制御し、loop回数が本来よりも少なくなるようにしました。
合わせて配列内のオブジェクトにも変更を加え、foreachによるloop処理が終わった後にどうなっているかを確認します。

それでは実行結果を見てみましょう。

foreach文バージョン実行結果

===========================================
配列の全体を表示
array(4) {
  [0]=>
  object(SampleObject)#1 (1) {
    ["test"]=>
    NULL
  }
  [1]=>
  object(SampleObject)#2 (1) {
    ["test"]=>
    NULL
  }
  [2]=>
  object(SampleObject)#3 (1) {
    ["test"]=>
    NULL
  }
  [3]=>
  object(SampleObject)#4 (1) {
    ["test"]=>
    NULL
  }
}
foreach 直前 current
object(SampleObject)#1 (1) {
  ["test"]=>
  NULL
}

****** foreach start ******

------ loop 0 start ------
foreach 内部直後 current
object(SampleObject)#2 (1) {
  ["test"]=>
  NULL
}
foreach 内部直後 next
object(SampleObject)#3 (1) {
  ["test"]=>
  NULL
}

foreach で処理している対象の配列の要素ポインタを配列の最後に動かす.
object(SampleObject)#4 (1) {
  ["test"]=>
  NULL
}

------ loop 1 start ------
foreach 内部直後 current
object(SampleObject)#4 (1) {
  ["test"]=>
  NULL
}
foreach 内部直後 next
bool(false)

foreach で処理している対象の配列の要素ポインタを配列の最後に動かす.
object(SampleObject)#4 (1) {
  ["test"]=>
  NULL
}

------ loop 2 start ------
foreach 内部直後 current
object(SampleObject)#4 (1) {
  ["test"]=>
  NULL
}
foreach 内部直後 next
bool(false)

foreach で処理している対象の配列の要素ポインタを配列の最後に動かす.
object(SampleObject)#4 (1) {
  ["test"]=>
  NULL
}

------ loop 3 start ------
foreach 内部直後 current
object(SampleObject)#4 (1) {
  ["test"]=>
  string(14) "sample string."
}
foreach 内部直後 next
bool(false)

foreach で処理している対象の配列の要素ポインタを配列の最後に動かす.
object(SampleObject)#4 (1) {
  ["test"]=>
  string(14) "sample string."
}

****** foreach end ******

array(4) {
  [0]=>
  object(SampleObject)#1 (1) {
    ["test"]=>
    string(14) "sample string."
  }
  [1]=>
  object(SampleObject)#2 (1) {
    ["test"]=>
    string(14) "sample string."
  }
  [2]=>
  object(SampleObject)#3 (1) {
    ["test"]=>
    string(14) "sample string."
  }
  [3]=>
  object(SampleObject)#4 (1) {
    ["test"]=>
    string(14) "sample string."
  }
}
===========================================

どうでしょうか。

loop回数と要素数が同じにならないように配列内部ポインタを変更しているにも関わらず、要素数分loopしています。
ループ内での各要素については、配列定義時のオブジェクトへの参照が取得できています。
また、処理が終わった後の配列内の各要素には、loop内で行った処理が反映されているのがわかります。

結論


以上の結果から、foreach文内で行われている「配列のコピー」は、以下のようなものと捉えることができます。

・配列の参照コピーではなく、foreachが開始した段階で新たに別の配列が内部で作成されている(配列内部ポインタが共有されていないため)

・配列の各要素(この場合はオブジェクト)については、参照のコピーが行われている(var_dumpの出力結果をみると同じオブジェクトへの参照になっているため)

所見

このような(ある意味)不思議な挙動をするにもかかわらず、プログラムを作る際に問題にならないのは、その動き自体が「プログラムを作成する側が、foreach文を使う際にイメージする挙動に近い結果が得られるため」と考えられます。

作る側がイメージする挙動に近いということは、利用の際の敷居(難易度)を下げ、意図しない不具合を無くし、最終的に作る側に対してメリットという形で還元されます。

foreachの一見不可解な動きも、PHP言語がプログラマのために用意した便利な仕組みを実現するために必要なことだったのかもしれません。

後は、foreach文のデメリットも把握したうえで、必要に応じて使う側がコントロールすればよいわけです。

ちなみに、foreach文による配列のコピーが問題になる場合は、each、list関数を使ってwhile文でループさせるのが良いようです(もちろんfor文でもOK)。

2012年5月24日木曜日

CSS3で透過指定

お疲れ様です。
5月1日に入社した三輪です。

今回エンジニアブログが初めて自分に回ってきたのですが、
書くネタがなさ過ぎて投稿が遅れました。
すみません。。

だいぶ前にCSS3の勉強をしていた時に、
こんなスタイル指定ができるようになったのかと感動した指定方法があったので、
ちょっとマイナーですがご紹介します。


背景色の透過

■背景色:skyblue(透過なし)

何もしない状態

■背景色:skyblue(透過あり opacity)

背景/文字が透過

■背景色:skyblue(透過あり CSS3でrgba指定)

背景だけ透過

【ソース(CSS/HTML)】

div#one {
  background:skyblue; opacity: 0.5;
}
div#two {
  background-color:rgba(135,206,250,0.5);
}
※0.5の部分が透過値

<p>■背景色:skyblue(透過なし)</p>
<div>何もしない状態</div>
<p>■背景色:skyblue(透過あり opacity)</p>
<div id="one">背景/文字が透過</div>
<p>■背景色:skyblue(透過あり CSS3でrgba指定)</p>
<div id="two">背景だけ透過</div>

エンジニアとしてまだまだ未熟なので、
これから色々と知識を身につけ、
成長していきたいと思っています。
それでは、今後ともよろしくお願いします。

2012年5月15日火曜日

Greasemonkey用スクリプト

こんにちは、またまた書くのが遅くなったhodori です。


今回はGreasemonkey用のスクリプトについて話そうと思います。


そもそも、GreasemonkeyとはFireFoxのアドオンで誰かが作った指定のページで
動作するjavascriptを実行できるようにしてくれるもので、
Chromeにも拡張機能で使う方法が幾つかあったりします。


主な使い方はWebサービスにブラウザ側で好きな機能を追加するみたいな感じで
自分の場合は某ソーシャルゲームの補助ツールを入れて楽をしていたりします。




Greasemonkeyで使われるスクリプトは基本下記のようなコメントから書き始めます。
// ==UserScript==
// @name           Google Search Add Range(Gsar)
// @namespace      http://labs.37to.net/gsar/
// @description    Add form by date range to Google search result.
// @include        http://www.google.co.jp/search*
// ==/UserScript==


この@から始まる各パラメータがスクリプトの動作を制御する形になります。


・@name
  Greasemonkeyなどで登録される際の識別名になります。
  同じスクリプトの別バージョンを使い分けたい場合などはこの名前を別の物にしてあげる必要があります。
・@namespace
  ネームスペース、自サイトのアドレスなど。無くても動作に問題なし。
・@description
  説明、同じく無くても動作に問題なし。
・@include
  スクリプトを動作させるサイトのURLを記入します。ワイルドカード「*」が使えるのと
  複数行記述できるので、関連サイト全部とか指定したページのみとかの指定もできます。


例)ブラウザ三国志のプロフィールと都市のページのみで実行する場合
// @include         http://*.3gokushi.jp/user/*
// @include         http://*.3gokushi.jp/village.php*
・@excludes
  スクリプトを適応しないURL、@includeで「*」を使ったことで含まれてしまうけど
  機能が必要ないページで実行されないように設定できる。


Greasemonkeyは便利だけど重くなるという人は使っているスクリプトの
これらの部分をカスタマイズして必要なページだけで使うようにすると重くならずに
便利な機能を使い放題になる……かも?

2012年4月12日木曜日

はじめまして!

はじめまして。


たゆたうにエンジニアとして入社いたしました柘植です。

エンジニアといっても予備知識がほとんどなく、

他のプログラマさんに「ほぉ~」と思ってもらえるような技術的で有用な話もまだまだできそうにないので、

私と同じようなプログラミング初心者でも分かる!話ができたらなぁと思っています。



ところで先日、いつものようにEclipseを立ち上げようとしたところ


Failed to create the Java Virtual Machine


というエラーが出て立ち上がりませんでした。

ぐぐってみると、どうやらJavaがなにかおかしいらしい。

そういえばJavaのアップデートしたな~と思いつつ解決策を調べたところ、

以下の方法で無事Eclipseが立ち上がるようになりました。


eclipse.iniの[-vmargs]より前に

-vm
C:/Program Files/Java/jdk1.6.0_20/bin/javaw.exe

を挿入。(パスは自分のJDKもしくはJREを確認)


いや~びっくりしました。

一度vmを指定して起動さえすれば、vmを消しても問題なく起動するようになります。


…すごくどうでもいい感じの話になってしまいましたが、これからどんどん勉強して立派なエンジニアになりたいと思っていますので、

これからよろしくお願いいたします。

2012年3月29日木曜日

node.jsの紹介

こんにちは、Leeです。
初めての投稿です。よろしくお願いします。


最近、Flash又はUnity3Dで作ったMMORPGやHTML5で作ったFPSゲーム(Quake)等、ウェブブラウザでリアルタイムに動くアプリケーションを開発できる技術が発展していますので、
今日はリアルタイムのウェブアプリを作る為の技術の中でServer側の技術である、node.jsに対して話してみたいと思います。


< node.jsとは >
node.jsはgoogleのすごく速いインタープリターであるV8 javascript engineを基盤で作られたサーバーサイドのJavascriptです。Javascriptでゲームサーバー等を作成できると言う意味です。
node.jsはV8エンジンが持っている長所(速さ、高効率のガベージコレクション等)の上に、OSのようなevented, non-blocking I/Oを提供します。
Evented I/Oとは入出力処理で処理がブロックされないように入出力処理を全て非同期で扱うようになっていると言う意味で、Event Loopを利用して動作を要請する時Callbackを指定して動作が終わるとCallbackが実行される仕組みです。
node.jsではひとつのプロセスEvent Loopを利用して多くのHTTPコネクションを扱う動作モデルを取っているので、高トラフィック(=多数のユーザ)を効率良く扱えます。接続数が多ければ多いほど、1つのコネクション当りに別のThreadを実行する方式に比べて、メモリの節約や安定性や性能が高いです。

< node.jsのインストール >
node.jsとNPM(Nodejs Package Manager)の設置が必要です。
・Fedora
sudo yum localinstall --nogpgcheck http://nodejs.tchol.org/repocfg/fedora/nodejs-stable-release.noarch.rpm
sudo yum install nodejs-compat-symlinks npm
・CentOS
wget http://nodejs.tchol.org/repocfg/el/nodejs-stable-release.noarch.rpm
yum localinstall --nogpgcheck nodejs-stable-release.noarch.rpm
yum install nodejs-compat-symlinks npm
・Amazon Linux
sudo yum localinstall --nogpgcheck http://nodejs.tchol.org/repocfg/amzn1/nodejs-stable-release.noarch.rpm
sudo yum install nodejs-compat-symlinks npm
※Debian, Ubuntu, openSUSE等のOSでのインストールもリンクを参考してください。
  https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager
※Windows、OSXの場合はhttp://www.nodejs.org/でInstallerをダウンロードして設置する方が便利です。

・設置が完了できたら、shellにnodeやnpmを入力して確認して見ましょう。
・もしエラーが出たら、Path問題の可能性が高いので、
    > export NODE_PATH=/インストールPATH/node_modules:$NODE_PATH
 等でPATHを環境変数に追加してください。


< チュートリアル >
簡単なWeb ServerやTCP ServerのSampleを作成して見ます。

[Web Server]
#Server側
・hello_server.jsと言うファイルを作成します。
01.var http = require('http'); 02. 03.http.createServer(function(request, response) { 04.   response.writeHead(200, {'Content-Type': 'text/html'}); 05.   response.write('Hello, World~!!'); 06.   response.end(); 07.}).listen(1337); 08. 09.console.log('Server running at http://localhost:1337/');
     line03:web serverのObjectを生成。callbackをanonymous function形で指定。
    line04:response headerを伝送。
    line05:文字列’Hello, World~!!’を伝送。
    line07: listenerがrequestイベントが発生する時、callbackを実行。

・作成したserverを実行します。
>node hello_server.js
Server running at http://127.0.0.1:1337/
#Client側
・Web browserでserverに接続して見ます。












[TCP Server]
#Server側
・tcp.jsと言うファイルを作成します。
01.var tcp = require('net');
02.
03.var server = tcp.createServer();
04. 
05.server.on('connection', function(e) {
06.   e.write('hello!\n');
07.   e.end();});
08.
09.server.listen(8000);
    line03:tcp serverのObjectを生成。
    line05:'connection'イベントが発生すると、'hello!'を伝送。
    line09:serverのlistenerを実行。

・作成したserverを実行します。







#Client側
・telnetでserverに接続して見ます。







・'hello!'という文字列が出力されます。







以上です。Sampleが簡単すぎて、すみません。
普通はnodejsでサーバーを作成する時はnodejsのpackageである
socket.ioと言うものを使って作ります。
次回はWebSocketやsocket.ioに対して話して見たいと思います。


ありがとうございました。

2012年3月21日水曜日

第4回 HTML5のススメ

久々の更新 KOYAです。

さて、今回はHTML4までは出来なかった、マルチスレッドプログラミングをする方法をご紹介します。

わざわざjavascriptでマルチスレッドプログラミングをする理由ですが、
今までは高負荷な処理をjavascriptで行うとブラウザが固まったりしましたが、
HTML5から導入された機能を使えば、バックグラウンドで処理を行いブラウザが固まることなく
快適なネット生活を送ることが可能となりました。

ということで、あえて、待機状態のある状況を作ってみます。
以下がソースです。

 
<head>
    <script type="text/javascript">
        // テキストエリアに指定された秒数待つ。
        function sleep(){
            var sec = document.getElementById("num").value
            var start = new Date;
            while (1) {
                var cur = new Date;
                if (sec * 1000 <= cur.getTime() - start.getTime()) 
                {
                    break;
                }
            }
            alert(sec + "秒まったよ");
        }
    </script>
</head>
<body>
    秒数:<input type="text" id="num"><button onclick="sleep()">スリープ</button>
</body>
テキストエリアに指定された秒数分待機状態が入った後、alertで何秒待ったかを表示する単純なロジックです。
buttonを押すと指定秒数ブラウザが固まり、何もすることができなくなったと思います。
ということで今迄はやきもきしながら結果をまたなくてはなりませんでした。

しかし、HTML5から導入された機能『Web Workers』を使用すれば固まることがなく動作することが可能です。
以下がソースとなります。

main.html
 
<head>
    <script type="text/javascript">
        // インスタンスの生成
        var worker = new Worker("http://ほにゃらら/worker.js");

        // 結果返却後処理
        worker.onmessage = function(event) {
            alert(event.data + "秒まったよ");
        };
        
        // post送信
        function post() {
          worker.postMessage(document.getElementById("num").value);
        }
    </script>
</head>
<body>
    秒数:<input type="text" id="num"><button onclick="post()">スリープ</button>
</body>

worker.js
 
// メッセージの受信
onmessage = function(event) {
    sleep(event.data);
    postMessage(event.data);
}

function sleep(sec){
    var start = new Date;
    while (1) {
        var cur = new Date;
        if (sec * 1000 <= cur.getTime() - start.getTime()) 
        {
            break;
        }
    }
} 
ということで、バックグラウンドで処理を行うjsでWoekerオブジェクトを作成し、
postで指定されたjsにデータの送信を行い、jsの中で受け取った値をWokerオブジェクト
のonmessageイベントで取得し、alertで値を表示するというような処理を行なっています。
これでバックグランドで処理を行なっているので、ブラウザが固まることなく結果が帰って来たと思います。

HTML5が台頭したことにより、リッチな処理を行わなければならない事が増えてきたと思います。
そんな時には、是非WebWorkerを使用してブラウザが固まることを回避してみてください。

2012年3月15日木曜日

for文とforeach文の不思議な違い

こんにちは、matsuiです。

今回ご紹介する内容は、私が実際にPHPにてソーシャルアプリを実装していく際に挙動確認のために作ったforとforeachのサンプルソースです。

出だしから既に内容について推測出来てしまった方もいらっしゃるとは思いますが、
ここでは「なぜ今更forとforeachの動きを確認しているの?」と思って頂いたと仮定して話を進めさせていただきたいと思います。

forとforeachを使ったサンプルプログラムを3パターンほどご用意しました。
以下のソースをご覧ください。

// パターンA
$arrayList = array(
    0,
    1,
    2
);

foreach ($arrayList as $key => $value) {
    array_pop($arrayList);
    var_dump($arrayList);
}

// パターンB
$arrayList = array(
    0,
    1,
    2
);

$length = count($arrayList);
for ($i = 0;$i < $length;$i++) {
    array_pop($arrayList);
    var_dump($arrayList);
}

// パターンC
$arrayList = array(
    0,
    1,
    2
);

for ($i = 0;$i < count($arrayList);$i++) {
    array_pop($arrayList);
    var_dump($arrayList);
}

それぞれの違いについてお分かりでしょうか?
配列の宣言は全て同じで、array_pop関数を使って宣言した配列の最後の要素を取り出した後、配列の中身をvar_dumpを使って出力しています。
違いはforかforeachで処理しているかと配列の長さをどこで取得しているかの違いです。
各パターンの出力結果の違いについて想像してみてください。

出力結果は以下。

パターンAの結果
array(2) {
[0]=>
int(0)
[1]=>
int(1)
}

array(1) {
[0]=>
int(0)
}

array(0) {
}

パターンBの結果
array(2) {
[0]=>
int(0)
[1]=>
int(1)
}

array(1) {
[0]=>
int(0)
}

array(0) {
}

パターンCの結果
array(2) {
[0]=>
int(0)
[1]=>
int(1)
}

array(1) {
[0]=>
int(0)
}

出力結果は想像通りだったでしょうか?
結果だけ見るのであればパターンAとパターンBは同じです。
パターンCだけが結果が違うのは、配列の要素数をどのタイミングで取得し、終了条件と比較しているかという点が他と違うためです。

さて、そろそろ本題に入ります。
以下のような2つのプログラム、パターンDとパターンEがあります。

// パターンD
$arrayList = array(
    0,
    1,
    2
);

foreach ($arrayList as $key => $value) {
    $popValue = array_pop($arrayList);
    array_unshift($arrayList, $popValue);
    var_dump('key : ' . $key . " value :" . $value);
    var_dump($arrayList);
}

// パターンE
$arrayList = array(
    0,
    1,
    2
);

$length = count($arrayList);
for ($i = 0;$i < $length;$i++) {
    $popValue = array_pop($arrayList);
    array_unshift($arrayList, $popValue);
    var_dump('key : ' . $i . " value :" . $arrayList[$i]);
    var_dump($arrayList);
}

今までの例と同じように配列の宣言は一緒です。
違うのはforとforeachの構文とそれに伴う配列の要素に対するアクセス方法の違いだけで、「配列の最後の要素を取得し、素配列の最初の要素として登録する」という動き自体は一緒です。
どんな実行結果が出るか想像してみてください。

実行結果は以下です。

パターンD
string(16) "key : 0 value :0"
array(3) {
[0]=>
int(2)
[1]=>
int(0)
[2]=>
int(1)
}

string(16) "key : 1 value :1"
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(0)
}

string(16) "key : 2 value :2"
array(3) {
[0]=>
int(0)
[1]=>
int(1)
[2]=>
int(2)
}

パターンE
string(16) "key : 0 value :2"
array(3) {
[0]=>
int(2)
[1]=>
int(0)
[2]=>
int(1)
}

string(16) "key : 1 value :2"
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(0)
}

string(16) "key : 2 value :2"
array(3) {
[0]=>
int(0)
[1]=>
int(1)
[2]=>
int(2)
}

実行前に想像した結果と実際の実行結果が違った方も、いらっしゃるのではないかと思います。
パターンDは、実際の宣言した配列の出力内容とforeach構文によって取得した要素($keyと$value)の中身が違っています。

どうしてこのような結果の違いが生まれるのでしょうか。

PHPのソースコードを読めば明確な答えが出るのでしょうが、ちょっと時間的にそうも言ってられないので、
プログラミングPHP(第2版)という本からその原因と思われる個所についての説明を拝借してくることにします。

foreachは配列自体を操作するわけではなく、配列のコピーを作成して処理します。foreachループ内で配列の要素を削除したり挿入したりしても、ループではその要素を反映しません。

だそうです。
配列の確かにコピーを作成して処理した場合の動きだと考えると、動作結果に納得がいきます。

特に意識をして使い分けをするようなケースは余りないかもしれませんが、forとforeachには明確な違いがあるということが実験から明らかになりました。

残念ながら内部動作まで私自身が読み解いたわけではありませんので、「違いがある」という結論以上のことを明言できないのが残念ですが、今回はここまでとさせてください。