読者です 読者をやめる 読者になる 読者になる

似非プログラマのうんちく

「似非プログラマの覚え書き」出張版

よくわかるかどうかわからない現代魔法(その 3)

C

今回は「二つの『ルーン文字』の効力を入れ替える『呪文』」を作ってみたいと思う。

第一の失敗

まずは「呪文」を作る前に、どうすれば「二つの『ルーン文字』の効力を入れ替える」ことができるか考えてみよう。素朴に書くとしたらこうだろうか。

#include <stdio.h>

int main() {
    int a = 3, b = 2;
    printf("a = %d, b = %d\n", a, b);
    a = b;
    b = a;
    printf("a = %d, b = %d\n", a, b);
    return 0;
}
int a = 3, b = 2;

のところは丁寧に書くと

int a;
int b;
a = 3;
b = 2;

だが省略して

int a = 3;
int b = 2;

さらに省略するとさっきの書き方になる。それはそれとして、パッと見はこれで良さそうである。ba にして、ab にするんだから。ところが…。

a = 3, b = 2
a = 2, b = 2

おや ? a の「効果」はどこへ消えた ?

失敗の原因は

a = b;

の部分である。この時点で、a の「効果」は b が持っている「効果」に置き換えられ、元々持っていた効果は雲散霧消してしまうのである。「ルーン文字」に「過去の記憶」はない。常に最新の「効果」しか保つことができないのである。

第一の成功 : 一時的な「ルーン文字」

そこで次のように考える。まず a の持っている「効果」を、一時的に別の「ルーン文字」を用いて退避させておく。それから a の「効果」を b の「効果」で置き換え、次に b の「効果」を、あらかじめ退避させておいた a の「効果」で置き換えるのである。

#include <stdio.h>

int main() {
    int a = 3, b = 2;
    printf("a = %d, b = %d\n", a, b);
    int tmp = a;
    a = b;
    b = tmp;
    printf("a = %d, b = %d\n", a, b);
    return 0;
}

今度は

a = 3, b = 2
a = 2, b = 3

とちゃんと入れ替わった。

第二の失敗

では、いよいよ「呪文」を作ろう。「効果」を入れ替える部分を新たな「呪文」として定義する。

#include <stdio.h>

void swap(int, int);

int main() {
    int a = 3, b = 2;
    printf("a = %d, b = %d\n", a, b);
    swap(a, b);
    printf("a = %d, b = %d\n", a, b);
    return 0;
}

void swap(int x, int y) {
    int tmp = x;
    x = y;
    y = tmp;
}
a = 3, b = 2
a = 3, b = 2

おや ? 入れ替わってないじゃないか。

その原因は、「呪文」に何を書き添えたかにある。実はこの「呪文」のままでは、「呪文」に書き添えたものは「『ルーン文字』の『効果』」であって「ルーン文字」そのものではないのである。そしてこの「呪文」は void 型、すなわち「魔法」本体には何も返さない。なので、元々の ab には何の影響も与えないのである。

どうにかしてこの「呪文」に、「ルーン文字」そのものを書き添えてやることはできないだろうか ?

第二の成功 : 「ルーン文字」そのものを「呪文」に書き添える

#include <stdio.h>

void swap(int *, int *);

int main() {
    int a = 3, b = 2;
    printf("a = %d, b = %d\n", a, b);
    swap(&a, &b);
    printf("a = %d, b = %d\n", a, b);
    return 0;
}

void swap(int *x, int *y) {
    int tmp = *x;
    *x = *y;
    *y = tmp;
}

まず、「呪文」を変更して、「ルーン文字」そのものを書き添えられるようにする。普通の「ルーン文字」は他の「ルーン文字」や「呪文」の「効果」だけを受け入れるが

void swap(int *x, int *y) {
    ...
}

xy は「ルーン文字」そのものを受け入れることができるのだ。それらの「効果」だけを呼び出すには *x, *y のように書く。

そして、「魔法」本体側でこの「呪文」を使用するときに

swap(&a, &b);

と書いたのは、a, b と書くと「効果」だけが呼び出されるので、「ルーン文字」そのものを呼び出すために &a, &b と書いている。

この辺は C 魔法の中でもかなり高等な技術で、C 魔法使い見習いがよく躓くところである。今回の要点をまとめると

  • 「ルーン文字」は「過去の記憶」を持たないので、必要に応じて別の「ルーン文字」に「効果」をコピーする必要がある。
  • 「ルーン文字」そのものを表す「ルーン文字」が使える。「ルーン文字」そのものを表す「ルーン文字」x について、その「効果」を呼び出すときは *x と書く。
  • 通常の「ルーン文字」a で「効果」ではなく「ルーン文字」自身を呼び出すときは &a と書く。