一昨日卒業式があった。今年は私が心配していた学生の多くが卒業できた。 (元々かなり単位数が不足していた学生が一人留年となったが、 とても前向きになっていて (元気のなかった頃を知っているのでとても嬉しい)、 来年は大丈夫そうに思えた。) -- 大学教員にとっては、学生の卒業は時候の挨拶のようなものだけれど、 実は今回の作業の動機とつながっている。
春学期に 「Apple Silicon Mac で GLSC をコンパイルする」 というのを書いた。Apple Silicon Mac の cc (Xcode に含まれているものというか、 Command Line Tool に含まれているものというか) で GLSC がコンパイルできなくなったけれど、原因は分かって、 コンパイルできるようになった、というものである。
rm -rf sagyou mkdir sagyou cd sagyou curl -O http://www602.math.ryukoku.ac.jp/~nakano/software/math/glsc-3.5.a.tar.Z curl -O https://m-katsurada.sakura.ne.jp/daishin/glsc-3.5+a.patch curl -O https://m-katsurada.sakura.ne.jp/program/graphics/glsc-3.5+mk.patch20240718 tar xzf glsc-3.5.a.tar.Z cd glsc-3.5.a patch -p1 < ../glsc-3.5+a.patch patch -p1 < ../glsc-3.5+mk.patch20240718 make |
やらなかった理由は、そもそも動かしたいと思う Fortran プログラムがないからで、 それは今も変わっていない。 だからまあ放っておいても良いのだけれど、今回やってみる気になった。
その動機は、ChatGPT をプログラミングに利用する経験をしてみたい、 ということである。 自分がやりたいプログラミングの多くは、AI を使わず自分で出来るのだが、 あえてChatGPTを使った経験値をあげたい、と考えた。 その理由を少し説明する。
私のゼミではコンピューターで数値実験をする学生が多いのだが、 今年度卒研はこれまでになかったような苦戦をする人が多かった。 振り返ってみて、 その大きな原因の1つに、 ChatGPT とのつきあい方が上手く行かなかったことがあるのかな、 と考えている。 ゼミで数値実験をさせるというのは、もう30年近くやっていて、 初期の色々な試行錯誤が過ぎた後、 「こういう場合は、こうすればそのうち解決する」 というのが何となく分かって、以下ずっとそれでやってきた (それでまあまあ上手く行った)。 前年度のゼミから、ChatGPTを使う人が出始めて、 それまでプログラミングを避けて来たような人が上手く使いこなして、 かなり上手くやってのけたケースも出て来ていた (作業の進展が明らかにそれまでの相場より早い)。 でも、やはり良いことばかりではないみたい。 今年度はかなりの学生が ChatGPT の力を借りたようなのだけれど (やめろというつもりは全くない)、 苦戦をする人が少なからずいて (これ自体はいつだってそうだ。なんでもスイスイ出来るはずはない。)、 でもその苦戦の状況がこれまでと違っているので、 「こうすればそのうち解決する」 が通用しなかった。 ゼミで書きかけのプログラムについて検討しているとき、 「その部分のコードはどういう意図ですか?」に学生が答えられない。 だって、自分が書いたものではないから。どうしたいか分かっていれば、 「そういう場合は、例えばこういうコードにすればいいですね」 とかアドバイスできるのだけれど。 ChatGPT はここで何をしたいのか見当もつきません。うーん。
「こういうことをするプログラムを作りたい。」 これを AI に要求すること自体はおかしいことではないと思う。 それである場合にはすんなり通って、ほぼ完成版に近いプログラムが得られ、 小修正で完成となるけれど (レポートにするにはちゃんと説明できないといけなくて、 それはそれで大変だ)、 全然見当違いのものを出して来てくれる場合が少なくない。 どうすればそれに気づけるか、気づいた場合にどうやって方向修正をするか。 そもそも、 どういう場合は上手くやってくれて、 どういう場合に上手くやってくれない可能性が高いのか。 「AIの伝道者」達は上手く行ったケースばかり宣伝するけれど。 上手く行かない場合にどうすれば良いか (「セミナーを受講しましょう」とか言われるのかなあ)、 そういうノウハウを自分なりにためる必要がありそうだ、と考えた次第である。
それで、 自分だけで出来そうなプログラム作成にも、ChatGPT を使って省力化をはかり、 また自分だけでは出来そうもないので、 挑戦も考えなかったプログラム作成にも挑戦してみる、 そういうのをやろうかなと。
さて、それで今回の GLSC を Fortran で使う件だけれど、 昨年7月の段階で問題は絞り込めていて、 libs/src/g_sformat.c を Apple Silicon でも動くように書き直せば良い、 ということは分かっていた。 いわゆる VARARG を使っていて、オリジナルは、 色々なシステムの違いに自動的に対処するようなコードになっていた。 それは私には到底書けないような代物で、理解して修正するのも難しい。 でも今は GLSC が開発された頃とは状況が全然違っていて、 利用するシステムはほぼ収斂していて、 古いシステムに対応することを放棄すれば、 すっきりしたプログラムが作れるだろう。
実は、最初、オリジナルのソースプログラムと、 それが Apple Silicon でどういう実行結果になるかを ChatGPT に伝えることで プログラムが修正できるか試してみたのだかが、 結構長い時間をかけた末に解決しなかった。 ChatGPT は、こちらがもううんざりしかてたときにも、 元気いっぱいに「それではこうしてみましょう」 と修正案を出して来てくれるのだけれど、 一向に直らず、そのうち「こうしたい」という希望から段々とズレていく。 こういうところは、苦戦している学生の状況に近いのかなあ、と考えてみたり。
ごはさんで願いまして。 自分でやる場合の補佐をしてもらう、という方向で行こう。 まず stdarg の使い方を理解する。何か適当なサンプル・プログラムが欲しい。 ネットで検索してみて、本当はどこかに適当なものがあるのだろうけれど、 なかなかそういうものには行きつかない。 サンプル・プログラム作成は ChatGPT にやってもらう。 えーと、こういうことがしたいのですけど。
int main(void) { char buffer[256]; int len; my_sprintf(buffer, &len, "a=%4d, b=%6.2f", 123, 456.0); printf("結果: \"%s\"\n", buffer); printf("長さ: %d\n", len); return 0; } |
% ./test_mysprintf 結果: "a= 123, b=456.00" 長さ: 16 |
#include <stdio.h> #include <stdarg.h> #include <string.h> #include <ctype.h> void my_sprintf(char *buf, int *length, const char *format, ...) { va_list args; va_start(args, format); char *out = buf; const char *p = format; int total_len = 0; while (*p) { if (*p == '%') { const char *start = p++; // % の次へ // 幅・精度のスキャン while (*p && (isdigit(*p) || *p == '.' || *p == '-' || *p == '+')) p++; char type = *p++; char fmtbuf[32]; int len = p - start; if (len >= sizeof(fmtbuf)) len = sizeof(fmtbuf) - 1; strncpy(fmtbuf, start, len); fmtbuf[len] = '\0'; char tmp[128]; if (type == 'd') { int val = va_arg(args, int); snprintf(tmp, sizeof(tmp), fmtbuf, val); } else if (type == 'f') { double val = va_arg(args, double); snprintf(tmp, sizeof(tmp), fmtbuf, val); } else { snprintf(tmp, sizeof(tmp), "<?>"); // 未対応 } strcpy(out, tmp); out += strlen(tmp); total_len += strlen(tmp); } else { *out++ = *p++; total_len++; } } *out = '\0'; // 終端 *length = total_len; va_end(args); } |
そうした上で、 本来の目的(Fortranプログラムから呼び出せるようにしたい)に近づけた。 同じようなことをする目的で、関数の呼び出し方を変えてみたい、 具体的には
int main(void) { int a = 123; double b = 456.0; char buffer[256]; int len; my_sprintf(buffer, &len, "a=%4d, b=%6.2f", &a, &b); printf("結果: \"%s\"\n", buffer); printf("長さ: %d\n", len); return 0; } |
あとはトントン拍子。g_sformat.c が完成した(と思った)。 書き出してから3時間弱だ。
GLSCに用意されているサンプル・プログラム ftests, ftestd が落ちなくなった。
ChatGPT万歳!!
…しかし、 よく見ると動作がおかしい!!
C 言語レベルではうまく出来ているようだけれど、 Fortran から呼び出すとうまく行かない。落ちないけれど、値がちゃんと伝わっていない。
また ChatGPT と相談。 すると、ぐるぐるおかしな忠告をやり始めた。 「その原因は〇〇です。A やって下さい。」 「その原因は□□です。AでなくBをやって下さい」。 「その原因は〇〇です。BでなくA やって下さい」… 「ちょっと待ってよ。おかしいでしょう?」 「ごめんなさい。」
ダメなやつ。でも、話し相手になってくれて、 引数をprintf()でダンプしましょうとか、 アセンブリ言語のソースを出力して どうやって引数を渡しているかチェックしましょうとか、 デバッガーを使ってとか、とか色々言い始めた。 デバッガー以外は全部やってみた (デバッガーやっていないのは、インストールしてなかったから。 そのうちやってみよう。)。
どうも gfortran が引数をレジスター渡しにしているようで、 スタック上に引数がないみたいで、それが原因で varargs 動かない (なのかなあ??)。 見間違いかもしれないけれど、スタック上に引数が見つからない。
色々やっているうちに、 ChatGPT が間違ったことを言っていたことがわかったりした。 Fortran では、 文字列の長さを暗黙に引数として渡したり、そうしなかったりする、 と言われたけれど、 少なくとも gfortran は調べた限り必ず文字列の長さを引数として渡している。 渡し方がちょっと分かりにくくて、勘違いする人がいるかもしれないけれど。 まあ、それが分かる程度に調べてみたのだけれど、解決出来なかった。
Apple Silicon Mac 上の Fortran で苦労している人達が多かったけれど (例えば MATLAB 出て来るのにあんなに時間がかかるとは思わなかった)、 その理由はもしかすると、こういうあたりにあるのかなあ。
仕方がないので、 (これまで気が進まなかったけれど) 引数の型ごとに別の g_sformat() を用意することにした。
Fortan プログラムの方で、これまでは何であっても call g_sformat(text, length, 書式文字列, 文字列に埋め込みたい値) としてきたのを、値の型に応じて、 別々の関数(サブルーチンというべきか) を呼び出すように直す必要がある。 既存のプログラムを書き換える必要があるのはとても嫌だけれど、諸般の事情により、 どうも仕方がないみたい、ということが分かったので、決心がついた。
(ちなみに gfortran は1つの Fortran ソースプログラム中に、
integer n real a ... call g_sformat(text, length, 'n=%d', n) ... call g_sformat(text, length, 'a=%f', a) |
(気持ちを切り替えて) そうだ。大抵の場合は、 そのプログラムで使っている実数を文字列に埋め込むわけだから、 g_sformat_(char *text, int *len, char *fmt, G_REAL *) とすると、サンプルプログラムは無修正で動くのか。 整数埋め込みたければ call g_sformat_i(...) とする、これくらいは受け入れられるであろう。そうしよう。
ちなみに G_REAL というのは、 glsc.h の中で次のように定義されているマクロである。
#ifdef G_DOUBLE typedef double G_REAL; #else typedef float G_REAL; #endif |
G_DOUBLE なんて使っていない、という人が多いかもしれないけれど、 サンプル・プログラムはちゃんとヘッダーファイルをインクルードしているので、 これで動いた。
長くなったけれど、
こうすれば動きます |
rm -rf sagyou mkdir sagyou cd sagyou curl -O http://www602.math.ryukoku.ac.jp/~nakano/software/math/glsc-3.5.a.tar.Z curl -O https://m-katsurada.sakura.ne.jp/daishin/glsc-3.5+a.patch curl -O https://m-katsurada.sakura.ne.jp/program/graphics/glsc-3.5+mk.patch20240718 tar xzf glsc-3.5.a.tar.Z cd glsc-3.5.a patch -p1 < ../glsc-3.5+a.patch patch -p1 < ../glsc-3.5+mk.patch20240718 curl -O http://nalab.mind.meiji.ac.jp/~mk/misc/20250328/g_sformat_v3.c mv libs/src/g_sformat.c libs/src/g_sformat.c.org cp -p g_sformat_v3.c libs/src/g_sformat.c make |
ただし、実際に利用する場合には、 既存の Fortran プログラムを書き換える必要があることが多いであろう。
結局のところ、今回 ChatGPT はどの程度役に立ったのかなあ。 本質的なことは直接教えてくれなかったような気がする。 実際に調べて考えるのは自分。話し相手程度?
もっとも人間の場合も大して変わりないような気もする。 役に立つことを直接的に教えてくれた人はあまり多くない。
一方、結局問題を解決したのは自分かもしれないけれど、 一人だけでやっていてはたして出来たかどうか? アドバイスや話し相手は大事であると思っている。
なんか、おかしなこと言ってるかしら。
ゼミ生には、 せっかく長い時間を投入するからには、 達成感があるような経験をさせてあげたい、 とつねづね思っているのだけれど、 今年度はその点に関してはうまく出来なかった。申し訳ない。 せめて次はもう少し上手く出来ますように。
桂田 祐史