Swift教室 Swift と競技プログラミングの教室

スコープ

読了目安 約3分

変数が使える範囲はブロックで決まる。シャドーイングを避けて名前の衝突を防ぐ。

この章の目次

変数と定数には「名前が使える範囲」があります。 この範囲をスコープと呼びます。

ブロックとスコープ

{ から } までのひとかたまりをブロックと呼びます。 ブロックの中で宣言した名前のスコープは、そのブロックの中だけです。

Swift
let temperature = 30
if temperature >= 25 {
    let message = "夏日です"
    print(message)
}
// ここで print(message) と書くとコンパイルエラー

message は if 文のブロックを出た時点で消えます。 外で message を使おうとすると、「そんな名前は無い」というコンパイルエラーになります。

逆に、ブロックの外で宣言した名前は、ブロックの中でも使えます。

Swift
var total = 0
let price = 150
if price < 200 {
    total = total + price
}
print(total)

totalprice は一番外で宣言されているので、if 文の中から読めるし、書き換えられます。 ブロックの中の結果を外に持ち出したいときは、変数を外で宣言しておきます。

試してみよう: if 文の中に let discount = 50 を足し、ブロックの外で print(discount) するとどんなエラーになるか見てください。

同名変数のシャドーイング

内側のブロックでは、外と同じ名前を新しく宣言できます。 内側の名前が外の名前を隠すので、これをシャドーイングと呼びます。

Swift
let count = 3
if count > 0 {
    let count = 100
    print(count)
}
print(count)

出力は 1003 です。 同じ名前が場所によって別の変数を指すので、読む人は混乱します。 シャドーイングは書けてしまいますが、内側には別の名前を付けてください。

この後学ぶ while 文や for-in 文のブロックでも、スコープの規則は同じです。

学びどころ

概念一言まとめ
スコープ名前が使える範囲。宣言したブロックの中だけ
内から外外で宣言した変数はブロックの中から使える
シャドーイング内側の同名宣言が外を隠す。避けるべき書き方

演習

たこ焼きをちょうど N 個買います。 単品は 1 個 A 円で、S 個入りセットは 1 セット B 円です。 セットと単品を組み合わせて合計をちょうど N 個にするとき、合計金額の最小値を出力してください。

入力は 1 行に整数 N S A B が空白区切りで並びます。

制約: 1 ≤ N, S, A, B ≤ 1000

テキスト
入力例:
10 3 50 120

出力例:
410

入力例では、3 個入りセットを 3 つと単品 1 個を買うと 120 × 3 + 50 = 410 円で最安です。

模範解答

セットが「単品 S 個分」より安いなら買えるだけ買い、高いなら全部単品にします。 両方を計算して、安いほうを出力すれば十分です。

Swift
let parts = readLine()!.split(separator: " ").map { Int($0)! }
let n = parts[0]
let s = parts[1]
let a = parts[2]
let b = parts[3]

// セットを買えるだけ買い、残りを単品で買う
let withSets = (n / s) * b + (n % s) * a
// 全部単品で買う
let allSingle = n * a

if withSets <= allSingle {
    print(withSets)
} else {
    print(allSingle)
}