HOME | 14. GUI プログラミング | Sather を試そう | download | 書き込む |
[code 1a]: ffact.f
01: integer function factorial(n) 02: integer n 03: factorial = 1 04: do i=1,n 05: factorial = factorial * i 06: enddo 07: return 08: end[code 1b]: ffact.sa
01: -- interface with fortran 02: -- to compile: 03: -- sacomp ffact.sa -external FACT ffact.f -o ffact 04: 05: 06: external FORTRAN class FACT is -- Fortran で書かれた外部クラスを宣言します。 07: factorial(i:F_INTEGER):F_INTEGER; -- FACT で定義されている関数です。 08: end; 09: 10: class MAIN is 11: main(av:ARRAY{STR}) is 12: i:F_INTEGER := #(av[1].cursor.get_int); -- Fortran の integer 型は Sather から見ると F_INTEGER class になる。 13: a:F_INTEGER := FACT::factorial(i); -- #(INT) でインスタンスを生成できる。 14: #OUT + av[1] + "! = " + a.str + "\n"; -- str メソッドで文字列に変換する。 15: end; 16: end;
コンパイルするときは -external オプションを使ってクラス FACT の実態が ffact.f だということを sacomp に知らせる必要があります。
$ sacomp ffact.sa -external FACT ffact.f -o ffact $ ./ffact 10 10! = 3628800Fortran のデータタイプは F_INTEGER, F_REAL などがあります。対応する Sather のデータを引数にして create を呼び出せば、インスタンスが生成されます。 詳しくは Sather — A Language Tutorial: Chapter 13 Interfacing with Fortran をみてください。
行列の積を求めるコードは [code 2a], [code 2b] のようになります。 Fortran と Sather は2次元配列のインデックスの順番が逆になるので注意が必要です。
[code 2a]: mat.f
01: c 02: c calculating the product of matrices c = a * b 03: c 04: subroutine mattimes(l,m,n,a,b,c) 05: integer l,m,n,i,j,k 06: real a(n,l), b(m,n), c(m,l), tmp 07: do i=1,l 08: do j=1,m 09: tmp=0.0 10: do k=1,n 11: tmp=tmp+a(k,i)*b(j,k) 12: enddo 13: c(j,i)=tmp 14: enddo 15: enddo 16: return 17: end[code 2b]: mat.sa
01: -- Matrix test 02: -- sacomp mat.sa -chk -external FMAT mat.f -o mat 03: 04: external FORTRAN class FMAT is 05: mattimes(l,m,n:F_INTEGER, a,b,c:F_ARRAY2{F_REAL}); 06: end; 07: 08: class MAT is 09: include ARRAY2{FLT}; -- ARRAY2{FLT} に 10: 11: times(b:SAME):SAME -- 掛け算を定義した MAT クラスを定義する。 12: pre self.nc = b.nr -- 掛け算の左側の行列の行数と右側の行列の列数が等しいことを確認 13: is 14: c:SAME:=#(self.nr, b.nc); -- 行数と列数を指定して答えを格納する MAT を宣言 15: FMAT::mattimes(#(self.nr), #(b.nc), #(self.nc), #(self), #(b), #(c)); -- Fortran で書かれた mattimes を呼び出す。 16: return c; -- F_ARRAY2{REAL} の値が変化すると ARRAY2{FLT} の値も変化する。 17: end; 18: end; 19: 20: class MAIN is 21: main is 22: i:INT; 23: a,b,c:MAT; 24: a:=#(||1.0, 1.0, 2.0|, |2.0, 2.0, 3.0||); 25: b:=#(||1.0, 1.0|, |1.0, 2.0|, |1.0, 3.0||); 26: c:=a*b; -- 行列の掛け算がきわめて簡単になる。 27: 28: loop -- 答えの表示 29: i:=1.up!; 30: #OUT + c.elt!; 31: if i%c.nc=0 then #OUT+"\n"; 32: else #OUT+" "; 33: end; 34: end; 35: end; 36: end;mat.sa ではテストとして
$ sacomp mat.sa -chk -external FMAT mat.f -o mat $ ./mat 4 9 7 15
[code 3a]: cfact.c
01: unsigned long factorial(unsigned long i){ 02: unsigned long j, n; 03: for(n=1,j=1;j<=i;++j) n*=j; 04: return n; 05: }[code 3b] cfact.sa
01: -- interfacing with C 02: -- to compile: 03: -- sacomp cfact.sa -external FACT cfact.c -o cfact 04: -- 05: 06: external C class FACT is 07: factorial(i:C_UNSIGNED_LONG):C_UNSIGNED_LONG; 08: end; 09: 10: class MAIN is 11: main(av:ARRAY{STR}) is 12: i:C_UNSIGNED_LONG := #(av[1].cursor.get_int); 13: a:C_UNSIGNED_LONG := FACT::factorial(i); 14: #OUT + av[1] + "! = " + a.str + "\n"; 15: end; 16: end;Sather から C 言語のデータ型を見るときは C_CHAR, C_FLOAT などのクラスを使います。 対応する Sather のデータを引数にして create を呼び出せば、インスタンスが生成されます。 詳しくは Sather — A Language Tutorial: Chapter 14 Interfacing with ANSI C をみてください。
Fortran で書いた ffact と同様にコンパイルして実行します。
$ sacomp cfact.sa -external FACT cfact.c -o cfact $ ./cfact 10 10! = 3628800
例として、以前紹介した暗号化プログラム crypt.sa の改良版を示します。 Sather は CHAR 型のビット排他論理和が取れないので、CHAR 型を一度 INT 型に変換する必要がありました。 C で書かれた関数を呼び出せばこの部分の効率がよくなります。[code 4a] に C で書かれた関数を、 [code 4b] に crypt.sa の変更した部分を挙げます。
[code 4a]: encrypt.c
01: void encrypt(char* key, char* plain, char* cipher){ 02: char *key1, *plain1, *cipher1; 03: for(key1=key, plain1=plain, cipher1=cipher; *plain1 != 0; key1++, plain1++, cipher1++){ // 文字列の最後がヌル文字であることをあてにしている 04: if(*key1==0) key1=key; // key を使い切ったら最初に戻る 05: *cipher1 = *key1 ^ *plain1; // bit ごとの XOR をとる 06: } 07: *cipher1=0; // 最後にヌル文字を追加 08: }[code 4b] crypt.sa (一部)
01: external C class ENCRYPT is -- 外部クラス ENCRYPT を宣言する 02: encrypt(key,plain,cipher:C_CHAR_PTR); 03: end; 04: 05: class ARCHAR is -- ARRAY{CHAR} + methods to convert from/to STR 06: include ARRAY{CHAR}; -- ARRAY{CHAR} の機能拡張クラス ARCHAR を定義 07: 08: create(s:STR):SAME is -- STR から create できるようにする。 09: res:SAME:=#(s.size); 10: loop 11: res.set!(s.elt!); 12: end; 13: return res; 14: end; 15: 16: to_str:STR is -- STR に変換できるようにする。 17: fs:FSTR:=#(self.size); 18: loop 19: fs:=fs+self.elt!; 20: end; 21: return fs.str; 22: end; 23: end; -- ARCHAR 24: 25: class XOR < $CRYPT is 26: 27: private attr akey:ARCHAR; 28: 29: create(key0:STR):SAME is 30: return new.init(key0); 31: end; 32: 33: private init(key0:STR):SAME is 34: akey:=#(key0); 35: return self; 36: end; 37: 38: encrypt(s:STR):STR is 39: pl:ARCHAR:=#(s); 40: ci:ARCHAR:=#(s.size); 41: ENCRYPT::encrypt(#(akey.array_ptr), #(pl.array_ptr), #(ci.array_ptr)); -- C で書かれた関数を呼び出す 42: return ci.to_str; 43: end; 44: 45: decrypt(s:STR):STR is -- decrypt is same as the encrypt 46: return encrypt(s); 47: end; 48: end; -- end of XOR文字列の最後がヌル文字で終わっていることを当てにしている C の関数も問題なく働きます。 C_CHAR_PTR に変換するときヌル文字が付加されるのでしょう。
コンパイルして実行すると次のようになります。
$ sacomp crypt.sa -external ENCRYPT encrypt.c -o crypt
$ ./crypt 'hello world! beautiful beautiful!'
Sipher Test using "hello world! beautiful beautiful!"
Plain send: hello world! beautiful beautiful!
Planin receive: hello world! beautiful beautiful!
XOR send: (表示できない文字列)
XOR receive: hello world! beautiful beautiful!
付録にこの章で使ったコードをつけておきます。
HOME | 14. GUI プログラミング | Sather を試そう | download | 書き込む |