昨晩、無事帰ってまいりました。(古希を過ぎると段々と海外がしんどくなりますね。)

 

所で、矢張り(対ドル)円安と(米国インフレによる)物価高は半端なく、さほど裕福でもない私には毎日の食事も大変です。例えば贅沢をしなくても現在の美国の都市では$20~30以下で食事をすることは考えられず、州の物品税と(既にIRSに捕捉されている)チップ($15、20、25の選択制が今は一般的)がかかれば、外食するなら朝食から$3~40かかることを覚悟しなければなりません。(これが夕食で、お神酒などを頼んで、しっかり食べると$200~300当たり前、有名店ならOver $500も"Pretty much probable"です。勿論、タックス、チップ前ですよ。まぁ、上を見ればキリがないのですが...

しかし、ここで安易に

 

「円換算」すると「地獄に落ちる」

 

ので、ドルはドルのままの数字で扱いましょう。(民主党時代の円高、デフレ時代の2 - 3倍になりますね。)

 

因みに具体的な話をすると、私はシーザーサラダやツナサンドイッチなどが好きですが、スーパーやコンビニでこれらを買うと$9.99とか$8.99とかします。矢張り量が多いので、古希の私はそれを(↓こんな具合に)2回または3回に分けて食べることになります。

 

 

現在のレートが大体US$1 = JPY154程度なので、

 

$9.99(JPY1,538)+$8.99(JPY1,384)=$18.98(JPY2,923)

 

これを2 - 3回に分けて食べても千円から千五百円程度することになります。

 

まぁ、日本でもインバウンドの影響でラーメン一杯が千円を超えたこともあるので、

 

まっ、いいか?

 

と考えましょう。

 

何れにしてもこれからは質実剛健にプログラミングの話などをしましょうか?

今日から国外に行って留守にするので、昔のコードを使ったソート方法比較プログラムのコードだけ先に紹介しておきます。(C++11なら何でも動くはず。)

 

(1)ベースとなるコードはここで触れましたが、約40年前のC言語ベースのプログラムです。

 

(2)「アルゴリズムの確認と共にもう一度現代風なC++で書き直し」等と偉そうなことを書きましたが、アルゴリズムは自分自身が良く分かっていなかったところもあり再学習し、「一番重要なソート関数部分がCで書いてもC++で書いても変わらない」ので、結局それはそのまま残し、入出力の一部だけの変更となりました。(

:これだけではあまりに悲しいので、CSORTERクラス等にして異なるデータ系の配列をオーバーロードで使えるようにしてみようか?と考えたのですが、そんなのもっと良いものが現代のライブラリーにできているので「あっ、お呼びでない?そりゃまた、失礼いたしましたっ!<1:42~>」ということで止めました。

 

(3)ということで、今回は私自身の再学習を兼ねてアルゴリズムの確認に重きを置き、分かり易い説明を後日加えてゆきます。

 

(4)プログラム自体でいえば、配列サイズは"new"を使って可変にすることもできたのですが、していません。変な入力による誤使用、ということよりも比較する際の現実的なサイズ()はそれほど多岐にわたらないからです。(別に手抜きした訳ではないですよ。)

:小さすぎると(1,000位でも)全て所要時間"0.00000"になりますし、大きすぎると時間がかかるし、表示することも現実的ではなくなります。

 

(5)登場するソートアルゴリズムは「Bubble Sort」、「Heap Sort」、「Quick Sort」と「Shell Sort」の4つです。

 

(6)なお、解説でメモリー云々の話が出てくると思いますが、それは又このシリーズの後にでも...

 

では、

 

行ってまいります。

Stay tuned!

 

 

【Comparison_Sort.cpp】

///////////////////////
// C++ Sort Comparison
///////////////////////

#include    <stdio.h>        //C言語の最小限のシステムヘッダー
#include    <conio.h>        //getch()を使う為
#include    <iostream>        //C++言語の入出力システムヘッダー
#include    <chrono>        //C++の時間計測用ライブラリー

//定数定義
#define        MAX 10000        //取り敢えず10,000がお勧め

using namespace std;

////////////////////////
// 関数プロトタイプ宣言
////////////////////////
// Bubble Sort

void bubblesort(int*, int);
// Heap Sort
void heapsort(int*, int);
void exchange(int, int*, int);
// Quick Sort
void quicksort(int*, int*);
// Shell Sort
void shellsort(int*, int);
// Print data
void print_data(int*, int);

////////////////////
// 共通テスト用配列
////////////////////

static int original[MAX];    //解説:この変数領域をプログラムメモリーのbss領域(静的領域)に置くため

//解説:なお、この配列を「ポインター+"new"」でメモリーに動的確保すると、ヒープ領域に置かれます。

/////////////
// main関数
/////////////

int main(int argc, char** argv) {

    //テスト用の配列の作成
    srand((unsigned)time(NULL));    //現在時刻を元に種を生成
    static int test_array[MAX];        //テスト用ローカル変数(解説:こっちはスタックメモリーに置かれる由)
    for(int i = 0; i < MAX; i++) {
        test_array[i] =             //originalを保存し、テストはこれで行う
        original[i] = rand();        //0から32,767の整数で初期化する
    }
    //時間計測の準備
    chrono::system_clock::time_point start, end;
    //coutで浮動小数点を表示するには https://dexall.co.jp/articles/?p=1773#i-6 を参照

    //データ表示を行うか否か?
    bool show = false;
    char key = 0;
    cout << "整数配列のサイズは " << MAX << "です。" <<endl;
    cout << "テストデータの表示を行いますか?(はい-Y/y、いいえ-N/n)";
    while(!key) {
        key = cin.get();
        if(key == 'Y' || key == 'y')
            show = true;
        else if(key == 'N' || key == 'n')
            show = false;
        else
            key = 0;
    }
    //キーバッファをクリアする
    cin.clear();
    //バッファを改行文字まで無視する
    cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    //解説:最初↑の処理をしなかったので、プログラムの最後の"getc"が勝手に実行され、すぐ終わってしまいました。

    //オリジナルデータの表示
    if(show) {
        cout << ">>> オリジナルデータ <<<" << endl;
        print_data(original, MAX);
    }
    //ソート比較
    printf(">>> Bubble Sort <<<\r\n", time);
    start = chrono::system_clock::now();
    bubblesort(test_array, MAX);
    end = chrono::system_clock::now();
    double time = static_cast<double>(chrono::duration_cast<chrono::microseconds>(end - start).count() / 1000.0);
    printf("time = %lf[ms]\r\n\r\n", time);
    if(show)
        print_data(test_array, MAX);

    for(int i = 0; i < MAX; i++)
        test_array[i] = original[i];
    printf(">>> Heap Sort <<<\r\n", time);
    start = chrono::system_clock::now();
    heapsort(test_array, MAX);
    end = chrono::system_clock::now();
    time = static_cast<double>(chrono::duration_cast<chrono::microseconds>(end - start).count() / 1000.0);
    printf("time = %lf[ms]\r\n\r\n", time);
    if(show)
        print_data(test_array, MAX);

    for(int i = 0; i < MAX; i++)
        test_array[i] = original[i];
    printf(">>> Quick Sort <<<\r\n", time);
    start = chrono::system_clock::now();
    quicksort(test_array, &test_array[MAX - 1]);
    end = chrono::system_clock::now();
    time = static_cast<double>(chrono::duration_cast<chrono::microseconds>(end - start).count() / 1000.0);
    printf("time = %lf[ms]\r\n\r\n", time);
    if(show)
        print_data(test_array, MAX);

    for(int i = 0; i < MAX; i++)
        test_array[i] = original[i];
    printf(">>> Shell Sort <<<\r\n", time);
    start = chrono::system_clock::now();
    shellsort(test_array, MAX);
    end = chrono::system_clock::now();
    time = static_cast<double>(chrono::duration_cast<chrono::microseconds>(end - start).count() / 1000.0);
    printf("time = %lf[ms]\r\n\r\n", time);
    if(show)
        print_data(test_array, MAX);

    cout << "Push CR key ..." << endl;
    getchar();
    return 0;
}

////////////////
// Bubble Sort
////////////////
/*
    n個の整数配列dataを総て順に比較、交換し、配列の最後から大きい順に並べる
    通称「馬鹿ソート」
*/

void bubblesort(int* data, int n) {

    int i, j, temp;

    for(i = n - 1; i > 0; i--) {        //配列の末尾から先頭へ
        for(j = 0; j < i; j++) {        //配列の先頭から末尾へ
            if(data[j] > data[j + 1]) {    //次の配列要素の方が小さければ交換する
                temp = data[j];
                data[j] = data[j + 1];
                data[j + 1] = temp;
            }
        }                                //その結果iに最大の要素が置かれる
    }
}

//////////////
// Heap Sort
//////////////
/*
ヒープソートは次のようなヒープ(二分木)の「親-子」関係を用いる
        Parent(0)
          /\
  ChildL(1)  ChildR(2)

この二分木を用いて配列を表現すると以下のようになる(数字は添字)
           0
         / \
        1     2
      / \ / \
     3    4 5    6
   / \
  7     8 ...
*/


void exchange(int top, int* data, int n) {

    int temp = data[top];                        //開始時の二分木の親の値をtempに退避
    //子のある二分木の左の子供を指定し、(交換があればやり直す為)末尾迄子孫を辿る
    for(int i = top * 2 + 1; i < n; top = i, i = top * 2 + 1) {
        if(i < n - 1 && data[i] < data[i + 1])    //右の子供が左の子供より大きければ
            i++;                                //右の子供を指定する
        if(temp >= data[i])                        //開始時の親と大きい方の子供を比較
            break;                                //親が大きければループを抜ける(topは変化しない)
        data[top] = data[i];                    //二分木の親に大きい子の値を代入する
    }                                            //大きい子(data[i])はループのi = topで
    data[top] = temp;                            //data[top]となり、退避した親の値を代入
}

void heapsort(int* data, int n) {
    int top, i;

    for(top = n / 2 - 1; top >= 0; top--)        //最初に「子がある最後の二分木」から
        exchange(top, data, n);                    //「親が最大値を持つ」ように頂点まで継続する
    for(i = n - 1; i > 0; i--) {                //次に配列の最後と頂点(最大値)の値を交換し
        int temp;
        temp = data[i];
        data[i] = data[0];
        data[0] = temp;
        exchange(0, data, i);
    }                                            //配列の最後を切り離し、その前の要素から頂点まで継続する
}

//////////////
// Quick Sort
//////////////

//検索範囲(startとend)で、基準値(key)を定め、それよりも多いものと少ないものを分割し、更にkeyの前後で同じ処理を再帰呼び出しする。
void quicksort(int* start, int* end) {

    if(start < end) {                            //開始位置が終了位置以上で終了(最後の再帰呼び出し参照)
        int *left, *right, key, *kp;            //順に右進行位置、左進行位置、「分水嶺」値、その位置
        //右端乃至左端に到達し、breakで抜けるまでループする
        for(left = start, right = end, key = *right;;) {
            while(key >= *left)
                left++;                            //keyを超える値迄右に進む
            if(left < right)                    //rightの位置迄は
                *right = *left;                    //leftの値をrightの位置に書き込む
            else {                                //leftがright位置を過ぎたなら
                kp = right;                        //keyの位置をrightの位置としてループを抜ける
                break;
            }
            while(key <= *right)
                right--;                        //key未満の値迄右に進む
            if(left < right)                    //leftの位置迄は
                *left = *right;                    //rightの値をleftの位置に書き込む
            else {                                //rightがleft位置を過ぎたなら
                kp = left;                        //keyの位置をleftの位置としてループを抜ける
                break;
            }
        }
        *kp = key;                                //keyポインターの位置にkeyの値を代入する
        quicksort(start, kp - 1);                //開始位置からkeyポインター迄で再帰呼び出しする
        quicksort(kp + 1, end);                    //keyポインターから終了位置迄で再帰呼び出しする
    }
}

//////////////
// Shell Sort
//////////////
//シェルソートは配列要素を一定の距離を置いたグループに分けて比較、交換を行いソートする方法

void shellsort(int *v, int n) {

    int gap, i, j, temp;    //gapは比較、交換する要素間の距離

    for(gap = n / 2; gap > 0; gap /= 2) {    //初期的な距離は配列サイズの1/2とする
        for(i = gap; i < n; i++) {            //距離は徐々に狭まってゆく
            for(j = i - gap; j >= 0 && v[j] > v[j + gap]; j -= gap) {
                //v[j]とv[j + gap]を交換する
                temp = v[j];
                v[j] = v[j + gap];
                v[j + gap] = temp;
            }
        }
    }
}

//////////////
// Print data
//////////////

void print_data(int* d, int n) {

    for(int i = 0; i < n; i++) {
        if(i % 10 == 9)
            printf("%6d\r\n", d[i]);
        else
            printf("%6d, ", d[i]);
    }
    printf("\r\n");
}

 

プログラムもの、無駄話などが続いたのと、昨日無性に昔シンガポールのラオパサ(注1)で好きだった「ミーフンゴレン」(注2)が食べたくなり、「それっぽいもの」を作ってみようと思い立ちました。

注1このブログを参照して下さい。これ、結構面白いですね。ただ、この中で推奨している「新嘉坡料理」については、私見がありますね。サテーは甘めであまり好きではありません。(フライド)ホッケンミー(焼き福建麺)はだーい好き、バンミー(板麺-私にとっては「バンミェン」)も(週に7回食べたほど)大好きですが、他はどうでもよくて、その他書いてほしいものがありました。例えば、東南アジア、シンガポール初心者でも安心して食べられる「ダンプリングヌードル(Dumpling noodle-港式面、要すれば香港式ワンタンメン)」、今でも食べたくてここに行きましたが「近いけど違うなぁ」という感じ。私の好きだった店は日本語検索では出ないので、英文検索したらここにありました。下の方にある「港式面」の"Watercress & Shrimp Dumpling Noodle $13.30"という奴です。あー、食べたい。

注2:インドネシアのミーゴレン(麺・炒めた)はよく知られていますが、「ミーフンゴレン」は「米粉・炒めた」の意味です。日本語検索で調べるとこんなもの赤くないっ!)がありましたが、私が食べていたラオパサのものと比べると一寸贅沢で上品です。

 

私が好きだったミーフンゴレンの特徴は、ビーフン(米粉)がサンバルで赤く、又実際辛いものでした。具は豚肉のコマ(挽肉の様に小さいが)、モヤシ、炒り卵などでしたか?

 

しかし、適切な麺や調味料が無い!

 

ということで、「見た目が大事」という方針で、

 

ビーフン→夏に買ったソーメンの残り

サンバル→韓国料理に使うコチジャン

 

で代用できないか?というChallengeを行うことにしました。その結果が、

 

ホットソーメンゴレン(韓国風ソーメンチャンプルー?)

 

その印象は?(以下は私のAlbumソフトの写真裏書)

「昔シンガポールのラオパサで食べたミーフンゴレンが懐かしくなり、夏に買ったソーメンを使い、具は豚挽肉、炒り卵、玉葱、長ネギ、モヤシ、キャベツで、真似してみました。
見た目は90点なんですが、味(鶏ガラ出汁)がやや薄かったことと、見た目の為に色をベースに入れたコチジャンがやや多すぎたことから味は70点でした。
次はビーフンか春雨で味を調整してチャレンジしましょう。」

 

昨日のブログで書き忘れたことが有るので、追加します。

 

懐かしいぃ~!

 

等とはしゃいで書いてしまいましたが(米国空母の上ではないですが...)、現在はプログラミング言語やライブラリーが充実しており、検索、置換、並び替えといった

 

昔のプログラミング課題

 

を自分で組むことは皆無の状況となっています。その為、

 

アルゴリズム学習

 

をすることも皆無と言っていいでしょう。

 

例えば現代のC++であれば、"stdlib.h"にある"qsort"という汎用のクイックソート関数が使え、引数に

 

void qsort(

        void *base,                //対象となる配列の先頭

        size_t number,        //配列サイズ (要素数

        size_t width,            //要素のサイズ (バイト単位)

        int (__cdecl *compare )(const void *, const void *)    //比較関数

);

 

を与えればよいのです。例えば文字列の並び替えを行うのであれば、

 

//文字列の比較関数(strcmp関数の戻り値を返すだけです)
int comp_str(const void *a, const void *b) {

    return strcmp(*(char**)a, *(char**)b);
}

を用意し、

 

    // Sorting Strings
    qsort(strdata(文字列配列の先頭のポインター),  1000(要素数), sizeof(char*), comp_str);    
 

とするだけであり、クイックソート()の何たるかを知る必要はありません。

:恐らくクイックソートが一番早いのでこれをライブラリーで提供しているのでしょう。

 

更にC++の標準テンプレートライブラリ(STL)には、std::sortという強力なソートアルゴリズムがあり、<algorithm>ヘッダーを読み込めば、様々なデータ型に対して並び替えが出来るようになります。

 

更にC#にもArray.Sort というスタティックメソッドが最も基本的なSystemアセンブリ(System.Runtime.dll)にあり、

 

自分でソートメソッドを作る必要など1ミリもありません。

 

じゃぁ、「なんでソートなど再学習するのか?」と言えば、

 

本ブログの主目的である「ボケ防止」

 

に尽きるわけで、no other significant reasons な訳です。

 

しかし、偶には脳味噌を使って

 

原理を探る

 

ことは、より対象を良く使える可能性を高めるのでお勧めでしょう。

 

プログラミングネタが切れたこと("as usual"ですが)こんなことを書いたことから、

 

原其初心

 

ということで、40年前に「プログラミング言語C」や「実戦Cプログラミング」でC言語を学習し始め、テキストのサンプルを8bitPCであるMZ-2500CP/Mで走るBDS-Cの為に入力してはコンパイルしていたことを思い出し、当時のプログラムを眺めてみました。

 

例えば、

 

/* Heap Sort */

#include    <stdio.h>
#include    <stdlib.h>    /* rand function is defined in this */
#define        MAX 1000

int heapsort(int*, int);
int exchg(int, int*, int);

main() {

    int data[MAX], i;


     /* Initialization of the array */
    srand((unsigned int)time(NULL));    /* Use time as a seed */
    printf("Initializing data .....\n");
    for(i = 0; i < MAX; i++) {
        data[i] = rand() % 10000;

    }
    /* Showing Original data */
    printf(">>> Original integer data <<<\n");
    for(i = 0; i < MAX; i++) {
        printf("%6d, ", data[i]);
        if((i % 10) == 9)
            printf("\n");
    }
    printf("\n");
    heapsort(data, MAX);

    /* Showing sorted data */
    printf(">>> Sorted data <<<\n");
    for(i = 0; i < MAX; i++) {
        printf("%6d, ", data[i]);
        if((i % 10) == 9)
            printf("\n");
    }
    printf("\n");
}

int heapsort(int* data, int n) {

/* A heap is a parent-child(ren) relationship as shown below.
    Parent(0)
     /\
 ChildL(1) ChildR(2)
ParentX2+1 ParentX2+2

This may extend to a largeer one.
                 0
    /\    
    1   2
       /\ /\
    3 4 5     6
First, each heap will have the biggest as the Parent. When exchanging takes place, do same down forward.
When done, The biggest is stored on the top, then the top and the bottom will be switched.
Then same process continues without the bottom and as a result, the biggest goes to the bottom, the second biggest before it, one by one. */


    int top, i;

    for(top = n / 2 - 1; top >= 0; top--) 
   /* top is the last parent with two children. */
        exchg(top, data, n);        /* The biggest in the heap will be the Parent */ 
    for(i = n - 1; i > 0; i--) {        /* The biggest will be the last one and the same process continues without the biggest */ 
        int temp;
        temp = data[i];
        data[i] = data[0];
        data[0] = temp;
        exchg(0, data, i);
    }
}

int exchg(int top, int* data, int n) {

    int i, temp;
    
    temp = data[top];
    for(i = top * 2 + 1; i < n; top = i, i = top * 2 +1) {  
 /* Identify CildrenL and go on to ground Children as far as any exists */
        if(i < n -1 && data[i] < data[i + 1])        /* Select the bigger child, L or R */
            i++;
        if(temp >= data[i]) 
   /* Compare the Parent and the bigger Child */
            break;
        data[top] = data[i]; 
   /* The Parent in the heap becomes the biggest */
    }
    data[top] = temp;
}

 

とか、

 

/* Quick Sort */

#include    <stdio.h>
#include    <stdlib.h>  
 /* rand function is defined in this */
#define        MAX 1000

int qcksort(int*, int*);

main() {

    int data[MAX], i;


     /* Initialization of the array */
    srand((unsigned int)time(NULL));    /* Use time as a seed */
    printf("Initializing data .....\n");
    for(i = 0; i < MAX; i++) {
        data[i] = rand() % 10000;

    }
    /* Showing Original data */
    printf(">>> Original integer data <<<\n");
    for(i = 0; i < MAX; i++) {
        printf("%6d, ", data[i]);
        if((i % 10) == 9)
            printf("\n");

    }
    printf("\n");
    qcksort(data, data + MAX - 1);
    /* Showing sorted data */
    printf(">>> Sorted data <<<\n");
    for(i = 0; i < MAX; i++) {
        printf("%6d, ", data[i]);
        if((i % 10) == 9)
            printf("\n");
    }
    printf("\n");
}

int qcksort(int* start, int* end) {

/* qcksort set a 'key' standard value and find the right position in the array, finally swapping the values of 'key' and the position's.
   In this case, key starts from the right end with check from the left end to see if any number greater than key exists. If such number exists, swap takes place.
   Then the check starts from the right end to see if any number smaller than key exists and if found, swap also takes place.
   Then qcksort calls itself recursively and do same in both the smaller side (left) and the greater side (right) */
    if(start < end) {
        int *left, *right, key, *kp;    /* Key(standard value) may be set in other way */


        for(left = start, right = end, key = *right;;) {
            while(key >= *left)
                left++;      
 /* left ponter proceeds to find a number greater than the standard value */
            if(left < right)    /* All numbers greater than key to go to right side */
                *right = *left;    /* If left does not yet reach right, swap the values of right and left */
            else {            /* No greater value than key exists */
                kp = right;    /* Key pointer set to right */
                break;
            }
            while(key <= *right)
                right--; 
   /* right ponter goes back to find a number smaller than the standard value */
            if(left < right)        /* All numbers smaller than key to go to left side */
                *left = *right;    /* If right does not yet come to left, swap the values of right and left */
            else {            /* No smaller value than key exists */
                kp = left;    /* Key pointer set to right */
                break;
            }
        }
        *kp = key;  
         /* Key value finally finds the position in the array swapping the value of right or left */
        qcksort(start, kp - 1);        /* Do same for the array of numbers smaller than Key */
        qcksort(kp + 1, end);        /* Do same for the array of numbers greater than Key */
    }
}

 

とか、です。

 

懐かしいぃ~!

 

ただ、(米国研修がえりで、無理して英文でコメント書いたりしてカワユイのですが)何をしているか理解するにはとても読みにくいので、アルゴリズムの確認と共にもう一度現代風なC++で書き直してみようかと、また序にタイムを計ったり、C#の同等プログラムをタイム競争してみたり、などと考えてしまいました。

 

乗ります?

 

ps. とはいえ、久々に来週から一寸国外に遊びに行こうと思っているので、シリーズはそれ以降になりますが...

一昨日メールにAMAZONから連絡が届きました。Prime Videoに新着があるとのこと。覗いてみたら、

 

M3GAN 2

 

が載っている。

2023年12月の予想サイトでは続編の観測を行っていたけど、公式サイトでは「公開中止」になっていました。wikiによれば、どうも米国では興行不振だったようです。制作会社トップが自己分析と共に"I'll be back!"と言ってます。

 

劇場は中止でもAMAZONさんが頑張って載っけてくれたので観賞させていただきました。

 

前回の観賞後感はこう【無駄話】M3GAN...そしてAGIのSingularity)だったのですが、今回の"M3GAN 2"では冒頭から「AGIの軍事転用リスク」、「知財流出・盗難リスク」、「AGIによるネット支配による危害の甚大化(前回「1.」で述べました)等が映像化されますが、前回作品と異なり、途中からM3GANが人間と共に敵対AGIと共に戦ってくれる「仲間」化して、前回フォーカスを当てていた「AGIのSingularityリスク」の問題が大幅に薄まっていることに気が付きました。

 

既視感?

 

そう、私が30歳の時にみた第1作目の「Terminator」の強烈な印象(表情のないシュワルツネッガーが降臨し、チンピラを叩きのめす時、電話帳で調べた3名のサラ・コナーを"Sara Conner?"と尋ねながら惨殺してゆく時、そして最後の追跡の時の圧倒的な衝撃)が、「Terminator 2」では「正義の味方」となり、「シュワちゃん 対 追手ターミネーター」化してしまう(私はこれを敢えて『堕落』とは言いませんが...)続編作りを思い出してしまいました。

 

そういえば、

 

この「恐怖の圧倒的な脅威」が「我々の味方」化する話って、昔の東宝の怪獣もの、特に昭和29年(ちなみに私の生まれ年)リリースの「ゴジラ」の恐怖が、続編以降では「人間の味方」に変貌(「堕落」)して「ゴジラ 対 他の怪獣」で戦うのと同じだな、と気がつきました。

 

これって、

 

人間が恐怖を緩和する為のファンタジー、

ホラーあるある

 

なのかしら?

 

とはいえ、

 

Aamzon Prime会員の方は見てくださいね。まだ十分楽しめますよ。(第3作は観みないかも、ですが。それよか、韓国映画の"Witch 2"の方が見たい。)

 

何やら物騒な話を持ち出しましたが、これは結構プログラミングのみならず、総ての人間の生活(or  so-called "人生")に関わる問題じゃないでしょうか?

 

