![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Sather のクロージャには通常の手続き (ROUT 型) とイテレーター(ITER 型) の2種類があります。
01: -- rout.sa 02: -- simple examples for ROUT and ITER 03: 04: class MAIN is 05: 06: -- helper function to treat TUP{INT,INT} 07: aux(tu:TUP{INT, INT}):INT is 08: return tu.t1 + tu.t2; 09: end; 10: 11: -- print function 12: print(obj:$STR, sep:STR) is 13: #OUT + obj + sep; 14: end; 15: 16: 17: main is 18: -- declaring closures 19: csqrt:ROUT{FLT}:FLT; 20: cpow:ROUT{FLT,FLT}:FLT; 21: ccomp:ROUT{TUP{INT,INT}}:INT; 22: istep:ITER{once INT,once INT,once INT}:INT; 23: 24: -- assigning closures 25: csqrt:=bind(_.sqrt); 26: cpow:=bind(_.pow(_)); 27: ccomp:=bind(aux(_)); 28: istep:=bind(_.step!(_,_)); 29: 30: -- calling closures 31: print(csqrt.call(2.0), "\n"); -- 1.414.... 32: print(cpow.call(0.99999, 300.0), "\n"); -- 0.997 33: print(ccomp.call(#TUP{INT,INT}(1,2)), "\n"); -- 3 34: loop 35: print(istep.call!(1,5,3), " "); -- 1 4 7 10 13 36: end; 37: #OUT + "\n"; 38: end; 39: end;
行 | 説明 |
---|---|
19 — 22 | クロージャーは引数と返り値を使って宣言します。 |
25 — 28 | 手続きの代入は bind を使って行います。bind の中の '_' は引数に対応します。 あまり複雑な手続きは bind を使って書けないので、別にメソッドを用意して、bind でそれを呼び出すようにします。 |
31 — 37 | ROUT 型クロージャを呼び出すときは call メソッドを、 ITER 型クロージャを呼び出すときは call! を使います。 |
上のコードでは説明のため宣言と代入を分けて書きましたが、一般には次のように同時に行うことが多いです。
cpow:ROUT{FLT,FLT}:FLT:=bind(_.pow(_));
$ sacomp rout.sa -o rout $ ./rout 1.41421 0.997 3 1 4 7 10 13
01: -- horder.sa 02: -- examples for higher order functions for ARRAY{INT} 03: 04: class MAIN is 05: const iarr:ARRAY{INT}:=|0,9,2,7,3,4,1,5,8,6|; 06: 07: 08: -- print function 09: print(obj:$STR, sep:STR) is 10: #OUT + obj + sep; 11: end; 12: 13: 14: -- filtering 15: select_even:ARRAY{INT} is 16: return iarr.remove_if(bind(_.is_odd)); 17: end; 18: 19: -- mapping 20: sq:ARRAY{INT} is 21: a:ARRAY{INT}; 22: a:=iarr.copy; 23: a.map(bind(_.pow(2))); 24: return a; 25: end; 26: 27: -- folding 28: sum:INT is 29: return iarr.reduce(bind(_.plus(_))); 30: end; 31: 32: -- a helper function for order_by_desc 33: gt(i,j:INT):BOOL is 34: return i > j; 35: end; 36: 37: -- sorting 38: order_by_desc:ARRAY{INT} is 39: a:ARRAY{INT}; 40: a:=iarr.copy; 41: a.insertion_sort_by(bind(gt(_,_))); 42: return a; 43: end; 44: 45: 46: main(av: ARRAY{STR}) is 47: print(select_even, "\n"); 48: print(sq, "\n"); 49: print(sum, "\n"); 50: print(order_by_desc, "\n"); 51: end; 52: end;
$ sacomp horder.sa -o horder $ ./horder {0,2,4,8,6} {0,81,4,49,9,16,1,25,64,36} 45 {9,8,7,6,5,4,3,2,1,0}
01: -- rpcalc.sa: a simple reverse polish calculator, second version 02: 03: class OPERATORS is 04: 05: attr biop:MAP{STR,ROUT{FLTD,FLTD}:FLTD}; -- MAP of binary operators 06: attr unop:MAP{STR,ROUT{FLTD}:FLTD}; -- unary operators 07: 08: create:SAME is 09: return new.init; 10: end; 11: 12: init:SAME is 13: init_biop; 14: init_unop; 15: return self; 16: end; 17: 18: init_biop is -- 二項演算子をハッシュ表に加える。 19: biop:=#; 20: biop["+"]:=bind(_.plus(_)); 21: biop["-"]:=bind(_.minus(_)); 22: biop["*"]:=bind(_.times(_)); 23: biop["/"]:=bind(_.div(_)); 24: biop["^"]:=bind(_.pow(_)); 25: end; 26: 27: init_unop is -- 単項演算子をハッシュ表に加える。必要があればもっと加えることもできる。 28: unop:=#; 29: unop["exp"] := bind(_.exp); 30: unop["log"] := bind(_.log); 31: unop["sin"] := bind(_.sin); 32: unop["cos"] := bind(_.cos); 33: unop["tan"] := bind(_.tan); 34: unop["asin"]:= bind(_.asin); 35: unop["acos"]:= bind(_.acos); 36: unop["atan"]:= bind(_.atan); 37: end; 38: 39: is_operator(s:STR):BOOL is -- 演算子? 40: return is_biop(s) or is_unop(s); 41: end; 42: 43: is_biop(s:STR):BOOL is -- 二項演算子? 44: return biop.has_ind(s); 45: end; 46: 47: is_unop(s:STR):BOOL is -- 単項演算子? 48: return unop.has_ind(s); 49: end; 50: 51: apply(s:STR, a:FLTD):FLTD is -- 単項演算子を使って計算する。 52: return unop[s].call(a); -- apply("log", 3.3) のように使う。 53: end; 54: 55: apply(s:STR, a,b:FLTD):FLTD is -- 二項演算子を使って計算する。 56: return biop[s].call(a,b); -- apply("+", 1.1, 2.3) のように使う。 57: end; -- 引数が違えば同じ名前のメソッドを定義できる。 58: 59: end; --OPERATORS 60: 61: 62: 63: class MAIN is 64: 65: const re:REGEXP:=REGEXP::regexp("^[+\\-]?[0-9]+\\.?[0-9]*$", false); -- 実数かどうかチェックする正規表現 66: attr stack:STACK{FLTD}; 67: attr op:OPERATORS; 68: 69: is_number(word:STR):BOOL is -- 実数? 70: return re.match(word); 71: end; 72: 73: bicalc(word:STR) is -- 二項演算子を使って計算する。 74: f1,f2 :FLTD; -- スタックから2回 pop して、計算結果を push する。 75: f2:=stack.pop; 76: f1:=stack.pop; 77: stack.push(op.apply(word,f1,f2)); 78: end; 79: 80: push_to_stack(word:STR) is -- 実数をスタックに積む 81: stack.push(word.cursor.get_fltd); 82: end; 83: 84: clear_stack is -- スタックを空にする。 85: a:FLTD; 86: loop 87: while!(~stack.is_empty); 88: a:=stack.pop; 89: end; 90: end; 91: 92: show_stack:STR is -- スタックを文字列に変換 93: s:STR:="> "; 94: loop 95: s:= s + stack.reverse_elt!.str + " "; 96: end; 97: return s; 98: end; 99: 100: main(av: ARRAY{STR}) is 101: line, word:STR; 102: stack:=#; 103: op:=#; -- OPERATORS クラスのインスタンスを作る。 104: loop 105: #OUT + show_stack; 106: line:=#IN.get_str + " "; 107: loop 108: word := line.split!(' '); 109: word := word.head(word.size-1); 110: if op.is_unop(word) then stack.push(op.apply(word, stack.pop)); -- 単項演算子ならスタックから pop して計算結果を push する。 111: elsif op.is_biop(word) then bicalc(word); -- 二項演算子なら bicalc を呼ぶ。 112: elsif is_number(word) then push_to_stack(word); -- 実数ならスタックに積む。 113: elsif word="clear" then clear_stack; -- "clear" ならスタックを空にする。 114: elsif word="end" then UNIX::exit(0); -- "end" なら終了。 115: end; 116: end; 117: end; 118: end; 119: end;
$ sacomp rpcalc.sa -o rpcalc $ ./rpcalc -- bold は入力 > 2.2 0.8 + 4.7 1.7 - * -- (2.2 + 0.8) * (4.7 - 1.7) > 9 0.5 ^ -- 9 ^ 0.5 > 3 sin -- sin(3) > 0.14112 asin -- asin(0.14112) > 0.141593 10 2 ^ -- 0.141593 (10 ^ 2) > 0.141593 100 * -- 0.141593 * 100 > 14.1593 end $
ここに示したコードは付録につけておいたので遊んでみてください。
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |