2014年7月25日金曜日

[Swift] アンダースコア (_) の使いどころ

色々あるのでまとめてみた。

数値リテラルの区切り文字として使用


単純に見やすさのため数値リテラル内を区切ることが出来る。評価時には無視される。
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1

タプル展開時の無視変数として使用


特定の要素のみ展開し、不要な要素を _ で無視。
let status = (200, "OK")
let (code, _) = status
let (_, message) = status

for-in ループ変数が必要ないときに使用


ループの現在値が必要ない時は _ で無視できる。
let base = 3
let power = 10
let answer = 1
for _ in 1...power {
   answer *= base
}
タプル展開も同様。
for (key, _) in dictionary {
    println("\(key)")
}

タプル + switch-case 文で、条件判定の無視要素として使用


ワイルドカード的な。
let point = (1, 1)
switch point {
    case (0, _)
        //
    case (_, 1)
        //
    default
        //
}

関数引数の外部名を無名化するのに使用


関数やメソッドのパラメータは外部名(External name)と内部名(Local name)をわけて宣言でき、外部名を宣言した場合は呼び出し時に指定しなければならない(省略できない)。

通常、外部名は宣言しなければ呼び出し時に指定する必要はないが、以下のケースでは自動的に外部名が追加される。この時の外部名を無名化するために使用することができる。

関数


普通の関数は、外部名を明示的に宣言に加えない限りは無名呼び出しが可能。
しかしデフォルト引数がある場合は別で、自動的に(内部名と同じ)外部名が追加され、呼び出し時に省略することは出来なくなる。
func hello(name: String = "World") {
    println("Hello, \(name)")
}

hello()
hello(name: "Swift") // デフォルト値を上書きする場合、引数名を省略できない。
これを回避するため、外部名を明示的にアンダースコア(_)として宣言することができる。
*当然ながら「本来は適切な名称をつけるべし」とされている。
func hello(_ name: String = "World") {
    println("Hello, \(name)")
}

hello()
hello("Swift") // 外部名の指定が必要なくなる
・・・らしいのだが、現時点の Xcode6-Beta4 ではクラッシュして動作を確認できていない。
Xcode6-Beta5 でクラッシュせずに実行できる事を確認。

また、以下のように内部名も無名化することができる。
func someFunction(_: Int) {
    //
}
この場合は変数にアクセスできなくなる。クロージャなど、引数を無視したい(使わない引数がある)場合に活用できる。

余談ながら、以下のように(必要もないのに)外部名を無名化したら警告が出た。
func someFunction(_ localName: Int) {
    //
}
コンパイラが教えてくれるので、特に迷うことはなさそう。

メソッド


メソッドにあって関数にない特性として、セカンドパラメータ以降は常に外部名が追加される、というのがある(デフォルトでは内部名 = ハッシュ修飾(#)と同じ効果)。
// 通常
class Counter {
    var count: Int = 0
    func incrementBy(amount: Int, numberOfTimes: Int) {
        count += amount * numberOfTimes
    }
}

let counter = Counter()
counter.incrementBy(5, numberOfTimes: 3) // セカンドパラメータ以降は外部名指定が必要
*この特性のため、セカンドパラメータ以降をハッシュ修飾(#)すると警告が出る。

この挙動を抑制する方法として、_ を外部名に指定することで、メソッド呼び出し時のセカンドパラメータ名を指定しなくともよくなる。
// 無名化
class Counter {
    var count: Int = 0
    func incrementBy(amount: Int, _ numberOfTimes: Int) {
        count += amount * numberOfTimes
    }
}

let counter = Counter()
counter.incrementBy(5, 3) // セカンドパラメータに名称が必要ない
*ファーストパラメータに対して _ を付けると警告になる。

イニシャライザ


イニシャライザの場合はさらに「全てのパラメータに対して外部名が追加される」という特性がある。
class Color {
    let red = 0.0, green = 0.0, blue: 0.0
    init(red: Double, green: Double, blue: Double) {
        self.red = red
        self.green = green
        self.blue = blue
    }
}

let black = Color(red: 0.0, green: 0.0, blue: 0.0)
これも関数やメソッド同様、必要であればすべてのパラメータに外部名を追加することは出来るが、アンダースコア(_)を指定することで無名化できる。
class Color {
    let red = 0.0, green = 0.0, blue: 0.0
    init(_ red: Double, _ green: Double, _ blue: Double) {
        self.red = red
        self.green = green
        self.blue = blue
    }
}

let white = Color(1.0, 1.0, 1.0)
イニシャライザは関数やメソッドと違って、パーレン記号の前に関数名のような識別子を持てないため、パラメータ名は意味を持たせるための重要な役割があると言える。無名化する場合は利用するときに困らない理由(名称が省略されても意味が明白、など)が欲しいところ。

外部名の自動追加は、基本的に Objective-C スタイルの(説明的な)呼び出しになるように、との配慮らしい。ObjC ではマナーに過ぎなかったことが Swift では言語で強制した感じ。


使ってはいけないところ


逆にアンダースコア(_)が使えないところとして明示されているのは、今のところ operator 定義時の優先度(precedence)値。
0〜255 の整数値だが、この時の区切り文字としては使えないとのこと。


まとめ


色々あると思ったけど、結局はどこを切っても「無名ラベル」として使えると。
なんなら以下のように宣言しても怒られない(使えないけど)。
let _ = 1
若干変わってると言えるのは数値リテラルの区切り文字くらいだろうか。

0 件のコメント:

コメントを投稿