できるようになってほしいこと
どんな演算があると便利か考えました(NumPy等を参考にするのではなくて)。
- 行列の四則演算
- 行列の成分の四則演算
- 和とか最大値
このくらいあればひとまず動かせるのではないでしょうか(何を?)。
実装
行列のクラスを作る
行列とは
+中身:配列={}
+大きさは,{0,0}。
はじめ(行,列)の手順
行回,【現在行】にカウントして繰り返す
【一行】は,{}。
列回,【現在列】にカウントして繰り返す
一行(現在列)は,0.0。
繰り返し終わり
中身(現在行)は,一行。
繰り返し終わり
大きさは,{行,列}。
終わり
終わり
プロデルでは,クラスのことを「種類」と呼びます。種類は,「○○とは」で始まり「終わり」で終わるブロック(?)によって記述されます。
行列種類内1行目と2行目では,配列を宣言しています。+というのは,外部からアクセスできることを示します。:で型を指定します。大きさについては{0,0}で初期化しています。
はじめの手順というのは,コンストラクタです。引数に,行列の大きさとして,行と列の2変数をとっています。今回は,指定された大きさの2次元配列を作り,中身を全て0.0で初期化しておくことにします。これをしないと,どうもうまく動いてくれないからです。
この後,いくつも種類手順を作っていくのですが,所謂getとsetに相当するものがプロデルにありまして,
○○を取得する手順
終わり
○○を設定する手順
終わり
というもので,これらは呼び出し方が他の手順とは異なり,
種類の○○
と書くだけで値が取得できるっぽいので,流暢な日本語を作るためには必須なのですが,どうもインタプリタでは動作するもののコンパイルするとエラーが発生するようです。
行列の演算を作る
まず,極めて重要なことには,プロデルでは演算子のオーバーライドができません。よって,
行列A×行列B
といったことは定義できないのです。しかし,これはメリットでもあると思っています。なぜなら,中身の要素について計算したい場合と,そうでない場合で表記が混乱するからです。好き好きではあると思うのですが,行列にスカラー値を足すと全ての要素にかかるという動作は便利なのですが僕は混乱するので嫌いです。明示的に{for all x_{ij} += 1
みたいなコードを書きたい。
かけ算
行列のかけ算には,$O(n^3)$というひっくり返るくらい大きな計算量がかかりますが,なんとかアルゴリズムで$O(n^{log_27})$にできるものの,再帰のオーバーヘッドで結局遅くなるらしいので,今回は愚直に3重ループを回します。
自分を[他の行列:行列]に左からかける手順
もし大きさ(2)が,他の行列の大きさ(1)でないなら
「かける相手の大きさ(1)が[他の行列の大きさ(2)]なので乗算できません。」というエラーを発生させる。
もし終わり
【計算後】という行列(大きさ(1),他の行列の大きさ(2))を作る。
大きさ(1)回,【現在行】にカウントしながら繰り返す
他の行列の大きさ(2)回,【現在列】にカウントしながら繰り返す
大きさ(2)回,【現在要素】にカウントしながら繰り返す
計算後の中身(現在行,現在列)は,計算後の中身(現在行,現在列)+(中身(現在行,現在要素)×他の行列の中身(現在要素,現在列))。
繰り返し終わり
繰り返し終わり
繰り返し終わり
計算後を返す。
終わり
【】によって局所変数を宣言できます。
次に,同じ大きさの行列を2つ用意して,同じ添字の値同士をかけ算する手順を作ります。
自分に[他の行列:行列]をかける手順
もし(大きさ(1)が他の行列の大きさ(1)でない)または(大きさ(2)が他の行列の大きさ(2)でない)なら
「行列の大きさがおかしいのでかけられません。」というエラーを発生させる。
もし終わり
【計算後】という行列(大きさ(1),大きさ(2))を作る。
大きさ(1)回,【現在行】にカウントしながら繰り返す
大きさ(2)回,【現在列】にカウントしながら繰り返す
計算後の中身(現在行)(現在列)は,中身(現在行)(現在列)×他の行列の中身(現在行)(現在列)。
繰り返し終わり
繰り返し終わり
計算後を返す。
終わり
プロデルでは,乗算の演算子として「×」が使えます。「*」は非文にしか見えませんからね。
同じ要領で,四則演算につき手順を作ります。また,$M \times N$の行列に,大きさ$M$の縦ベクトルや,大きさ$N$の横ベクトルを使って,全体に足したり引いたりしたいこともあるので,その手順も作ります。これらは,明示的に分かるように動詞を変えています。引数の型によるオーバーロードはできるので,それも利用しています。
それから,行ごとや列ごとの和や最大値,そしてargmaxも作ります。
使用例
「行列.rdr」を参照する。
Aという行列(2,2)を作る。
Bという行列(2,2)を作る。
Aの中身は,{{1,2},{3,4}}。
Bの中身は,{{1,0},{0,1}}。
Aを左からBにかけたものを表示。
ちなみに,語尾は自由に変えられるので,
「行列.rdr」を参照してほしい。
Aという行列(2,2)を作ってほしい。
Bという行列(2,2)を作ってほしい。
Aの中身は,{{1,2},{3,4}}。
Bの中身は,{{1,0},{0,1}}。
AをBに左からかけたものを表示してほしい。
としても動くし,
「行列.rdr」を参照するぽよ。
Aという行列(2,2)を作ぽよ。
Bという行列(2,2)を作ったぽよ。
Aの中身は,{{1,2},{3,4}}。
Bの中身は,{{1,0},{0,1}}。
左からAをBにかけたものを表示するんだ。
としても動きます。