2023年11月23日木曜日

つれづれ思うこと 外国語編 その1

Perl 編を 10 編ほど書いてきたが、だんだんと書くことがなくなってきてしまい、次はどうしようかと思った時、ふと同じく「言語」というジャンルで「外国語編」を書きたくなってしまった。

英語を勉強し始めてからもうかれこれ 40 年以上も経っていることに、年齢の引き算をして初めて気づいた。それほど力を抜いた訳でもなく、ちょっとずつながらも努力を続けてきた "つもり" ではあったが、「英語」は、いや「英語」もやはりダメだった。特にリスニング(昔はヒヤリングと言ったが)がダメ。年齢とともに、日本語でも細く高い声が聞き取りにくくなってきた。いつも視るテレビのデフォルト音量もこの 10 年間で確実にアップしている。ましてや、イギリス英語の1万ヘルツを超える音声ともなると、風のささやきのようにしか聞こえない。

それではスピーキングは大丈夫なのかというと、これまた怪しい。日本語でも会話の中で人の名前は7割以上は出てこない。年齢の近い友人どうしでは「あの A さん」で会話を通すことが多い。翌日になっても「A さん」の本名が出てこない。名前に限らず、頭の中にはその物体が浮かんでいるのに、それを表す名詞が出てこない。これが英単語になってくると、もっと酷くなる。

数年前に、通勤の途中で単語集でも暗記しようと奮起したことがある。しかし、前日どこのページまで進んだかすら忘れてしまう始末である。「歳をとっても暗記力は変わらない」という研究結果があるとどこかで読んだことがあるが、それは絶対に嘘である!というより、実験の仕方が悪いと固く信じている。

今、大学生ぐらいの人たちに言っておきたい。スマホを触っている時間があるなら、それを少しでも削って英語を勉強しなさい。若い時の暗記力は 30 年後の 10 倍以上はある。

とは言え、Google 翻訳、DeepL、そして ChatGPT の登場により、果たして英語を勉強する必要はあるのか?と私自身も時々ふと思うようになってしまった。英語にかける莫大な時間と労力を、もっと数学や理科(理系ならば)にかけた方が良いような気がしてきた。ほとんどの人にとって外国語はあくまでツールであり、何かを論理的に考えるための土台とはならない。

もしかすると、30-40 年後は母国語をきっちりと間違いなく書くことが最重要視され(今でもそうなのであるが)、それさえ出来れば、あとは AI を含めて情報科学が確実に正しい諸外国語に翻訳してくれるのかもしれない(逆翻訳もまた然り)。誤訳されたとしたら、それはそもそも母国語が間違えているためだと判断されるほど、自動翻訳技術が発達するかもしれない。いや、きっとそうなるだろう。

すると「英語」は何のために勉強するのか?このような思考が頭の中をぐるぐると回り、この1年間ほど自分の頭の中で結論を出せなかった。

ところで、非難轟々を覚悟でいうが、英語ができる人は恰好よく(かっこよく)見える。頭も良いように見えてしまう(実際、その場合もある)。特に学会などで、日本人がアメリカ発音でべらべらと外国人と質疑応答をしようものなら、「あれは誰だろう?」と振り返ってしまう。一方、これがカタカナ発音のしどろもどろ英語であったりすると、何の感情も起きない。これは何故だろう?同じスピードでしゃべったとしても、アメリカ発音とカタカナ発音とでは、日本人のそれに対する見方に雲泥の差があるように思う。

それでは、米語発音の中のどれが聞いている日本人に一種の羨望のような感情を引き起こさせるのだろうか?筆者は「R」と「T」の音にあるのではないかと思っている。米語では「R」は舌を上に反り返らせて発音する傾向がある。そして「T」はラ行のように発音する。例えば、water は「ワラー(& 舌反り返し)」letter は「レラー(& 舌反り返し)」という具合にである。この二つの音は日本語にはなく異質である。そこに欧米文化へのあこがれや西部劇俳優のかっこよさ等が加わり、結果として英語ペラペラの人がかっこよく見えるのではないだろうか?

かく言う私も大学生の時には、この発音にあこがれ、アクセントを徹底的に調べつつ「R」と「T」を米語式に、そして「TH, F, V, W」を native 発音に近くなるように、まるで何かにとり憑かれたかのように練習した。さらに母音では「A」を「ア」と「エ」の中間音にしたり、「O」を極端に「ア」と発音したり(top は「タップ」に)した。いずれもそれが正しいというか唯一であると思い込み、またそうすることでかっこよく見れらたかったからである。

しかし、この 10 年間ほど、今はこれらの発音を極力しないように、どちらかというとカタカナ発音に近くなるようにむしろ努力している。どちらかというと米語ではなくイギリス英語に近い発音とでも言おうか、しかし、これまたイギリス英語を発音の手本とするのではない。最終目標は、アメリカ英語、イギリス英語のどちらへにも偏らず、それでいて世界中の誰にでも間違いなく伝わるような発音である。

なぜそれを目指すようになったかは続きでおいおい語ることにするが、要は、英語はもはや世界共通語となってしまったので、どこかの国の訛に固執するのも変ではないかと思ったためである。例えば、日本国内でも大阪のテレビニュースで司会者が「ほな次行くで。さっき大阪市で火事あって電車止まってん」などと発話することはまずない。ちゃんとした標準発音でしゃべられる。これは極端な例であるが、英語でも同じように、イギリス、アメリカに特化せずに、しかし英語に共通した発音は守りつつ、しゃべることは出来ないだろうか?と思う。

しかし、もう 40 年間もアメリカ発音こそが英語と思い込んできただけに、今さら water を「ワラー」ではなく「ウォーター」と発音するのは結構しんどい。授業でも最初は標準日本語でしゃべっていても(多くの受講生はそうなっていないと主張するが、私本人はそのつもり)、授業が遅れてくるとつい「そいで電子が葉緑体の膜ん中渡ってく時に、水素イオンがどさくさ紛れで汲み出されるんや」といった調子になってしまう。その方が速くて疲れない。

発音は運動と同じで、毎日少しずつでも練習していないとすぐに口と舌の筋力が落ちてしまい、上手く発音できなくなる(若い人はまだそれほど実感はしないと思うが)。「R」と「L」をひっくり返して発音してしまうことなんてザラである。これからも練習を続けていきたい。

ちなみに、上記の「そいで電子が...」の関西弁、BARD は完全に正確に英訳してしまった。恐るべし。

アメリカ英語では I don't want to ... を「アイダナワナ」などと発音する。この音をローマ字で表記すると「aidanawana」となる。このようにアメリカ発音はローマ字でいうところの「a」音が多いような気がする。一方、関西弁もローマ字で表記すると「a」が多いような気がする。

関東弁「それでは、しないといけないですね。」
関西弁「ほな、やらなあかんやん。」

これをローマ字で表記すると
関東弁「soredewa sinaito ikenai desune」i が多い
関西弁「hona yarana akanyan」a が多い

これは偶然の一致というか、いかに口をパクパクしないで、流れるように話そうとすると、このように a 音が自然に多くなるためではないかという気がしている。よって、西部劇ではタバコを加えたクリントイーストウッドが口をあまり動かさないで「かっこよく」話す。一方、シャーロックホームズがドラマでこれをしようとすると、口からタバコが落ちてしまうだろう。

2023年10月15日日曜日

つれづれ思うこと Perl 編 その9

Mathematica が他のプログラミング言語と異なる点の中で、筆者が面白いと思ったことを紹介します。

x = 3;
y = 4;
ab = {x, y};
ab

と書くと、ab には {3, 4} という具体的な数値が代入されます。後から x や y の値を変えても ab は変わりません。これは、一般的なプログラムと同じです。ところが、

ab = {x, y};
x = 3;
ab

というように、今度は行の順番を変えてみると、一行目で ab には {x, y} という文字式が代入されます。そして、面白いことに、二行目で x や y の文字変数の値を変えると、ab も {3, y} に変わるのです。続けて、

x = 8;
ab

と打つと、なんと ab は {8, y} に変わります。ab の中の x という文字はまだ生きているのです。これは Perl などの一般的なプログラムとは異なる点です。

詳細は Mathematica の「遅延割当」という項目をご覧ください。

これは Mathematica が数値計算だけでなく数式処理もできるためですが、いま大変重宝しています。例えば、ある時は数値計算を、またある時には数式処理をしたいとします。いずれにしても、ab = {x, y} のような公式にあたる箇所を先にプログラミングで組み立てておきます。もし、数式処理をしたければ、これでそのまま数式での答を得ることができます。

一方、数値計算をしたい場合には、ここに具体的な数値(たとえば、x = 8)を代入すれば数値としての答を得ることができます。次に x = 4 の場合を計算したければ、続けて x = 4 を実行するだけです。Mathematica は Jupyter のように使うことができるので、二度も ab = {x, y} を実行する必要はありません。実は、再び ab = {x, y} を実行してしまうと、ab は {8, y} となってしまい、もう x = 4 のケースを計算できなくなってしまいます。

上記のような書き方は数値計算に特化されていないので、実は余計に計算時間がかかってしまうのですが、いろいろな数値を入れて答を比べてみたい時には助かります。

よって、次のような計算もできます。

d = e;
e = f;
f = 8;
d

d を表示させると、8 になっています。
これは、普通のプログラミング言語とは行の順番が真逆です。

ややこしくなりますが、次のような配列を含む演算でも同じことが言えます。

blist = alist; (* 先に変数を結び付けておく *)
alist = {2, 4, 6, 8, 10}; (* そして alist に偶数の配列を代入する *)
alist (* 試しに alist を表示 *)
{2, 4, 6, 8, 10} (* もちろん alist は偶数の配列 *)
blist (* そして blist も表示 *)
{2, 4, 6, 8, 10} (* blist も同じ偶数の配列 *)

alist[[3]] = 100; (* それでは alist の3番目の要素に 100 を代入 *)
alist (* を表示させてみる *)
{2, 4, 100, 8, 10} (* ちゃんと3番目の要素は 100 に変わっている OK *)
blist (* それでは blist は? *)
{2, 4, 100, 8, 10}

先に blist = alist のように公式を組み立てておくと、このような結果になります。

Perl ではこのような行の順番で書くことはできませんので、下記のように上の行から下の行へと水が流れ落ちるように書くことになります。

#!/usr/bin/perl

@alist = (2, 4, 6, 8, 10);
@blist = (1, 3, 5, 7, 9, 11);
@blist = @alist;
$alist[2] = 100;
print "alist = @alist\n"; # 2 4 100 8 10 と表示
print "blist = @blist\n"; # 2, 4, 6, 8, 10 と表示

以上より @blist = @alist という表現では、alist のコピーが blist に複写されているようです。つまり、alist の先頭アドレスが blist に代入されたわけではありません。

しかし、次のように書くと、

#!/usr/bin/perl

@alist = (2, 4, 6, 8, 10);
@blist = (1, 3, 5, 7, 9, 11);
$blist = \@alist;
$alist[2] = 100;
print "alist = @alist\n"; # 2 4 100 8 10 と表示
print "blist = @$blist\n"; # 2 4 100 8 10 と表示
print "blist = @blist\n"; # 1 3 5 7 9 11 と表示

$blist には @alist の先頭アドレス(レファレンス)が入り、それとは別に @blist の配列そのものはちゃんと維持されるようです。

話が急に配列そのもののコピーか、それともアドレス(参照)のコピーかに飛んでしまいましたが、Perl, Python, JavaScript とごちゃまぜに書いていると、いつも分からなくなって困ってしまう点です。どこかに比較対照表のようなものをまとめたブログはないでしょうか?

おそらく Python では次のようになります。

a = [1, 1]
b = a
a[1] = 2
print(b) #[1,2]が表示される

多くの HP には「数値はイミュータブル(変更不可)、一方、リストはミュータブル(変更可能)」などといった説明が載っており、それはそれなりに説得力があるのであるが、Perl と比較しながら理解しようとすると混乱してしまいます。上記の Python は、いわば C 言語でいうところの、配列名はその先頭アドレスを表すという概念と似ています。

Python でリスト(配列)そのものをコピーするには、
b = copy.copy(a) # 浅いコピー
としないといけないようです。二次元以上の配列になってくると、またちょっと複雑。

b = copy.deepcopy(a) # 深いコピー
を使わないといけない。

一方、Perl では
@blist = @alist;
と書くと、配列そのもののコピー(複写)になることに注意しないといけません。

Mathematica でも同じ複写ですが、blist = alist を先に書いてから、後で具体的な数値の配列を alist に入れるという方式をとると、これはアドレス渡しのように振る舞うことになります。

いや~もう疲れた。もう JavaScript まで行きたくない。が、ついでだ!思い切ってやってみよう。もう混乱してきたので、詳細は他の情報源に譲ることにして要点だけ。

JavaScript の配列では参照渡しとなるようです。配列を値渡しに(複写)するには、slice や concat の引数なしを使うようです。

var arr1 = [0, 1, 2, 3, 4];
var arr3 = arr1.slice();

ということは C 言語と似ているということ?

C 言語では、配列名は先頭アドレスを指しますよね。
なので、b = a の後で、a の中身を変えると、b も変わって見える。

C 参照渡し
a[3] = {1, 2, 3};
b = a;

Python 参照渡し
a = [1, 2, 3]
b = a

JavaScript 参照渡し
a = [1, 2, 3];
b = a;

Perl 値渡し
@a = (1, 2, 3);
@b = @a;

ただし、$b = \@a; のように、b をポインタ変数のように使うことも可能。この場合は、それ以降 @$b の形で配列 b を使う。「\」は、C 言語のアドレス演算子「&」と似ている。

Mathematica 1 値渡し
a = {1, 2, 3}
b = a

Mathematica 2 参照渡し
b = a
a = {1, 2, 3}

こうして見てみると、Python は、まあ C と似ているのかも。
Perl や Mathematica がむしろ異端児なのか?

以上は一次元配列での話でしたが、NMR データのように二次元配列の場合、配列のコピーはどのようになるのでしょう?おそらく C 言語では memcpy を使う?

もう限界なので、ここでやめておきます。

2023年9月26日火曜日

つれづれ思うこと Perl 編 その8

では次に、最近少しは慣れてきた Mathematica について書いてみたいと思う。おっとまずい、どんどん Perl の話題から遠ざかっている。両者の間に何か関連はないだろうか?