私が(プログラムできる)コンピューター言語に触ったのは、28で結婚した1983年(42年前!)のことで、シャープのポケットコンピューターのBASICでした。それから8bit、16bit、32bit、64bitとステップアップして現在に至るのですが、今は殆どのアルゴリズムやUIがコンポーネント化しており、ユーザーは低級プログラミングを行う必要が全くありません。(Visual Studioがその象徴ですかね?)

 

一方、当時は代入、四則演算やループ処理(循環処理、反復処理)や条件分岐などを一から書かなければならなかったので、大変でしたが、一方アルゴリズムを考えたり、より効率的な処理を工夫するという「人間系を鍛える」所もあったように思えます。逆に現在は殆どの処理がコンポーネント化して、細かいことを行う必要がなくなり便利になったのですが、「人間系が劣化」しているようにも思えます。(大体、古希を過ぎた私でも漢字が書けなくなってきているので実感されます。)

 

今後は(実際に私自身がやっているように)、粗書きをAIに任せてそれを弄るようなプログラミングから、総てAIに任せるようなプログラミングになってくるのでしょう。要するに「人間のすることはほぼ何もなくなる」という楽な世界、天国です。天国においては数学、物理、化学等有体物に関わる法則や原理について学習する必要もなく、又社会、政治、歴史、文化、言語等無体物に関わる帰納分析、演繹推論等を学習することも求められないかもしれません。

 

