HOME Sather を試そう 書き込む

4. 繰り返しとイテレーター


1. はじめに

今回は iterator について述べます。Sather で繰り返しを行うためには普通これを使います。

2. ループの書式

ループは次のように、loop から end; までで、1つ以上の iterator を含みます。 iterator の名前の終わりには ! をつけます。

イテレータが1つでもループからの脱出を指示したらループから抜けます。

loop
    (something)
    iterator
    (something)
end;
iterator は loop 内の好きなところに置くことができます。
下の例で、
01:     -- primitive counting prime numbers program
02:     
03:     class MAIN is
04:     
05:        top_while(n:INT) is
06:           i:INT := 0;
07:           #OUT + "Top while test:\n";
08:           loop
09:     	 while!(i < n);
10:     	 #OUT + "repeat: " + i + "\n";
11:     	 i := i+1;
12:           end;
13:           #OUT + "\n";
14:        end;
15:     
16:        bottom_while(n:INT) is
17:           i:INT := 0;
18:           #OUT + "Bottom while test:\n";
19:           loop
20:     	 #OUT + "repeat: " + i + "\n";
21:     	 i := i+1;
22:     	 while!(i < n);
23:           end;
24:           #OUT + "\n";
25:        end;
26:     
27:        middle_while(n:INT) is
28:           i:INT := 0;
29:           #OUT + "Middle while test:\n";
30:           loop
31:     	 #OUT + "before while: " + i + "\n";
32:     	 while!(i < n);
33:     	 #OUT + "after while: " + i + "\n";
34:     	 i := i+1;
35:           end;
36:           #OUT + "\n";
37:        end;
38:     
39:     
40:     
41:        
42:        main(av:ARRAY{STR}) is
43:           if av.size = 2 then
44:     	 n:INT := av[1].cursor.get_int;
45:     	 top_while(n);
46:     	 bottom_while(n);
47:     	 middle_while(n);
48:           else
49:     	 #ERR + "Usage: while N\n"
50:           end;
51:        end;
52:     end;
takafumi@suse:~/doc/monthly/06-05/sather> ./while 5
Top while test:
repeat: 0
repeat: 1
repeat: 2
repeat: 3
repeat: 4

Bottom while test:
repeat: 0
repeat: 1
repeat: 2
repeat: 3
repeat: 4

Middle while test:
before while: 0
after while: 0
before while: 1
after while: 1
before while: 2
after while: 2
before while: 3
after while: 3
before while: 4
after while: 4
before while: 5
while! は他の言語の while と同様に、引数が真の間ループを繰り返します。 一方、until! という iterator もあり、こちらは引数が真になるとループから抜けます。 また、bkeak! は強制的にループから抜け出すときに使います。
01:     -- primitive counting prime numbers program
02:     
03:     class MAIN is
04:     
05:        until_test(n:INT) is
06:           i:INT:=0;
07:           loop
08:     	 #OUT + i + "\n";
09:     	 if i= n then break!; end;
10:     	 until!(i = 1000);
11:     	 i := i+1;
12:           end;
13:        end;
14:        
15:        main(av:ARRAY{STR}) is
16:           if av.size = 2 then
17:     	 n:INT := av[1].cursor.get_int;
18:     	 until_test(n);
19:           else
20:     	 #ERR + "Usage: until_break N\n"
21:           end;
22:        end;
23:     end;

3. 整数型の iterator

Sather には整数型の iterator が用意されており、プログラムを簡潔に書くことができます。 以下に整数の iterator を使った簡単なプログラムとその出力を示します。 詳しくは http://www.icsi.berkeley.edu/~sather/Documentation/Library/LibraryBrowser/short-INT.html を見てください。

int_iter.sa