このソフトは高価なのですが、無料で楽しめる方法が少なくとも2つあります。一つは、Raspberry pi です。これに Mathematica を無料でインストールできるのです。その代わり速度は遅く、10 倍ぐらいの処理時間がかかってしまうかもしれません。Mathematica は購入しても一つの PC にだけしかインストールできない仕組みになっています。そのため、筆者も家では Raspberry pi にのんびりと計算をさせ、翌日に職場の PC で本格的に計算させていました。Raspberry pi の OS には 32-bit と 64-bit があり、Mathematica は前者にだけインストール可能でした。しかし、今 HP を見てみると、2023/May からは 64-bit でも使えるようなことが書かれています。64-bit 版ですと、メモリーを 8 GB などと積めるので、Mathematica の動きも速くなるかもしれません。まだ試したことがないので、時間の空いた時に試してみたいと思います。

もうひとつ無料で楽しめる方法は、Jupyter lab を使う方法です。実は Mathematica という名前が指しているのは GUI 部分(という表現は変ですが)のことで、実際に計算しているコア部分は Wolfram-engine と呼ばれています。その engine 部分が無料で PC にインストールできるようになりました。すると、Windows のコマンドプロンプトなどから、計算を打ち込むことができます。しかし、いくらなんでも、これは使い勝手が悪い。そこで engine を Jupyter lab につなげると、ちょうど Python を Jupyter lab で操作するのと同じように Wolfram-engine を使うことができるようになります。もちろん Mathematica の方がインターフェースとしてはよいのですが、この Jupyter lab は Mathematica とよく似ているので(というより、Mathematica を見本として作られた?)まずまずのことができます。なお、計算能力は全く同じです。Mathematica ではできて、Wolfram-engine & Jupyter lab ではできないということはありません。今、家では Raspberry pi をやめて、この第二の方法を活用しています。Jupyter lab の上では、グラフをマウスで拡大したり回したりはできないのですが、これもコマンドベースで命令すればできますので、まあなんとかなります。PDF に出力させれば、論文用の高分解能の図も作れます。

この Mathematica は Lisp と呼ばれる言語で作られているらしく、いわゆる関数型言語に分類されるそうです。関数型が何かという専門的な部分は、私にはよく分かりません。使い始めてちょっと驚いたのは、例えば次のような IF 文です。

y = If[x < 0, -x, x]

これを普通?の言語で書くと、

if ($x < 0)
{
    $y = -$x;
}
else
{
    $y = $x;
}

となります(むりやり Perl にしてしまった)。。。

これだけだと特に変わった言語のようにも見えないのですが、もし、else 文の中に複数の行があると、次のようになります。「もし x が負の数ならば -x を y に代入する。逆に正の数ならば、x に 3 をかけて平方根をとり、これを y に代入する。」

y = If[x < 0, -x, z=x*3; Sqrt[z]]

よって、"z=x*3; Sqrt[z]" の部分がまとまって、まるで If 文の第三引数であるかのように振る舞うのです。この時の「;」が重要で、これで "z=x*3" と "Sqrt[z]" の二つが繋がって、あたかも一つの引数であるかのように振る舞います。よって、"Sqrt[z]" の後にまで「;」を付けてはいけません。もし付けてしまうと、もう一つ後ろに何らかの表現が続くことになってしまいます。

さらに、If 文が最終的に Sqrt[z] を返してくる、そして、それが y に代入されるという点も興味深いところです。このように If 文そのものが関数となっているわけです。Excel の =If もそうなっていたような気がする。

筆者は、どうも C, Perl などの癖が出てしまい、Mathematica でも以下のように書いてしまいます。各文末の「,」や「;」に気を付けないといけませんが、これですと一般的な手続き型言語に似た構文となります。ここでも Sqrt[z] の後に「;」を付けないように気をつけないといけません。

(* の箇所はコメントとして無視されます。 *)

If
[
    (* if *) x < 0,
        y = -x,
    (* else *)
        z = x*3;
        y = Sqrt[z]
]

このように If も For も全てが関数として作られており、この関数の引数の中に別の、あるいは同じ関数を入れ込むことができます。この「引数の中に関数を書く」という操作をどんどん繰り返していくと、入れ子(ロシアのマトリョーシカ人形)のようになります。そんな構文を何に使うのかというと、漸化式です。Mathematica においても For 文を使わずに、この漸化式(数学的帰納法)のコンセプトで書く方法があるそうですが、私にはとてもできません。筆者の知っている人で OCaml が大好きという方がおり、何故かと尋ねたところ、一つの理由はこの関数型にあるのだそうです。

2023年9月16日土曜日

つれづれ思うこと Perl 編 その7

まだ Python の NumPy の存在に気づかなかった 2000 年代前半の頃、Octave という名の数値計算用ソフトがあることを知り使い始めました。これは便利で、今まで Perl などを使って書いていた面倒な数値計算が、たいへん簡単に行えるようになりました。Octave では「for 文を使ったら負け」という表現がよく出てきます。つまり、行列の計算などをする際に、できるだけライブラリの関数を使うようにせよということです。そうすると高速で演算できるのですが、それを安易にうっかり for 文を使って行と列の要素をひとつずつ繰り返しながら計算しようものなら、たいへん遅くなってしまいますよという意味です。よって、たえず「この計算は行列の演算に置き換えることはできないだろうか?」と考えながらプログラミングする癖がつきます。この考え方は、特に Octave に限らず、NumPy でも Mathematica でも同じですね。

NMR では数値シミュレーションだけでなく、数式処理も時々必要になってきます。3+4=7 のような数値計算ではなく、cos^2(wt)+sin^2(wt) = 1 のような計算が数式処理です。ω や sin, cos などを残したまま計算するのです。これが出来るのは限られた幾つかのソフトだけで、有名なのは Maple と Mathematica です。幸い、前にいた職場にはキャンパスライセンスがあったので、両者とも個人的には研究費を払わずに使うことができました。それで、Maple の HP を見てみると、モミジが描かれており、赤毛のアンを連想させるような雰囲気の中で、メープルシロップのたっぷりかかったモミジ型のお菓子(お土産で有名ですが、これおいしい!)の連想も手伝って、何も迷わずにこれの DVD を取り寄せました。

ちなみに、こうして自分の過去を思い返してみると、書籍の表紙に描かれている動物が可愛いかどうか?あるいは、連想させる食べ物がおいしいかどうか?でその後に使うプログラミング言語を決めてきたようなところがあります。ちゃんと情報教育を受けてきた人は、もっと理論的な考えでもってツールを選ぶものですが、生物という全く異なる分野の教育しか受けたことのない人間は案外、雰囲気というもので重要なことを決めてしまっているのかもしれません。もちろん Raspberry-pi 大好きです。そして Strawberry-Perl を愛用しています。これもフルーツ系の食べ物です。Red-Hat は嫌いで、ペンギンの描かれた Linux 系が好きです。ソフトに限らず、何か製品を開発していく人は、そのような雰囲気や感情で物事を決めていく人間もいるということを知り、ちょっとネーミングや宣伝を工夫してみると大当たりしてしまうかもしれません。もし、Raspberry-pi が Blue-Hat というような名前で、ギャング(やくざ)が青い帽子をかぶったような絵がイメージキャラクターだったとしたら、小中学校で教育用として人気を博すことはまずなかったでしょう。

数年間ほど楽しんで Maple を使っていたのですが、ある時、NMR の HSQC をシミュレーションしようとして動かしたところ、超複雑な答が 100 行ぐらい吐き出されてくるのです。理想的には Ix という二文字で終わりのはずなのですが。。。これは数式処理でしか起こらず、しかも全く同じ計算をしているのに 10 回に 1 回ぐらいしか出現しない。どうして変な答がたまに出るのかが分からず、また半年ほどあれやこれやと悩んだのですが、最終的に分かったことは、exp(i H t) の計算がうまく行っていないということでした。どこかで書いたと思うのですが、この H は行列で (i H t) の固有関数と固有値がピタッと数式処理で出れば exp(i H t) の計算は超簡単になります。しかし、この行列の対角化ができないと判断されれば、テーラー展開に切り替わるのです。しかし、テーラー展開の答はあくまで近似値にすぎません。HSQC の数式シミュレーションでは途中で多数の項が出てきますが、それらは位相回しによって最終的にはほとんどの項が打ち消され簡単な答だけが残るのです。まさに三角関数の加法定理などが式の簡略化に使われます。しかし、近似値どうしでは、この最後のキャンセルが完全な0にならないわけですね。そして、結果として多数の大小変な項が残ってしまうのです。

これを Maple の開発部門に伝えたところ、先方でも再現されバグであると認めてくれました(認められて嬉しいのやら、逆に計算できなくなって悲しいのやら)。それでバグが修正されるのを待っていたのですが、その間に筆者が今の職場に異動してしまいました。ところが残念なことに、ここはキャンパスライセンスがないのです。もしかしたら、もうバグが修正されているかもしれないのですが、25 万円を払って購入し、それを確かめるのも勇気がいり、もしかしたら開発部門から「バグを見つけてくれたので感謝料として、あなたには Maple パックをモミジ型お菓子とともに無料進呈いたします!」みたいな知らせが来ないかな~などと期待していました。しかし、こちらから不躾にそんな要求をするわけにもいかず、泣く泣く Maple を断念しました。それで、なんだか変な狐が描かれた Mathematica を買うことに。今だに Mathematica にはあまり慣れず、Maple が恋しいのですが、キャンパスライセンスがない大学では仕方がありません。頑張って Mathematica に慣れることにしました。

Maple はシンタックスが C 言語とちょっと似ているので、C, Perl, Python などを知っている人にはとっつきやすいと思います。GUI もきれいで、ずっと使い続けたいソフトの一つです。いずれにしても、Mathematica と Maple という二大ソフトが競争してくれるお陰で、ともに開発が進み、価格も(高いとは思いますが)25 万円で済んでいるのでしょう。そして、幸いにもキャンパスライセンスのある大学の人はどんどんこれを利用して、自分を教育すべきです。筆者は高校の時から数学があまり得意ではなく、その所為もあって数学とはもっとも無縁の学科を選びました。しかし、不得意であっても腐らずに勉強していれば、上記のような優秀なソフトが数学の不得意部分を補ってくれます。例えば、上記で exp(i H t) を書きましたが、本当に賢い人は、これを紙と鉛筆だけで対角化して計算し、HSQC などをさっとシミュレートしてしまいます。私にはそれがとても出来そうにないので、ソフトの計算力を借りるわけです。

