ざっくりZig - 繰り返しの応用 (for, while, else, break, continue, オプション, ラベル)
forによる繰り返し
繰り返しにはwhileのほかにforもあります。これはwhileによる繰り返し処理をもう少しシンプルに書けるようにしたものです。
以下は変数iの値が0~4のときi = 0
~ i = 4
まで出力する処理を表します。
// while
var i: u8 = 0;
while (i < 5) : (i += 1) { // 繰り返し処理
try stdout.print("i = {}\n", .{i});
}
// for
for (0..5) |i| { // 繰り返し処理
try stdout.print("i = {}\n", .{i});
}
// 結果
i = 0
i = 1
i = 2
i = 3
i = 4
while
の場合、変数i
は繰り返しの外で定義し、i += 1
のように変数の値を更新する処理も明示しています。一方for
の場合は変数を1|i|
のように繰り返しの中で定義し、変数の値を更新する処理は明示していません。
また、変数が取り得る値を(0..5)
のように範囲で書いていますが、..
の左辺を右辺より必ず小さい値とします。これにより、iの値が0から4まで1ずつ増加し、5にはならないのがポイントです。
for
で複数の変数を扱うときは、(0..5, 5..10)
のようにカンマ区切りで複数の範囲を書き、変数も|i, j|
のように書きます。こうするとi
が0..5
、j
が5..10
の範囲で変化します。変数がとりうる値の数はすべての変数で同じになるようにします。これらの変数は繰り返しの中で同時に変化していきます。
// for
const std = @import("std");
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
for (0..5, 5..10) |i, j| {
try stdout.print("i = {}, j = {}\n", .{i, j});
}
}
// 結果
i = 0, j = 5
i = 1, j = 6
i = 2, j = 7
i = 3, j = 8
i = 4, j = 9
なお、|i|
などの変数を使用しないときは|_|
のようにします。
const std = @import("std");
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
for (0..5) |_| { // 変数を使用しない
try stdout.print("forによる繰り返し\n", .{});
}
}
代入とelse
while
とfor
のどちらも末尾のelse ...
もしくはelse { ... }
で繰り返しの終了後に行う処理を定義できます。これは代入と組み合わせて繰り返し処理後の判定結果などを設定するのに利用できます。
// while .. else
const std = @import("std");
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
for (2..10) |n| {
var i: u8 = 2;
// while..elseでnが素数かを判定
const isPrime = while (i < n): (i += 1) {
if (n % i == 0) {
break false; // nは素数じゃない
}
} else true; // nは素数
try stdout.print("isPrime {} = {}\n", .{n, isPrime});
}
}
// for .. else
const std = @import("std");
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
for (2..10) |n| {
// for..elseでnが素数かを判定
const isPrime = for (2..n) |i| {
if (n % i == 0) {
break false; // nは素数じゃない
}
} else true; // nは素数
try stdout.print("isPrime {} = {}\n", .{n, isPrime});
}
}
// 結果
isPrime 2 = true
isPrime 3 = true
isPrime 4 = false
isPrime 5 = true
isPrime 6 = false
isPrime 7 = true
isPrime 8 = false
isPrime 9 = false
whileで変化させる変数がオプション型のとき
while
で変化させる変数が?u8
のようなオプション型の時はwhile (オプション型の変数) |繰り返し処理で使用する変数| { ... }
のようにすると、(...)
の変数の値がnull
になるまで繰り返し処理を行います。
const std = @import("std");
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
var i: ?u8 = 0;
while (i) |c| : ({
i.? += 1;
if (i.? > 4) { i = null; }
}) {
try stdout.print("i = {}, c = {}\n", .{i.?, c});
}
try stdout.print("i = {?}\n", .{i});
}
// 結果
c = 0, i = 0
c = 1, i = 1
c = 2, i = 2
c = 3, i = 3
c = 4, i = 4
c = null
breakとcontinueによるラベル指定
break
とcontinue
では行き先をラベルで指定できます。これにより、多重の繰り返し処理になっているとき、行き先を外側の繰り返しの方に指定できます。ラベルをつけるときは行の最初にラベル名:
、行先を指定するときは:ラベル名
とします。
const std = @import("std");
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
outer: for (1..5) |i| { // 先頭にラベル
for (1..5) |j| {
if (j > 1) {
try stdout.print("\t", .{});
}
if (i * j > 5) {
try stdout.print("\n", .{});
continue :outer; // 行き先をラベルで指定
}
try stdout.print("{} * {} = {d:2}", .{i, j, i * j});
}
try stdout.print("\n", .{});
}
}
// 結果
1 * 1 = 1 1 * 2 = 2 1 * 3 = 3 1 * 4 = 4
2 * 1 = 2 2 * 2 = 4
3 * 1 = 3
4 * 1 = 4
// continueをbreakに変更したとき
1 * 1 = 1 1 * 2 = 2 1 * 3 = 3 1 * 4 = 4
2 * 1 = 2 2 * 2 = 4
まとめ
-
forは
while
と違い、変数を|i|
のように繰り返しの中で定義し、変数がとりうる値も(0..5)
とする
このとき変数の値は必ず1ずつ増加し、i
の値は5
にならない -
for
で複数の変数を|i, j|
のように定義でき、変化の範囲は(0..5, 5..10)
のように書く
これらの変数は同時に変化する -
for
で変数を使用しないときは|_|
とする -
while
とfor
のいずれも末尾にelse
で繰り返し終了後の処理を追加し、代入の時に繰り返し後の判定処理などで利用できる -
while
では変数がオプション型の時、値がnull
になるまで繰り返し処理を行う -
break
とcontinue
では行き先をラベルで指定できる
Discussion