01:     -- iterators of INT class
02:     -- 
03:     -- downto!(once i:SAME):SAME
04:     -- **** 	Yield successive integers from self down to `i' inclusive.
05:     -- for!(once i:SAME):SAME
06:     -- **** 	Yields `i' successive integers starting with self.
07:     -- product!(i:SAME):SAME
08:     -- **** 	Yields the product of all previous values of `i'.
09:     -- step!(once num,once step:SAME):SAME
10:     -- **** 	Yield `num' integers starting with self and stepping by `step'.
11:     -- stepto!(once to,once by:SAME): SAME
12:     -- **** 	Yield succeeding integers from self to `to' by step `by'. Might quit immediately if self is aleady `beyond'.
13:     -- sum!(i:SAME):SAME
14:     -- **** 	Yields the sum of all previous values of `i'.
15:     -- times!
16:     -- **** 	Yields self times without returning anything.
17:     -- times!:SAME
18:     -- **** 	Yield successive integers from 0 up to self-1.
19:     -- up!:SAME
20:     -- **** 	Yield successive integers from self up.
21:     -- upto!(once i:SAME):SAME
22:     ----------------
23:     
24:     class MAIN is
25:     
26:        test_downto(start, last:INT)
27:           pre start >= last
28:        is
29:           #OUT + "Test for " + start + ".downto!(" + last + "): "; 
30:           loop
31:     	 #OUT + start.downto!(last) + " ";
32:           end;
33:           #OUT + "\n";
34:        end;
35:     
36:        test_for(start, last :INT)
37:           pre start <= last
38:        is
39:           #OUT + "Test for " + start + ".for!(" + last + "): ";
40:           loop
41:     	 #OUT + start.for!(last) + " "; 
42:           end;
43:           #OUT + "\n";
44:        end;
45:     
46:        test_step(start, last, step :INT) is
47:           #OUT + "Test for " + start + ".step!(" + last + ", " + step + "): ";
48:           loop
49:     	 #OUT + start.step!(last, step) + " "; 
50:           end;
51:           #OUT + "\n";
52:        end;
53:     
54:        test_stepto(start, last, step :INT) is
55:           #OUT + "Test for " + start + ".stepto!(" + last + ", " + step + "): ";
56:           loop
57:     	 #OUT + start.stepto!(last, step) + " "; 
58:           end;
59:           #OUT + "\n";
60:        end;
61:     
62:        test_times0(n:INT) 
63:           pre n>=0
64:        is
65:           #OUT + "Test for " + n + ".times!: ";
66:           loop
67:     	 n.times!;
68:     	 #OUT +  "Hey! "; 
69:           end;
70:           #OUT + "\n";
71:        end;
72:     
73:        test_times1(n:INT) 
74:           pre n>=0
75:        is
76:           i:INT;
77:           #OUT + "Test for " + n + ".times! returning INT: ";
78:           loop
79:     	 i := n.times!;
80:     	 #OUT +  "Hey!(" + i + ") "; 
81:           end;
82:           #OUT + "\n";
83:        end;
84:     
85:        test_up(n:INT)
86:           pre n >= 0
87:        is
88:           i:INT;
89:           #OUT + "Test for " + n + ".up!: ";
90:           loop
91:     	 i := n.up!;
92:     	 #OUT + i  + " ";
93:     	 if i = n+ 9 then #OUT + "break\n"; break!; end;
94:           end;
95:        end;
96:     
97:        test_upto(start, last :INT) is
98:           #OUT + "Test for " + start + ".upto!(" + last +  "): ";
99:           loop
100:     	 #OUT + start.upto!(last) + " "; 
101:           end;
102:           #OUT + "\n";
103:        end;
104:      
105:        test_product(m,n:INT)
106:           pre n >= m
107:        is
108:           p:INT;
109:           #OUT + "Test for product!: " + m + " * ... * " + n + " = ";
110:           loop
111:     	 p := INT::product!(m.upto!(n));
112:           end;
113:           #OUT + p + "\n";
114:        end;
115:     
116:        test_sum(m,n:INT)
117:           pre n >= m
118:        is
119:           p:INT;
120:           #OUT + "Test for sum!: " + m + " + ... + " + n + " = ";
121:           loop
122:     	 p := INT::sum!(m.upto!(n));
123:           end;
124:           #OUT + p + "\n";
125:        end;
126:        
127:        main(av:ARRAY{STR}) is
128:           if av.size = 4 then
129:     	 a:ARRAY{INT} :=#(3);
130:     	 i:INT;
131:     	 loop
132:     	    i:= 3.times!;
133:     	    a[i]:= av[i+1].cursor.get_int;
134:     	 end;
135:     	 test_times0(a[1]);
136:     	 test_times1(a[1]);
137:     	 test_up(a[0]);
138:     	 test_upto(a[0],a[1]);
139:     	 test_downto(a[1], a[0]);
140:     	 test_for(a[0], a[1]);
141:     	 test_step(a[0], a[1], a[2]);
142:     	 test_stepto(a[0], a[1], a[2]);
143:     	 test_sum(a[0], a[1]);
144:     	 test_product(a[0], a[1]);
145:     
146:           else
147:     	 #ERR + "Usage: int_iter MIN MAX STEP\n"
148:           end;
149:        end;
150:     end;
takafumi@suse:~/doc/monthly/06-05/sather> sacomp -chk int_iter.sa -o int_iter
takafumi@suse:~/doc/monthly/06-05/sather> ./int_iter 1 6 2
Test for 6.times!: Hey! Hey! Hey! Hey! Hey! Hey! 
Test for 6.times! returning INT: Hey!(0) Hey!(1) Hey!(2) Hey!(3) Hey!(4) Hey!(5) 
Test for 1.up!: 1 2 3 4 5 6 7 8 9 10 break
Test for 1.upto!(6): 1 2 3 4 5 6 
Test for 6.downto!(1): 6 5 4 3 2 1 
Test for 1.for!(6): 1 2 3 4 5 6 
Test for 1.step!(6, 2): 1 3 5 7 9 11 
Test for 1.stepto!(6, 2): 1 3 5 
Test for sum!: 1 + ... + 6 = 21
Test for product!: 1 * ... * 6 = 720

4. ARRAY 型の iterator

ARRAY 型の iterator には ind!, elt!, set! などがあります。以下にそれらを使った簡単なプログラムと実行例を示します。

詳しくは http://www.icsi.berkeley.edu/~sather/Documentation/Library/LibraryBrowser/short-ARRAY%7B_%7D.html をみて下さい。

array_iter.sa

01:     -- example of iterators in the ARRAY class
02:     
03:     class MAIN is
04:     
05:        const a1:ARRAY{INT}:=|1,2,3,4,5|;
06:        const a2:ARRAY{INT}:=|7,11,13,17,19|;
07:     
08:        show_array(a:ARRAY{INT}, name:STR) is
09:           i,j:INT;
10:           loop
11:     	 i:=a.ind!;
12:     	 j:=a.elt!;
13:     	 #OUT + name + "[" + i + "] = " + j + ", ";
14:           end;
15:           #OUT + "\n";
16:        end;
17:     
18:        ipro(a1,a2:ARRAY{INT}):INT 
19:           pre a1.size = a2.size
20:        is
21:           p:INT;
22:           loop
23:     	 p := INT::sum!(a1.elt! * a2.elt!);
24:           end;
25:           return p;
26:        end;
27:     
28:        add_arr(a1, a2:ARRAY{INT}):ARRAY{INT} 
29:           pre a1.size = a2.size
30:        is
31:           ar :ARRAY{INT} := #(a1.size);
32:           loop
33:     	 ar.set!(a1.elt! + a2.elt!);
34:           end;
35:           return ar;
36:        end;
37:     
38:        main is
39:           #OUT + "a1 = " + a1 + "\n" + "a2 = " + a2 + "\n\n";
40:           show_array(a1, "a1");
41:           show_array(a2, "a2");
42:           #OUT + "a1 + a2 = " + add_arr(a1, a2) + "\n";
43:           #OUT + "inner product of a1 and a2 = " + ipro(a1, a2) + "\n";
44:        end;
45:     end;
takafumi@suse:~/doc/monthly/06-05/sather> sacomp -chk array_iter.sa -o array_iter
takafumi@suse:~/doc/monthly/06-05/sather> ./array_iter
a1 = {1,2,3,4,5}
a2 = {7,11,13,17,19}

a1[0] = 1, a1[1] = 2, a1[2] = 3, a1[3] = 4, a1[4] = 5, 
a2[0] = 7, a2[1] = 11, a2[2] = 13, a2[3] = 17, a2[4] = 19, 
a1 + a2 = {8,13,16,21,24}
inner product of a1 and a2 = 231

5. STR 型の iterator

簡単な例と実行結果を示します。 詳しくは http://www.icsi.berkeley.edu/~sather/Documentation/Library/LibraryBrowser/short-STR.html をみて下さい。

str_iter.sa

01:     -- iterators of STR class
02:     -- 
03:     -- chunk!(chunk_size: INT): STR
04:     -- **** 	Yield successive chunks of self, defined by the chunk size "size"
05:     -- elt!(once beg:INT):CHAR
06:     -- **** 	Yield the characters of self in order starting at `beg'. Self may be void. Modified (ben)
07:     -- elt!(once beg,once num:INT):CHAR
08:     -- **** 	Yield 'num' characters of self in order starting at `beg'. Self may be void. Modified (ben)
09:     -- elt!:CHAR
10:     -- **** 	Yield the characters of self in order. Self may be void. Modified (ben)
11:     -- ind!:INT
12:     -- **** 	Yield the indices of the characters of self in order. Self may be void. Modified (ben)
13:     -- separate!(s:$STR):STR
14:     -- **** 	On the first iteration just outputs `s', on successive iterations it outputs self followed by `s'. Useful for forming lists,
15:     -- ____loop_#OUT_+_",_".separate!(a.elt!)_end;
16:     -- split!(once c: CHAR): STR
17:     -- **** 	Yield successive substrings that are separated by the character "c"
18:     ----------------------------------------------------------------------------------------
19:     
20:     
21:     class MAIN is
22:     
23:        test_chunk is
24:           s:STR := "abcdefghijklmnopqrstuvwxyz@";
25:           s1:STR;
26:           loop
27:     	 s1 := s1 + s.chunk!(3) + " ";
28:           end;
29:           #OUT + "Test of (" + s + ").chunk!(3): " + s1 + "\n";
30:        end;
31:     
32:        test_split is
33:           s:STR:= "192.168.1.1";
34:           #OUT + "Test of (" + s + ").split('.'): ";
35:           loop
36:     	 #OUT +  (s+".").split!('.') + "   ";  -- add "." at the end
37:           end;
38:           #OUT + "\n";
39:     
40:        end;
41:     
42:        test_separate is
43:           a:ARRAY{INT}:=|1,2,3,4,5,6,7|;
44:           #OUT + "Test of \"     \".separate(" + a + ".elt!): ";
45:            loop
46:     	  #OUT + "     ".separate!(a.elt!);
47:           end;
48:            #OUT  + "\n";
49:        end;
50:        
51:        main is
52:           test_chunk;
53:           test_split;
54:           test_separate;
55:        end;
56:     end;
takafumi@suse:~/doc/monthly/06-05/sather> sacomp str_iter.sa -o str_iter
takafumi@suse:~/doc/monthly/06-05/sather> ./str_iter
Test of (abcdefghijklmnopqrstuvwxyz@).chunk!(3): abc def ghi jkl mno pqr stu vwx yz@ 
Test of (192.168.1.1).split('.'): 192.   168.   1.   1.   
Test of "     ".separate({1,2,3,4,5,6,7}.elt!): 1     2     3     4     5     6     7