2023年9月14日木曜日

つれづれ思うこと Perl 編 その6

前回の記事で C 言語について追加で書いたのは、実は Tcl/Tk との関連を書きたかったためです。Tcl/Tk を知ったのは、Perl, Python を知った直後でした。NMR で有名なフーリエ変換ソフト NMRPipe にもこれが使われていました(今も動いています)。Tcl 言語は Perl や Python と同じような(コンパイル不要の)スクリプト言語で、一方の Tk は GUI(グラフィカル・ユーザー・インターフェース)を作るためのツールキットです。それまでは、ちょっとした GUI を作るのも面倒だったのですが、この Tk のおかげでだいぶん楽になりました。その Tk が Tcl と相性がよいので両者が一緒に使われるようになったのだと思います。

Tcl 言語はひとことで言うと、たいへん可愛いに尽きます。Perl や Python がそれぞれ立派なリャマと大蛇だとすると、Tcl はコアラのような感じ。例のシリーズ本では、お猿さんの図が表紙になっていますが、猿のイメージとは大違い。ぱっと見た感じではシェルに見えます。ですので、ちょっとした計算(x = 5+3)でも、set x [expr 5+3] のような面倒な書き方になります。しかし、逆に c-shell も知らない筆者にとっては、これが新鮮で可愛く映ったのでした。

しかし、これだけなら Tcl/Tk を使おうとは思わなったのですが、Tcl にはそれ以上に面白い点がありました。Tcl は C 言語のコードをコンパイルする時に、そこにライブラリーとして含めることができるのです。つまり、以下のようになります。複雑な計算の箇所は高速化が必要ですので C 言語で書いておきます。そして、そのコードの入力と出力のパラメータを Tcl 言語と紐づけします。そして、Tcl ライブラリーを含めてコンパイルします。できあがった実行可能バイナリーファイルですが、入出力を Tcl 言語にすることができるのです。これはたいへん便利。いろいろなパラメータで同じような計算をし、結果をグラフに表して比べたい時、そのパラメータの入出力部分は Tcl でいかようにも変えることができるのです。ですので、二度目、三度目のコンパイルは不要。もし、C 言語だけでそれをしようとすると、「パラメータ a を入力してください」などと画面で促し、それを scanf で取り込むという面倒なことになります。もし、数百種類のパラメータでそれをしないといけないとすると、それでもう終わりです。

25~30 年ほど経った先日、当時のプログラムを使わないといけなくなり、バイナリーを動かそうとしてみましたがダメでした。やはりコンパイルし直さないと。しかし、コンパイル時にかなりの量の warning と error が出てしまいました。仕様がかなり変わっているのですね(引数に int, float などを明記しないとダメなど)。半日ほどかけて修正し、やっとほんの少しの warning を残した状態で make できました。残余双極子相互作用値と PDB から蛋白質分子の磁場配向テンソルを求めるプログラムなのですが、無事に計算できました。C 言語のアメーバを使っているのですが、初期値を5種類つくっての計算は 0.5 秒ぐらいでしょうか?この程度のフィッティングであれば、どのような初期値でスタートしても、いつも同じ最適解に収束します。

もう最近は C 言語から離れてしまったので、Tcl も使わなくなってしまいました。それに(間違えているかもしれませんが)いちどバージョンを上げるのに有料になった時があり、それを機会にやめてしまいました。手元に 2010 年に発刊された書籍「Tcl and the Tk Toolkit, 2nd ed」がありますので、また始めてみてもよいかもしれません。

2023年9月13日水曜日

つれづれ思うこと Perl 編 その5

C 言語についてもう少し。

Perl で exp(i H t) などの計算を試してはみたものの、やはり 1995 年頃は遅くて使い物にはならなかった。そこで科学計算にはやはり C 言語を使っていた。その時に役立った本が「Mumerical Recipes in C」(の翻訳版)。ここにはいろいろな計算のためのコードが載っていて、それをそのまま使ってかなりの数値計算をすることができる。特に重宝したのが最適化(フィッティング)の処理である。データに非線形的にフィッティングするための方法では Levenberg–Marquardt 法が有名であるが、コードを打ち込むのが大変そうだったので、何かもっと簡単な方法はないかな?とこの本で探してみると「アメーバ法」という方法が紹介されていた。正式には Nelder–Mead 法と呼ぶらしい。コードも数百行ぐらいで閉じているので、頑張って間違わずに打ち込めば、すぐに動く。この方法のよい点はモデル式の微分が要らない、不連続な箇所があってもよいなどで、まあとにかく制限がほとんど無いので、どんな状況にも適用できる。欠点は時間がかかる、間違えた解にトラップされやすいなど。よって、予想できる解がすでにある時には、できるだけそこからスタートするとよいです。

