対応する APG4b の箇所:1.11.for文・break・continue , 2.02.多重ループ
break
式 / continue
式
break
式
for
式のブロック中で break;
と書くと,そこで即座にループが終了します.
fn main() {
let array = [2, 3, 0, 4, 5];
for &i in &array {
if i == 0 {
break;
}
print!("{}, ", i);
}
println!();
}
このプログラムを実行すると,次のような流れになります.
- 最初のループでは
i
の値が 2 です.-
if
式のブロックは実行されません. -
print!
マクロが2,
と出力します.
-
- 次のループでは
i
の値が 3 です.-
if
式のブロックは実行されません. -
print!
マクロが3,
と出力します.
-
- 次のループでは
i
の値が 0 です.-
if
式のブロックが実行されます.ブロックの中身がbreak;
なので,for
式のループが即座に終了します.
-
- ループ後の
println!();
が実行され,改行が出力されます.
よって全体の出力は次のようになります.
2, 3,
continue
式
for
式のブロック中で continue;
と書くと,そこで即座に次のループに移ります.
fn main() {
let array = [2, 3, 0, 4, 5];
for &i in &array {
if i == 0 {
continue;
}
print!("{}, ", i);
}
println!();
}
このプログラムを実行すると,次のような流れになります.
- 最初のループでは
i
の値が 2 です.-
if
式のブロックは実行されません. -
print!
マクロが2,
と出力します.
-
- 次のループでは
i
の値が 3 です.-
if
式のブロックは実行されません. -
print!
マクロが3,
と出力します.
-
- 次のループでは
i
の値が 0 です.-
if
式のブロックが実行されます.ブロックの中身がcontinue;
なので,次のprint!
マクロは実行されず即座に次のループに移ります.
-
- 次のループでは
i
の値が 4 です.-
if
式のブロックは実行されません. -
print!
マクロが4,
と出力します.
-
- 次のループでは
i
の値が 5 です.-
if
式のブロックは実行されません. -
print!
マクロが5,
と出力します.
-
- ループ後の
println!();
が実行され,改行が出力されます.
よって全体の出力は次のようになります.
2, 3, 4, 5,
..
演算子
for
式で in
の後に書けるものとして,今までに「配列への参照」「ベクタへの参照」の 2 つが登場しました.ここではさらにもう一つ紹介します.
次のコードを実行してみてください.
fn main() {
for i in 0..5 {
println!("{}", i);
}
}
出力は
0
1
2
3
4
となります.
in
の後に 0..5
と書くと, for
式のブロックは i
が 0
, 1
, 2
, 3
, 4
のときそれぞれについて実行されます.
a..b
の形式で, a
以上 b
未満の範囲を表します. a
と b
の型は同じでなければなりません.
a
と b
の型がともに i32
であれば, i
の型も同じ i32
になります.参照型 &i32
ではないことに注意してください.
一方, ..
を ..=
に変えて a..=b
とすると, a
以上 b
以下になります.
fn main() {
for i in 0..=5 {
println!("{}", i);
}
}
0
1
2
3
4
5
また,上限を書かずに a..
と書くと,内部で break
式が実行されない限り永遠にループし続けます.
fn main() {
for i in 3.. {
println!("{}", i);
if i * i > 30 {
break;
}
}
}
なんと出力されるか予想してください.実際に実行して,予想が合っていたか確かめてみてください.
loop
式
for
式の代わりに loop
式を使うと, break;
式が実行されない限りブロックが繰り返し実行され続けます.
use proconio::input;
fn main() {
loop {
input! {
x: i32,
}
if x > 0 {
println!("{}", x * 2);
} else {
break;
}
}
}
入力を繰り返し読みこんで,正なら 2 倍して出力し,負であるようなものを読み込んだ時点で終了します.たとえば入力として
3 10 6 -5 2 7
を与えると
6
20
12
と出力されます.
値を返す loop
式
loop
式では, break
の直後に式を書くと値を返すことができます.
use proconio::input;
fn main() {
let value = loop {
input! {
x: i32,
}
if x > 0 {
println!("{}", x * 2);
} else {
break x; // x の値を返す
}
}; // セミコロンが必要
println!("value: {}", value);
}
先ほどと同様に,負であるようなものを読み込んだ時点で終了しますが,そのときに x
の値を返しています.これが value
に代入され,最後に println!
マクロで出力されています.
よって入力が
3 10 6 -5 2 7
であれば
6
20
12
value: -5
と出力されます.
while
式
while
式は,ある条件が成り立っている間ずっとループします.
fn main() {
let mut x = 15;
let mut v = Vec::new();
while x > 0 {
v.push(x);
x /= 2;
}
assert_eq!(x, 0);
assert_eq!(v, vec![15, 7, 3, 1]);
}
-
x
に ,15 v
に空のベクタが代入されます. -
よりx = 15 が成り立つので,ブロックが実行されます.x > 0 -
v
に が追加され,15 vec![15]
になります. -
x
が で割られ,2 になります.7
-
-
よりx = 7 が成り立つので,ブロックが実行されます.x > 0 -
v
に が追加され,7 vec![15, 7]
になります. -
x
が で割られ,2 になります.3
-
-
よりx = 3 が成り立つので,ブロックが実行されます.x > 0 -
v
に が追加され,3 vec![15, 7, 3]
になります. -
x
が で割られ,2 になります.1
-
-
よりx = 1 が成り立つので,ブロックが実行されます.x > 0 -
v
に が追加され,1 vec![15, 7, 3, 1]
になります. -
x
が で割られ,2 になります.0
-
-
よりx = 0 が成り立たないので,ブロックが実行されずにループが終了します.x > 0 -
x
の値は ,0 v
の値はvec![15, 7, 3, 1]
なのでアサートが成功します.
2 重ループとラベル
2 重ループ
for
式のブロック中に,さらに for
式を書くことができます.
fn main() {
for i in 0..4 {
for j in 0..i {
print!("({}, {}) ", i, j);
}
println!();
}
}
- 外側のループ 1 周目:
.i = 0 - 範囲
0..0
は空なので,内側のループは一度も実行されません. - 改行が出力されます.
- 範囲
- 外側のループ 2 周目:
.i = 1 -
0..1
の中の各j
について内側のループが実行されます. - 内側のループ 1 周目:
.j = 0 -
(1, 0)
と出力されます.
-
- 改行が出力されます.
-
- 外側のループ 3 周目:
.i = 2 -
0..2
の中の各j
について内側のループが実行されます. - 内側のループ 1 周目:
.j = 0 -
(2, 0)
と出力されます.
-
- 内側のループ 2 周目:
.j = 1 -
(2, 1)
と出力されます.
-
- 改行が出力されます.
-
- 外側のループ 4 周目:
.i = 3 -
0..3
の中の各j
について内側のループが実行されます. - 内側のループ 1 周目:
.j = 0 -
(3, 0)
と出力されます.
-
- 内側のループ 2 周目:
.j = 1 -
(3, 1)
と出力されます.
-
- 内側のループ 3 周目:
.j = 2 -
(3, 2)
と出力されます.
-
- 改行が出力されます.
-
よって実行すると次のように出力されます.
(1, 0)
(2, 0) (2, 1)
(3, 0) (3, 1) (3, 2)
0..0
や 2..1
のような空の範囲だと, for
式のブロックは一度も実行されません.よって一行目は空のまま改行だけが行われます.
break
次は,このコードに break;
を含む if
式を追加して
fn main() {
for i in 0..4 {
for j in 0..i {
if i * j >= 2 {
break;
}
print!("({}, {}) ", i, j);
}
println!();
}
}
としてみます.出力はどうなるでしょうか?
break;
によって終了するのは,一番内側のループただ一つです.
よって,出力は次のようになります.
(1, 0)
(2, 0)
(3, 0)
(2, 1)
が出力される前に内側のループが終了しますが,外側のループは続いているため
ラベル
一方, break;
によって外側のループを終了し,出力が
(1, 0)
(2, 0)
となるようにしたいときはどうすれば良いでしょうか.
このようなときは,ループにラベルを付けます.
'outer: for i in 0..4 {
アポストロフィ '
で始まっている部分 'outer
がラベルです.このように書くと, for
式に 'outer
という名前を付けることができます.ラベルは先頭に '
が付いていなければなりません.また,ラベルと for
の間にコロン :
を書くのを忘れてはいけません.
こうして付けた名前は, break
式や continue
式で使うことができます.
fn main() {
'outer: for i in 0..4 {
for j in 0..i {
if i * j >= 2 {
println!();
break 'outer;
}
print!("({}, {}) ", i, j);
}
println!();
}
}
break 'outer;
によって終了するのは,ラベル 'outer
が付いた外側の for
式です.よって出力
(1, 0)
(2, 0)
が得られます.
値を返すラベル付き loop
式
ラベル付き break
でさらに値を返すには,
break ラベル 式;
とします.次は「入力を受け取って,素数ならまた次の入力を受け取り,合成数なら最小の素因数を出力して終了する」プログラムです.
use proconio::input;
fn main() {
let factor = 'input: loop {
input! {
x: i32,
}
for i in 2.. {
// 2 以上の各整数について,
// x を割り切るか順に調べる
if i * i > x {
// i が x の平方根より大きくなったら
// それ以上調べても意味がない
break;
} else if x % i == 0 {
// i は x を割り切る最小の整数
break 'input i;
}
}
};
println!("{}", factor);
}
14 行目の break;
では内側のループ, 17 行目の break 'input i;
では外側のループが終了します.
たとえば入力が
7 11 5 9 13 8
なら,このうち 7 11 5 9
までを読み込んで 3
と出力します.
練習問題
分からなかったら解答例を見てください.説明できるところはコメントで説明しています.