6. 自前の iterator

簡単に自前の iterator を作ることができます。

名前の最後に '!' をつけるほかは、通常の関数定義と同じです。
引数に 'once' と書いてあるのは、最初に呼ばれたときだけ引数を評価するという意味です。
'yield' に出会うと値を返し、次に呼ばれると計算を再開します。

ここでは、 等比数列を生成する iterator geo!(once a0, once r:FLTD):FLTD を定義しそれを動かしてみます。

01:     -- iterator for geometrical progressions
02:     
03:     class MAIN is
04:     
05:        geo!(once a0, once r:FLTD):FLTD is
06:           a:FLTD:=a0;
07:           loop
08:     	 yield a;
09:     	 a := a * r;
10:           end;
11:        end;
12:        
13:        main is
14:           g1, g2 : FLTD;
15:           #OUT + "User defined iterator: geo!(a0, r, FLTD)\n";
16:           loop
17:     	 10.times!;
18:     	 g1 := geo!(1.0d, 2.0d);
19:     	 g2 := geo!(1.0d, 0.5d);
20:     	 #OUT + g1 + " * " + g2 + " = " + (g1*g2) + "\n";
21:           end;
22:        end;
23:     end;
takafumi@suse:~/doc/monthly/06-05/sather> sacomp geo.sa -o geo
takafumi@suse:~/doc/monthly/06-05/sather> ./geo
User defined iterator: geo!(a0, r, FLTD)
1 * 1 = 1
2 * 0.5 = 1
4 * 0.25 = 1
8 * 0.125 = 1
16 * 0.0625 = 1
32 * 0.03125 = 1
64 * 0.015625 = 1
128 * 0.0078125 = 1
256 * 0.00390625 = 1
512 * 0.00195312 = 1
また、quit を使うと明示的にループから抜けることができます。 quit はイテレータ定義の中でのみ有効です。 例えば、while! は次のようにかけます。
   my_while!(predicate :BOOL) is
      loop
	 if predicate then yield;
	 else quit; 
	 end;
      end;
   end;

6. 終わりに

今回は iterator について簡単に例を挙げて説明しました。 このページを読む人はかなりプログラミングができると思われますので、ごく簡単な説明にとどめました。

次回は自前のクラス定義について述べます。


HOME Sather を試そう 書き込む