こんな(失礼ながら)子供遊びのような方法で数学的な最適化ができるのか?と思われ勝ちだが、試してみるとたいへん強力であった。例えば、NMR の残余双極子相互作用値を蛋白質の構造に当てはめる場合、400 残基分のデータをフィットするのに今の PC であれば 0.1 秒とかからない。当時でも 3 秒あれば十分であった。そこで、いろいろな初期値を 10 個ぐらい適当に準備して、それらを全部試してみる。 30 秒ぐらいで 10 個のフィッティングがすべて終わるので、その中から最小の二乗偏差を示す結果だけを選ぶ。エラーバーはどうするのかということになるが、これはモンテカルロ・シミュレーションがよい。100 個ほど模擬データを作っておき、全てにアメーバを振りかける。バナナを食べ終わる 10 分後にはすでに終わっており、その結果をエクセルで統計処理すればエラーバー(標準偏差)が出てくる。

結局つい数年前まで、NMR の緩和、残余双極子相互作用、回転拡散の異方性、拡散係数など、ほとんど全ての処理にアメーバを這わせたが、まったく問題なかった。緩和分散曲線もかなりは問題なし。もちろん Levenberg–Marquardt 法を使えば 0.1 秒ほどで終わってしまうが、今の PC をもってすると、それが延びたところで 1 秒ぐらいなので、もはや大した問題ではなくなっている。今は Perl に書き直したコードも使っている。C の 3-5 倍ぐらいかかるが、上記の NMR データの最適化で 1 分以上かかるような処理はあまりない。