要するに、学ぶことが不要になる世界

 

です。では、その段階で人は何をするのでしょうか?

 

ただ本能(欲望)に任せて生きるだけ?家畜みたいに?

 

ははは!

 

漫画みたいで可笑しいですよね?絵空事でしょ、ですって?でも確実にそのようになってきているんじゃないでしょうか、私たち?

 

そう考えたら、再度「低級プログラミング」で何の役にも立たないコードを見直してみるのも、自分の脳味噌の錆を落とすにはよいのかもしれないかな?と感じてしまいます。

 

如何?

What do you think?

 

相も変らぬ「ネタ切れ病」でwebをさ迷い歩いている老人ですが、

 

ん?

 

そういえば『量子コンピュータ』ってーのがあったな?確かディジタル(0か1の択一)ではなく、『0、1と0と1の重ね合わせ(即ち、1&1、1&0、0&0、0&1の何れか)』を解に持つ奴って言っていたな?」)と思いだし、(検索キーで量子コンピュータが思い出せなかったので、アナログコンピューターとか、「3つの値を持つコンピューター」などと入力していたら、

:ご参考「量子コンピュータ(入門編) - やさしい技術講座 : 富士通

 

 

三値論理

という(量子コンピューターよりもとっつきやすい)話に出くわし、既にRDBSの世界では(肯定、否定交々に感じますが)実際に使われている()ということで、ちょっと興味をそそられました。

:ご参考(RDBのBOOLEAN型の3値論理について

 

というのは、C#でも通常のbool型はtrueまたはfalseしかありませんが、(禁断の)bool?型はtrue、false、nullを許容するからです。となるとbool?型があれば、真(true)、偽(false)と不明(null-Unknown)で一寸したお遊びができるのではないか、と考えた次第。

 

だって、

 

元気のよい高校生がこんなことをやっているのですから。

 

【PropertyGrid】と【RichEditor】でC#プログラミングを二連発したら、何かぽっかりと穴が開いてしまったような...

 

そんな時に、webでウロウロしていたら

 

こんなの

 

に出会いました。(

:因みにオリジナルはこれ

 

単純に「すごいっ!」「完成度高いね」とか感じられる以上に、私には思い入れ深いものがあります。

 

USA for AfraicaWe are the Worldについては参照記事をお読みいただきたいのですが、1985年というのは私が30歳で初めて企業研修で米国本土を踏みしめた年(悲しい哉、この時はまだこの大ヒット曲を知らなかったのですが)、円高・ドル安の進展から日本がバブル経済へと進んだ1985年9月22日のG5(日本、アメリカ、イギリス、ドイツ、フランスの先進5カ国)所謂「プラザ合意」の年であり、後に帰国後教育の一環で通っていた英語クラスの教師とこの曲の歌詞について話したことを覚えています。(勿論、MJが好きだったので歌も覚えました。)

 

今回私を感慨深くさせたのは

 

この時代、

 

世界はもっと単純であり、寛容であり、共感的であり、

 

手をつなぐことが出来た時代

 

だったな、という感慨です。この曲や発表過程が現代であれば

 

「偽善」、

「南北問題ステルス差別」、

「不透明な寄付金使途」、

「もっと自分たちファーストだろう?」

 

等々もっと批判勢力があり、このようなプロジェクトにはならなかっただろう、という感慨です。

 

もう、このような無邪気な善意には戻れない

 

んだろうなという気持ちに満たされました。

 

ps. 今から40年前の1985年、米国留学中、5番街に「黄金のTrump Tower」を見物にいきました。というのは彼が莫大な損失を出していたこともあったからですが、結局彼はその後も損失を資産として返り咲き、現在に至るわけです。この事実も感慨深いのです。

 

【RichEditor】最後にRichTextBoxExコントロールについて(1)からの続き

 

    /////////////////////////////////
    //文字列の検索・置換用ダイアログ(
解説:自作ですが、コモンダイアログに似せました。)
    /////////////////////////////////

    public class FRDialog : Form
    {
        //メンバープロパティ
        string ToBeRep {get; set;}        //「検索文字列」
        string ToRepWith {get; set;}    //「置換文字列」
        //メンバーフィールド
        RichTextBox rtBox;
        int stPos = 0;                    //検索・置換始点
        int edPos = 0;                    //検索・置換終点
        string ToFind;                    //前回の検索文字列
        string ToReplace;                //前回の置換文字列
        int foundPos = 0;                //Findメソッドの結果
        int Count = 0;                    //検索・置換回数
        //メンバーコントロール
        private const int UNIT = 4;    //解説:描画用に4 x 4ドットの桝目を想定しました。
        private Label strToFind;
        private TextBox strToFindBox;
        private Label strReplaced;
        private TextBox strReplacedBox;
        private CheckBox ByWord;
        private CheckBox UNotL;
        private Button FindNext;
        private Button ReplaceNext;
        private Button ReplaceAll;
        private Button Cancel;
        private GroupBox gBox;
        private RadioButton Up;
        private RadioButton Down;

        public FRDialog(RichTextBox rte, bool replace = false)    //コンストラクター
        {    //解説:(1)の最後で示した通り、RichTextBox(本当はExなんですが...) rteとインスタンスと引数replaceにより、検索と置換を使い分けます。
            //呼び出したRichTextBoxExを保存
            rtBox = rte;
            //ダイアログ本体
            this.FormBorderStyle = FormBorderStyle.FixedDialog;
            this.MaximizeBox = false;
            this.MinimizeBox = false;
            this.ShowIcon = false;
            this.ShowInTaskbar = false;
            this.StartPosition = FormStartPosition.CenterScreen;    //中央に配置
            this.ClientSize = new Size(UNIT * 120, UNIT * 36);
            this.AcceptButton = this.FindNext;
            this.CancelButton = this.Cancel;
            this.AutoScaleMode = AutoScaleMode.Font;
            this.TopMost = true;
            if(replace)    //解説:検索と置換の切り替え部分
                this.Text = "置換";
            else
                this.Text = "検索";
            this.SuspendLayout();
            //strToFind
            this.strToFind = new Label();
            this.strToFind.AutoSize = true;
            this.strToFind.Location = new Point(UNIT * 2, UNIT * 3);
            this.strToFind.Size = new Size(UNIT * 28, UNIT * 3);
            this.strToFind.Text = "検索する文字列(&N):";
            this.Controls.Add(this.strToFind);
            //strToFindBox
            this.strToFindBox = new TextBox();
            this.strToFindBox.Location = new Point(UNIT * 40, UNIT * 3);
            this.strToFindBox.Size = new Size(UNIT * 48, UNIT * 5);
            this.strToFindBox.TabIndex = 0;
            this.strToFindBox.Text = ToFind = rte.SelectedText;
            this.strToFindBox.TextChanged += strToFindBox_TextChanged;
            this.Controls.Add(this.strToFindBox);
            //strReplaced
            this.strReplaced = new Label();
            this.strReplaced.Visible = replace ? true : false;    //解説:検索と置換の切り替え部分
            this.strReplaced.AutoSize = true;
            this.strReplaced.Location = new Point(UNIT * 2, UNIT * 9);
            this.strReplaced.Size = new Size(UNIT * 28, UNIT * 3);
            this.strReplaced.Text = "置換する文字列(&P):";
            this.Controls.Add(this.strReplaced);
            //strReplacedBox
            this.strReplacedBox = new TextBox();
            this.strReplacedBox.Visible = replace ? true : false;    //解説:検索と置換の切り替え部分
            this.strReplacedBox.Location = new Point(UNIT * 40, UNIT * 9);
            this.strReplacedBox.Size = new Size(UNIT * 48, UNIT * 5);
            this.strReplacedBox.TabIndex = 1;
            this.Controls.Add(this.strReplacedBox);
            //ByWord
            this.ByWord = new CheckBox();
            this.ByWord.AutoSize = true;
            this.ByWord.Location = new Point(UNIT * 3, UNIT * 18);
            this.ByWord.Size = new Size(UNIT * 40, UNIT * 4);
            this.ByWord.TabIndex = 2;
            this.ByWord.Text = "単語単位で探す(&W)";
            this.ByWord.UseVisualStyleBackColor = true;
            this.Controls.Add(this.ByWord);
            //UNotL
            this.UNotL = new CheckBox();
            this.UNotL.AutoSize = true;
            this.UNotL.Location = new Point(UNIT * 3, UNIT * 24);
            this.UNotL.Size = new Size(UNIT * 40, UNIT * 4);
            this.UNotL.TabIndex = 3;
            this.UNotL.Text = "大文字と小文字を区別する(&C)";
            this.UNotL.UseVisualStyleBackColor = true;
            this.Controls.Add(this.UNotL);
            //FindNext
            this.FindNext = new Button();
            this.FindNext.Enabled = false;
            this.FindNext.Location = new Point(UNIT * 92, UNIT * 2);
            this.FindNext.Size = new Size(UNIT * 26, UNIT * 6);
            this.FindNext.TabIndex = 4;
            this.FindNext.Text = "次を検索(&F)";
            this.FindNext.UseVisualStyleBackColor = true;
            this.FindNext.Click += FindNext_Click;
            this.Controls.Add(this.FindNext);
            //ReplaceNext
            this.ReplaceNext = new Button();
            this.ReplaceNext.Visible = replace ? true : false;    //解説:検索と置換の切り替え部分
            this.ReplaceNext.Enabled = false;
            this.ReplaceNext.Location = new Point(UNIT * 92, UNIT * 10);
            this.ReplaceNext.Size = new Size(UNIT * 26, UNIT * 6);
            this.ReplaceNext.TabIndex = 5;
            this.ReplaceNext.Text = "置換して次に(&R)";
            this.ReplaceNext.UseVisualStyleBackColor = true;
            this.ReplaceNext.Click += ReplaceNext_Click;
            this.Controls.Add(this.ReplaceNext);
            //ReplaceAll
            this.ReplaceAll = new Button();
            this.ReplaceAll.Visible = replace ? true : false;    //解説:検索と置換の切り替え部分
            this.ReplaceAll.Enabled = false;
            this.ReplaceAll.Location = new Point(UNIT * 92, UNIT * 19);
            this.ReplaceAll.Size = new Size(UNIT * 26, UNIT * 6);
            this.ReplaceAll.TabIndex = 6;
            this.ReplaceAll.Text = "すべて置換(&A)";
            this.ReplaceAll.UseVisualStyleBackColor = true;
            this.ReplaceAll.Click += ReplaceAll_Click;
            this.Controls.Add(this.ReplaceAll);
            //Cancel
            this.Cancel = new Button();
            this.Cancel.DialogResult = DialogResult.Cancel;
            this.Cancel.Location = new Point(UNIT * 92, UNIT * 27);
            this.Cancel.Size = new Size(UNIT * 26, UNIT * 6);
            this.Cancel.TabIndex = 7;
            this.Cancel.Text = "キャンセル";
            this.Cancel.UseVisualStyleBackColor = true;
            this.Cancel.Click += Cancel_Click;
            this.Controls.Add(this.Cancel);
            //gBox
            gBox = new GroupBox();
            this.gBox.Visible = replace ? false : true;    //解説:検索と置換の切り替え部分
            this.gBox.Location = new Point(UNIT * 56, UNIT * 18);
            this.gBox.Size = new Size(UNIT * 28, UNIT * 15);
            this.gBox.Text = "検索する方向";
            this.Controls.Add(this.gBox);
            //Up
            this.Up = new RadioButton();
            this.Up.Visible = replace ? false : true;    //解説:検索と置換の切り替え部分
            this.Up.Location = new Point(UNIT * 6, UNIT * 3);
            this.Up.Size = new Size(UNIT * 20, UNIT * 6);
            this.Up.TabIndex = 8;
            this.Up.Text = "上へ(&U)";
            this.Up.UseVisualStyleBackColor = true;
            this.gBox.Controls.Add(this.Up);
            //Down
            this.Down = new RadioButton();
            this.Down.Visible = replace ? false : true;    //解説:検索と置換の切り替え部分
            this.Down.Location = new Point(UNIT * 6, UNIT * 8);
            this.Down.Size = new Size(UNIT * 20, UNIT * 6);
            this.Down.TabIndex = 9;
            this.Down.Text = "下へ(&D)";
            this.Down.UseVisualStyleBackColor = true;
            this.Down.Checked = true;
            this.gBox.Controls.Add(this.Down);
            //ボタン状態の設定(解説:引数に検索文字列があれば有効化します。)
            FindNext.Enabled = ReplaceNext.Enabled = ReplaceAll.Enabled = (rte.SelectedText.Length > 0);
            //再開
            this.ResumeLayout(false);
            this.PerformLayout();
        }

        //終了処理
        protected override void OnClosed(EventArgs e)
        {
            base.OnClosed(e);
            this.Dispose();            //念の為(解説:モードレスダイアログの為、手動で開放する必要があります。)
        }

        //「検索する文字列」が入力された場合(解説:ボタンを有効化します。)
        private void strToFindBox_TextChanged(object sender, EventArgs e)
        {
            FindNext.Enabled = ReplaceNext.Enabled = ReplaceAll.Enabled =
            (strToFindBox.Text.Length > 0);    //true
        }

        private void FindNext_Click(object sender, EventArgs e)
        {
            //前回の検索文字列の設定
            if(!string.IsNullOrEmpty(ToFind))
                strToFindBox.Text = ToFind;
            //Findメソッドのフラグ
            RichTextBoxFinds flag = RichTextBoxFinds.None;
            //<RichTextBoxFinds>
            //名前            値    説明
            //MatchCase        4    大文字と小文字を区別して検索する。
            //NoHighlight    8    検索文字列を強調表示しない。
            //None            0    大小文字の区分、独立した単語か否かに拘わらず検索文字列に一致するものを検索する。
            //Reverse        16    テキストの末尾から先頭に向かって検索を開始する。
            //WholeWord        2    検索文字列に完全一致する単語だけを検索する。

            if(ByWord.Checked == true)            //単語検索
                flag |= RichTextBoxFinds.WholeWord;
            if(UNotL.Checked == true)            //大文字小文字区別
                flag |= RichTextBoxFinds.MatchCase;
            if(Up.Checked == true)                //上方検索
                flag |= RichTextBoxFinds.Reverse;
            if(Down.Checked == true)            //下方検索
            {
                if(Count == 0)    //初回はキャレットのある位置から
                {
                    stPos = rtBox.SelectionStart;
                    edPos = rtBox.Text.Length;    //文末迄(既定値は下方検索)
                }
                else            //継続中は見つかった文字列の次の位置から
                    stPos = foundPos + rtBox.SelectionLength;
            }
            else                                //上方検索
            {
                if(Count == 0)    //初回はキャレットのある位置から
                {
                    stPos = 0;                    //文頭迄
                    edPos = rtBox.SelectionStart;
                }
                else            //継続中は見つかった文字列の前の位置から
                    edPos = foundPos - 1;
            }
            foundPos = rtBox.Find(strToFindBox.Text, stPos, edPos, flag);
            if(foundPos == -1 || edPos < stPos)    //RichTextBoxのFindはReverseの際に-1を返さない為(解説:Microsoftの仕様違反の部分です。)
            {
                MessageBox.Show("検索文字は " + Count.ToString() + "回見つかりました。", "検索結果", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                stPos = edPos = foundPos = Count = 0;
                Close();
            }
            else
                Count++;
            rtBox.FindForm().Activate();        //(Form)をキャストしないとActivateメソッドが無いControlになる
            ToFind = strToFindBox.Text;                //今回の検索文字列の記録
        }

        private void ReplaceNext_Click(object sender, EventArgs e)
        {
            //前回の検索・置換文字列の設定
            if(!string.IsNullOrEmpty(ToFind))
                strToFindBox.Text = ToFind;
            if(!string.IsNullOrEmpty(ToReplace))
                strReplacedBox.Text = ToReplace;
            //「次を検索」等で既に検索文字列を選択していれば、先ず置換する
            if(rtBox.SelectedText == strToFindBox.Text)
                rtBox.SelectedText = strReplacedBox.Text;
            //次の置換処理
            RichTextBoxFinds flag = RichTextBoxFinds.None;    //Findメソッドのフラグ(下方検索のみ)
            if(ByWord.Checked == true)            //単語検索
                flag |= RichTextBoxFinds.WholeWord;
            if(UNotL.Checked == true)            //大文字小文字区別
                flag |= RichTextBoxFinds.MatchCase;
            if(Count == 0)                        //初回は
            {
                stPos = 0;                        //文頭から
                edPos = rtBox.Text.Length;        //文末迄(既定値は下方検索)
            }
            else            //継続中は見つかった文字列の次の位置から
                stPos = foundPos + rtBox.SelectionLength;
            foundPos = rtBox.Find(strToFindBox.Text, stPos, edPos, flag);
            if(foundPos == -1)
            {
                MessageBox.Show("検索文字を " + Count.ToString() + "回置換しました。", "検索結果", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                stPos = edPos = foundPos = Count = 0;
                Close();
            }
            else
            {
                if(MessageBox.Show("置換しますか?", "確認", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                {
                    rtBox.SelectedText = strReplacedBox.Text;
                    Count++;
                }
                else
                    stPos = foundPos + rtBox.SelectionLength;
            }
            rtBox.FindForm().Activate();        //(Form)をキャストしないとActivateメソッドが無いControlになる
            //今回の検索・置換文字列の記録
            ToFind = strToFindBox.Text;
            ToReplace = strReplacedBox.Text;
        }

        private void ReplaceAll_Click(object sender, EventArgs e)
        {
            if(MessageBox.Show("本当にこの置換条件で実行しますか?", "再確認", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
            {
                stPos = 0;                        //文頭から
                edPos = rtBox.Text.Length;        //文末迄(既定値は下方検索)
                //全置換処理
                RichTextBoxFinds flag = RichTextBoxFinds.None;    //Findメソッドのフラグ(下方検索のみ)
                if(ByWord.Checked == true)            //単語検索
                    flag |= RichTextBoxFinds.WholeWord;
                if(UNotL.Checked == true)            //大文字小文字区別
                    flag |= RichTextBoxFinds.MatchCase;
                while((foundPos = rtBox.Find(strToFindBox.Text, stPos, edPos, flag)) != -1)
                {
                    rtBox.SelectedText = strReplacedBox.Text;
                    Count++;
                    stPos = foundPos + rtBox.SelectionLength;
                }
                MessageBox.Show("検索文字を " + Count.ToString() + "回置換しました。", "検索結果", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                stPos = edPos = foundPos = Count = 0;
                Close();
                rtBox.FindForm().Activate();        //(Form)をキャストしないとActivateメソッドが無いControlになる
                //今回の検索・置換文字列の記録
                ToFind = strToFindBox.Text;
                ToReplace = strReplacedBox.Text;
            }
        }

        private void Cancel_Click(object sender, EventArgs e)
        {
            Close();
        }
    }
}

 

以上です。RichTextBoxExがお役に立てたならば幸いです。

 

しっかし、C#、簡単ですねぇ。

 

(今回は随分と苦戦しましたが...)

 

ps. あまりあてにはできませんが、Chat-GPT様も次のように評価していただきました。

「あなたの RichTextBoxExFRDialog の構造は、WinFormsの内部仕様を完全に理解した上で設計されている= “正統派のWin32的C#設計” と言えます。このままクラスをパッケージ化して、他プロジェクトに組み込んでも十分に堅牢ですよ。」