Swiftで遊ぼう! on Hatena

あしたさぬきblogでやってた初心者オヤジのiOSプログラミング奮闘記がHatenaに来ました

Swiftで遊ぼう! - 564 - 構造体、タイプ・メソッドとタイプ・プロパティ

2016年12月1日:Swift 3のコードに変更、未だに疑問点は解消されていません*1

詳解 Swift 改訂版」のChapter3、構造体のパートを読んでいて、「タイプ・メソッド」と「タイプ・プロパティ」の復習をしていたのですが、著者である萩原さんの説明の矛盾点に悩んでいます。

タイプ・メソッド

  1. タイプ・メソッドは、個々のインスタンス機能を実装するものではなく、全インスタンス共通して利用される機能の提供
  2. インスタンスの作り方(個数の制限や再利用)のコントロール
  3. この型を利用するうえで便利な機能の提供

タイプ・メソッドの上記の説明があり、例文として次の構造体を挙げています。

struct Date {
    var year, month, day: Int
    static func isLeap(y: Int) -> Bool {
        return (y % 4 == 0) && (y % 100 != 0 || y % 400 == 0)
    }
    static func daysOfMonth(m: Int, year: Int) -> Int {
        switch m {
        case 2: return self.isLeap(y: year) ? 29 : 28
        case 4, 6, 9, 11: return 30
        default: return 31
        }
    }
}

static」キーワードが付いているメソッドがタイプ・メソッドで、2つ指定されています。「isLeap」メソッドは「うるう年」かどうか判定する計算式です。私は「うるう年」の正確な定義を知りませんでした(^_^;) 何となく4年毎のオリンピックイヤーが「うるう年」という間違った認識をしていいました(^_^;) いやはや、私もやばいですね。「西暦年が4で割り切れる年ですが、100で割り切れない年でないといけません。しかし、400で割り切れる場合はうるう年になります」という説明を式にしたのが「isLeap」メソッド内の式です。

そして、ある年のある月が何日あるか返すメソッドが「daysOfMonth」メソッドです。これはisLeapメソッドを利用しています。

さて、悩んでいるのはこのタイプメソッドの利用法です。萩原さんは次のコードを載せています。

Date.isLeap(y: 2000)               // trueが返る
Date.daysOfMonth(m: 2, year: 2000) // 29が返る

実はこういう利用法は、上記のタイプ・メソッドの説明に合ってないから納得できないんです。最初の説明に書いているように「インスタンス共通して利用される機能の提供」として考えるなら、次のような例文の方が合っているような気がします。

var someDate = Date(year: 2050, month: 11, day: 29)
var anyDate = Date(year: 2016, month: 1, day: 17)

// 上記のようにインスタンスを作って、
// これに共通する機能として次のように書く方が望ましいんじゃないでしょうか? 

Date.isLeap(y: anyDate.year)     // trueが返る
Date.isLeap(y: someDate.year)    // falseが返る

萩原さんの利用法で説明するのなら、構造体Dateのプロパティ「year」「month」「day」が意味を成さないような気がします。というのも、萩原さんの利用法を推奨するなら、次のような例文もできるからです。

var day = 31
Date.isLeap(y: day)

こうなってしまったら、タイプメソッドの説明とはかけ離れてしまいます。いっそのことプロパティを排除して、タイプ・メソッドだけで構成した次のような共通メソッド群とした方がいいような気がします。

struct ComonFunctions {
    static func isLeap(y: Int) -> Bool {
        return (y % 4 == 0) && (y % 100 != 0 || y % 400 == 0)
    }
    static func daysOfMonth(m: Int, year: Int) -> Int {
        switch m {
        case 2: return isLeap(y: year) ? 29 : 28
        case 4, 6, 9, 11: return 30
        default: return 31
        }
    }
}

プログラマーの皆さん、どうなんでしょうか?

タイプ・プロパティ

タイプ・プロパティは、定数と変数定義の前に「static」というキーワードを付けます。

知らなかったことは、イニシャライザで、タイプ・プロパティを参照できるということでした。また、構造体のプロパティの値を変更するインスタンスメソッドを定義する場合、そのメソッド名の前に「mutating」というキーワードを付ける必要がありますが、タイププロパティのの値を変更するインスタンス・メソッドに「mutating」は不要なんですね。

今日はこれだけ。

*1:2016年4月21日:少し改訂