(https://github.com/tikegami-tak/cpmg_hsqc) の中の perl/TI_lib_perl/TI_montecarlo.pm の中の lsquare_simplex というサブルーチンがアメーバ法です。

Fortran 一色という友達がおり「何故?」と尋ねてみると、いろいろ試してみたけれど、やはりこれが最速とのこと。特に数学処理では LAPACK ライブラリなどを使うと(もちろん C でも使えます)そこには何十年もかけて多くの人々が努力してきた成果が埋め込まれており、量子力学の計算ではそれを使わないと計算が終わるまでに一生かかるとのこと。NumPy も中ではこの LAPACK や BLAS といったライブラリーが使われているそうで、まあ道理で速いはずです。そこで、Perl でもこのようなライブラリーをさっと使えるようにならないだろうか?そしたら、アメーバからやっと卒業できるかも?一応、CPAN には Perl と LAPACK などをつなぐラッパーがあるようですが、NumPy のような使い方ではないような気がする。

ちなみに、最近はずるをして Mathematica で Levenberg–Marquardt 法をこっそりと使ったりするのであるが(Perl のアメーバには内緒)、Python を見てみると、変数の範囲を限定して Levenberg–Marquardt 法が使えるようになっていた。さらに CPMG 法では各残基ごとに異なる変数と分子全体で共通の変数があり、Mathematica のようにきっちりとまとまり過ぎた関数ではうまく最適化のためのコードを書けない時がある(ちゃんと勉強したら出来るのかもしれないが、私にはダメだった)。そんな変則的な時、ちょっと原始的な Python は非常に便利だった。また、アメーバ法も今はアルゴリズムがさらに進化しており、もっと高速化しているようです。

2023年9月10日日曜日

つれづれ思うこと Perl 編 その4

Perl と Python の比較だけでは話が単調になってしまうので、他の言語についても触れてみたい。

実は、最初に学んだ言語は Prolog だった。学んだというよりかは、大学の教養課程の授業「情報処理」で半年ほど触れただけである。1987 年当時、理系の授業で例として習うプログラム言語は普通は Pascal であった。ところが何故か私の授業の担当教員は Prolog に興味をもっており、そのため初めて触れるプログラム言語が Prolog となってしまった。「A さんの親は B さんで、B さんの兄は C さんで...」などという関係性を一行ずつ書いていく。そして、最後に A さんの叔父さんは誰ですか?」と質問すると「C さんです」という答が返ってくる。当時は人工知能のための言語として将来を期待されていたらしく、その先生も夢をもって語っていたのを思い出す。なにしろ初めて見たプログラムなので「Prolog で行列の計算ができないではないか!」などという疑問をもつこともなく、素直に「プログラミングって面白いな」と思った。

人口知能用の言語ということで、今の ChatGPT などの祖先のように思ってしまうが、おそらく全く違うように思う。今この言語はどうなっているのだろう?と思い調べてみたら、今もマニアの間では人気があるようだ。SWI-Prolog など無料でダウンロードできるサイトも活躍している。たぶん Prolog でしか処理できないような論理的な問題があるためであろう。C, Python, Perl などとは全く異なったコンセプトで設計されているので、視野を広げる意味でもいつか復習してみたい。なお、Perl との関係はなさそうで、唯一見つけられたのは、どちらもプログラムファイルの拡張子が ".pl" であることぐらいか?

Prolog の次に習ったのは C 言語で、これはたいへん難しかった。特にポインタの部分はよく分からない状態が続いた。しかし、当時勤めていた会社がたいへん教育的で、毎週 C 言語の講座を開いてくれて、少しずつではあるが理解できるようになってきた。1991 年当時は開発を担当していた半導体検査用電子顕微鏡を制御するのにアセンブリ言語(おそらくモトローラの MC6800 だったか?)が使われており、多くの技師がこれでプログラミングしていた。これは素人にはただの暗号に見える。そして、電顕の画像処理などに C 言語が使われ始めたのをきっかけに、機械制御の部分もワークステーション上での C 言語処理に少しずつ置き換えられつつあった。そのような状況もあり、アセンブリ言語の講座も社内にあった。しかし、その講座は一週間の合宿制になっており、参加するには上司の許可が必要であった。おそるおそる上司に相談してみると OK とのこと。実はこれが良かった。この講座によりアセンブリ言語がどのようにメモリーに値を入れたり、ビットをシフトさせたり、読み出したりしているかが分かり、同時に C 言語のアドレス(ポインタ)が理解できるようになった。あの時の上司の理解がなければ、プログラミングをその後 30 年以上にわたって今日までの仕事に活かすことはできなかったであろう。今も当時の教科書は残してあり、時々思い出しては感謝している(「マイコン回路の手ほどき」(1983)白土義男著)。

今はアセンブリ言語に触れることはあまりなく(マイコン制御ではあるのかな?)Python などあまりアドレスを意識しないで書けるプログラミング言語が主流となった。しかし、その根底にはメモリーの制御が常にある。よって、若い人ほどアセンブリ言語をちょっと勉強してほしい。私が受けた第二種情報処理技術者試験では CASL というアセンブリ言語が出題されたのであるが、今でもそれが選択肢のひとつとして残っていることを知って嬉しかった。是非 CASL を選んで受験して欲しいと思う。そしたら、人によっては、その後に学ぶすべてのプログラミング言語での見方が変わるだろう。

最後にアセンブリ言語と Perl との間に何か関係があるのか?という点になるが、もちろんあります!Perl でも Python でもそうですが、配列 abc をコピーしたりする時に (hiq = abc)、配列そのものが複製されるのか、それとも配列 abc を表す代表アドレスだけが別の変数 hiq に代入されるのかという問題を理解しておかないといけない。この仕様が言語によって異なるので大変ややこしい。さらに3次元 NMR スペクトルのように多次元データになってくると、配列そのものの複製なのか、それともアドレスだけの代入なのかの区別が複雑極まりない。C 言語では配列名がその配列の先頭アドレスを示すことになっているので、それをしっかりと理解しながらプログラミングできる。しかし、Perl にしろ Python にしろ他の言語になってくると、またその仕様が変わってくるので、いつもウェブで調べてはあれこれ悩んでいる(特に二次元目)。間違えて1万行 x 1万行の行列を、何か要素を少しだけ計算するたびに何千回も複製していたら、メモリーがいくらあっても足りないですからね。。

まあ、しかし今の言語の良いところは、C 言語での malloc と free が不要な点でしょう。いわゆるメモリーの動的確保と解放です。この free を忘れると、演算をするたびに自由に使えるメモリー領域が減っていき、最後には PC が固まってしまう。このガーベッジコレクション(というのかな?)が裏で働いてくれるお陰で、安心してプログラムが作れるようになった。また、私が C 言語ではなく Perl に移った主な原因もこれです。

ちなみに、上記の電顕についていた画像処理装置(Cognex)は C 言語で動くのだが、フリーのメモリーの現在量を示してくれる関数があった。これは大変便利。自分の作ったプログラムを動かす前と後とでわずかでもメモリー量が異なった場合、それはどこかで free を忘れているということを意味する。もし、この機能がなかったら半導体工場のオートメーション上で数時間使ったら必ず再起動してしまうといった事故が起きていたことだろう。

ところで、うちは生物系であるので、学生達はあまりコンピュータについて習ってきていない。しかし、今後はスマホにしろ AI にしろ、コンピュータ技術と無縁ではいられないので、少しは知っておく必要がある。そこで、PC のメモリーを 8 GB から 16 GB に増設するついでに、PC の中身を開いて「これはハードディスク、これがシムメモリー、これが CPU」と、一応の基礎を教えることにした。そしたら、NMR の三次元スペクトルのシリーズを 10 個ぐらい開いてピークを拾い始めると、なぜ自分の PC が急に重くなったり、時には落ちてしまうのかの理由も分かるはず。ついでに、それらがスマホとどのように対応しているのかを知ると、ちょっと親近感が湧くようだ。そう、何でもブラックボックスとプロトコールだけで済ませず、その原理を知ると面白いものなのだ。