>100 Views
November 20, 16
スライド概要
iOS オールスターズ2でお話しした資料です! Swift らしさってなんだろう、そんなところを API デザインガイドラインと Swift 標準ライブラリの表現に着目して 7 つほど紹介してみました。あくまでも "指針" なので『そういう風に考えていくのね』みたいに捉えて、そこからは "自分らしい" 言葉を紡いでいってくれたらいいのかなって思います。
※ Docswell での公開に移行する直前の Slideshare での閲覧数は 9,119 でした。
正統派趣味人プログラマー。プログラミングとは幼馴染です。
ΧδϡΞϧ4XJGUϓϩάϥϛϯά 4XJGU Β͍͠දݱΛࢦͦ͏ J04Φʔϧελʔζ ۽୩༑ IUUQF[OFUKQ 4XJGU
۽୩༑ 5PNPIJSP,VNBHBJ ⾣ 4XJGU͕ޠݴେ͖Ͱ͢ʂ ⾣ ΈΜͳͰָ͠Ήษڧձ͕େ͖Ͱ͢ʂ ⾣ ϓϩάϥϛϯάͷָ͠͞Λ͍͖͍͑ͯͨɻ !FT@LVNBHBJ UPNPIJSPLVNBHBJ $PEF1JFDFGPSNBD04 IUUQF[OFUKQ 9DPEFపఈղઆ
ษڧձΛ։࠵͍ͯ͠·͢ɻ Θ͍Θ͍ɺָ͘͠ɺ ΈΜͳͰޠΒ͑ΔॴΛࢦͯ͠ ԣJ1IPOF։ൃऀษڧձ ୈճ݄ͷ։࠵ ZJEFW ˏԣɾഅंಓ IUUQTBUOEPSHHSPVQTZJEFW ΧδϡΞϧ4XJGUษڧձ DTXJGU !ԣɾ੨༿ ୈճΛ݄͔݄̍ʹ։࠵ʢݕ౼தʣ IUUQTBUOEPSHHSPVQTDTXJGU ΈΜͳͰ4XJGU ෮शձ ୈճΛ݄தʹ։࠵ʢඪʣ NJOOB@EF@TXJGU ˏौ୩ IUUQDTXJGUDPOOQBTTDPN
NPPLNPPLSBEJP Ќ൛ ϜοΫϜοΫϥδΦ ۽୩ͱ៸໘͕ϓϩάϥϛϯάίʔυͷ͔Β ௌ͑ͯ͘͜ΔʹࣖΛָ͚ͯ͠ΉϥδΦ ຖ݄ୈ̎ɾୈ̐ ݄༵ ʹ৴ IUUQNPPLNPPLSBEJPDPNB ⾣ ୈ̓ ʢ݄ʹ৴༧ఆʣ ⾣ ୈ̒ ʰϝιουΛͲ͜ʹඋ͑Α͏ʁʱ ⾣ ୈ̑ ʰΞΫηείϯτϩʔϧʱ ⾣ ୈ̐ ʰίϝϯτͷଓ͖ˍܕʱ ⾣ ୈ̏ ʰίϝϯτʱ
4XJGUrָ͍͠Ͱ͢ΑͶ
͔͔ͤͬͩ͘Β 4XJGUΒ͍͠ίʔυΛඳ͖͍ͨ
4XJGU Β͍͠දݱΛࢦͦ͏
4XJGUද͕ྗݱ๛͔ͳݴ༿ 4XJGUΒ͠͞ ͬͯɺͳΜͩΖ͏ʜ
ࠓճͷඪ ͜ΕΛҎͬͯ4XJGUΒ͠͞ ͱ͢ʹͱ͜ͿݺΔ 4XJGU"1*%FTJHO(VJEFMJOFTʹ ͔༷ޠݴΒӐ͑ΔࢥΛΉ 4XJGUඪ४ϥΠϒϥϦͷදʹݱ฿͏
4XJGUΒ͍͠දݱΛࢦͦ͏ ໊ؔͱϝιου໊ "1*ࣗମ͕จষදͱݱ৺ಘΔ ΠχγϟϥΠβʔ໊ ܕΛੑ࣭Ͱઆ໌͢Δ શ෯ม͋ͰΔ͔Λҙࣝ͢Δ ػೳͷॴଐ ৼΔ͍ͱͯ͠ओମʹඋ͑Δ ܕΩϟετ มઌͷͰม͢Δ ൺֱՄೳͳܕ ςΩετදݱՄೳͳܕ $VTUPN4USJOH$POWFSUJCMFͰදݱ ࿈ଓతͳΛड͚औΔ ੑ࣭ʹணͯ͠ػೳΛΈཱͯΔ
1/7 ໊ؔͱϝιου໊ 10*/5⭐"1*ࣗମ͕จষදͳʹݱΔ
໊ؔͱϝιου໊ ໊લͷجຊํ ⾣ ӳͰޠίϝϯτΛॻ͘Α͏ͳ ྲྀΕΛҙࣝ ⾣ શͯͷݴ༿Ͱաෆͳ͘ҙຯΛ͙ ⾣ ૢ࡞ͷओମͰͳ͍ͷΛϥϕϧͰઆ໌ // 定義は、外部引数名、内部変数名、型名を活用して紡ぐ func insert(_ newItem: Item, into position: Int) // 使用時は、変数名も活用して紡ぐ bookshelf.insert(newBook, into: freeSpace) // item が冗長だったり、freeSpace が説明不足だったり bookshelf.insert(item: newBook, position: freeSpace)
໊ؔͱϝιου໊ ओମͷઆ໌ྗ͕ෆ͢Δͱ͖ ⾣ ͰܕҙຯΛෆͳ͛͘ͳ͍ͱ͖ ⾣ දݱൣғ͕͍ͱҙਤΛΈ͍͜ͱ w "OZ "OZ0CKFDU /40CKFDUͳͲͷ൚༻ܕ w *OU 4USJOHͳͲͷجຊܕ
໊ؔͱϝιου໊ ܕͷઆ໌ྗ͕ෆͷͱ͖ // 定義を見れば、意図を汲めるけれど… func add(handler: () -> Void, for eventName: String) // 使用時に、説明不足で主体から用途が想像しにくい document.add(handler: () -> Void, for: String) // 定義時に名前で補足すると… func addEventHandler(_ handler: () -> Void, forEventName name: String) // 使用時に用途を汲みやすくなる document.addEventHandler(handler: () -> Void, forEventName: String)
10*/5 "1*ࣗମ͕จষදͳʹݱΔΑ͏ʹ "1*ΛσβΠϯ͢Δ ໊ؔͱϝιου໊
2/7 ΠχγϟϥΠβʔ໊ 10*/5⭐શ෯ม͋ͰΔ͔Λҙࣝ͢Δ
ΠχγϟϥΠβʔ໊ جຊతͳ໊લͷ͚ํ ⾣ ༻్ΛϥϕϧͰઆ໌͢Δ ⾣ ҾΛจষද͍ͳྀ͠ߟͯ͠ͱݱ ⾣ ϓϩύςΟʔ໊Λ୯७ྻ͢هΔ͜ͱଟ͍ // bytes と count をとって初期化する init(bytes: UnsafeRawPointer, count: Int) // value と radix と uppercase をとって初期化する init(_ value: Int, radix: Int, uppercase: Bool) // lower と upper をとって初期化する init(lower: Bound, upper: Bound)
ΠχγϟϥΠβʔ໊ มΠχγϟϥΠβʔͷ໊લ ⾣ ผͷܕΛࣗͷʹܕม͢ΔΠχγϟϥΠβʔ w Λҡ࣋͢Δมɺ࠷ॳͷϥϕϧΛ ল͘ w ՃΛ͏มɺ࠷ॳͷϥϕϧͰ આ໌͢Δ // Int 型の値を String 型で忠実に再現(全幅変換) struct String { init(_ value: Int, radix: Int = 10) } // UInt32 型の値をビットパターンとして Int32 型に再解釈 struct Int32 { init(truncatingBitPattern pattern: UInt64) }
ΠχγϟϥΠβʔ໊ ͜͏໊ͯ͠લΛ͚Δ͜ͱͰʜ ⾣ ϥϕϧ͔ΒΠχγϟϥΠβʔͷҙਤ ͕ΊΔ // Int 型の値を String 型にキャスト(全幅変換) let string = String(number, radix: 16) // ビットパターンを指定して Int32 型を生成(Narrow 変換) let value = Int32(truncatingBitPattern: number) // 範囲を生成 let range = MyRange(lower: start, upper: last)
10*/5 શ෯ม͋ͰΔ͔Λҙࣝͯ͠ ϥϕϧ໊ͰػೳΛઆ໌͢Δ ΠχγϟϥΠβʔ໊
3/7 ػೳͷॴଐ 10*/5⭐ৼΔ͍ͱͯ͠ओମʹඋ͑Δ
ػೳͷॴଐ ओମʹػೳΛ࣮͢Δ ⾣ ϝιουϓϩύςΟʔͱͯ͠ ओମʹ࣮͢Δ ⾣ ࣮ ֦ܕு ɺϓϩτίϧ֦ு Ͱ࣮͢Δ // 自作の型なら、直接でも拡張でも自由に備えられる struct Bookshelf : Collection { mutating func arrange() { … } } // 他者が作ったものにも、拡張で備えられる extension Collection where Iterator.Element == Book { func sortedByTitle() -> [Book] { … } }
ϑϦʔͳؔͰ࣮͢Δ໘
ػೳͷॴଐ ϑϦʔͳؔͰ࣮͢Δ໘ ⾣ ओମ͕̍ͭʹܾ·Βͳ͍ͱ͖ ⾣ ओମ͕ఆٛʹݱΕͳ͍ͱ͖ ⾣ తʹ ؔදهͷ΄͏͕ࣗવͳ ͱ͖ // 全ての値が対等で、主体が定まらない func average(of values: Double...) -> Double // 具体的な主体が定義に現れないため、所属できない func convert<T: Any, U: Compatible>(from: T) -> U // プログラミングの分野では、絶対値は関数 abs で取得する func abs(_ x: Double) -> Double
10*/5 ػೳৼΔ͍ͱͯ͠ओମʹඋ͑Δ ঢ়ʹگΑͬͯϑϦʔͳؔͰ ػೳͷॴଐ
4/7 ܕΩϟετ 10*/5⭐มมઌͷͰߦ͏
ܕΩϟετ มΠχγϟϥΠβʔͰ࣮͢Δ ⾣ มઌʹΠχγϟϥΠβʔΛඋ͑Δ ⾣ Λ࣋ͬͯมݩͷΛड͚ೖΕΔ struct Int128 { fileprivate var low: UInt64 fileprivate var high: UInt64 // Int 型の値を Int128 型に全幅変換 init(_ value: Int) { low = UInt64(value) high = 0 } }
ܕΩϟετ طଘͷʹܕม͢Δ ⾣ มઌʹΠχγϟϥΠβʔΛ֦ு ͢Δ ⾣ Λ࣋ͬͯมݩͷΛड͚ೖΕΔ extension Int { // Int128 型の値を Int 型に全幅変換 init(_ value: Int128) { guard value.high == 0 else { fatalError("Overflow") } self = Int(value.low) } }
ܕΩϟετ มΠχγϟϥΠβʔΛ͏ͱʜ ⾣ ͍ͭͷܕΩϟετͱ ॻ͖ํ͕ἧ͏ ⾣ ࣮ࡍɺͨͱ͑%PVCMF͔Β*OUͷΩϟετ มΠχγϟϥΠβʔͰ࣮͞ݱΕ͍ͯΔ // お馴染みの Double から Int へのキャスト例 let taxIncluded = Int(Double(price) * (1 + tax)) // お馴染みのキャストと同じ書き方になる let endOfInteger = Int128(Int64.max) + 1 // 自作の Int128 から Int への変換もいつも通り let stride = Int(Int128.max / samples)
10*/5 มઌʹมΠχγϟϥΠβʔΛඋ͑Δͱ ͍ͭͷΩϟετͷॻ͖ํʹͳΔ ܕΩϟετ
5/7 ൺֱՄೳͳܕ 10*/5⭐ܕΛੑ࣭Ͱઆ໌͢Δ
ൺֱՄೳͳܕ
&RVBUBCMFϓϩτίϧ
⾣
ൺֱՄೳੑΛࣔ͢ϓϩτίϧ
⾣
ඞཁػೳΛඋ͑Δͱɺͦͷੑ࣭Λ࣋ͭͳʹܕΔ
// 標準ライブラリーに規定されている性質
/// A type that can be compared for value equality.
protocol Equatable {
/// Returns a Boolean value indicating
/// whether two values are equal.
static func ==(lhs: Self, rhs: Self) -> Bool
}
ൺֱՄೳͳܕ
&ʹܕRVBUBCMFΛద༻͢Δ
⾣
ܕΛ&RVBUBCMFϓϩτίϧͰઆ໌͢Δ
⾣
ൺֱͰ͖Δͯ͠ͱܕѻ͑ΔΑ͏ʹͳΔ
// プロトコルで比較可能性を説明すると…
extension Int128 : Equatable {
static func == (lhs: Int128, rhs: Int128) -> Bool {
return (lhs.low, lhs.high) == (rhs.low, rhs.high)
}
}
// 比較演算を使って比較可能になる
Int128(1000) == Int128(1000)
Int128(1000) != Int128(1000)
ൺֱՄೳͳܕ
δΣωϦοΫͳੈք͕͕Δ
⾣
ͦͷੑ࣭Λఆͨ͠ՕॴͰ͑Δ
⾣
ੑ࣭͍͑ࣔͯ͞͠ΕͲΜͳͰܕྑ͍
// 比較可能な要素を扱う場合だけ使えるようになっている
extension Array where Element : Equatable {
func index(of element: Element) -> Int?
}
// 配列で、要素からインデックスを特定できるようになる
let values = [Int128(1), Int128(5), Int128(10)]
let index = values.index(of: Int128(5))
10*/5 ܕΛϓϩτίϧͰઆ໌͢Δͱ ͦͷੑ࣭ʹ͋ͬͨৼΔ͍͕උΘΔ ൺֱՄೳͳܕ
6/7 ςΩετදݱՄೳͳܕ 10*/5⭐$VTUPN4USJOH$POWFSUJCMFͰઆ໌
ςΩετදݱՄೳͳܕ $VTUPN4USJOH$POWFSUJCMF ⾣ ΠϯελϯεΛ4USJOHʹม͢Δํ๏Λنఆ ⾣ ࣗΛςΩετͱͯ͠࠶ද͢ݱΔͷʹ͏ // 標準ライブラリーに規定されている性質 /// A type with a customized textual representation. protocol CustomStringConvertible { /// A textual representation of this instance. var description: String { get } }
ςΩετදݱՄೳͳܕ ܕΛςΩετදݱՄೳʹ͢Δ ⾣ $VTUPN4USJOH$POWFSUJCMFϓϩτίϧͰઆ໌͢Δ ⾣ ༰Λ4USJOHͰܕද͖ͰݱΔ Α͏ʹͳΔ enum Device : CustomStringConvertible { case iPhone, iPad, appleWatch var description: String { switch self { case .iPhone: case .iPad: case .appleWatch: } } return "iPhone" return "iPad" return " Watch"
ςΩετදݱՄೳͳܕ จࣈྻදݱΛظ͢Δ໘ͰΘΕΔ ⾣ 4USJOH EFTDSJCJOH Ͱจࣈྻม͢Δͱ͖ ⾣ QSJOUؔͰදࣔ͢Δͱ͖ ⾣ 4USJOH*OUFSQPMBUJPOͰѻ͏ͱ͖ let device = Device.appleWatch // String 型への変換(表示用の表現) let displayText = String(describing: device) // 文字列として表示(表示用の表現) print(device) // String Interpolation を介した変換(表示用の表現) let message = "I love \(device)"
10*/5 $VTUPN4USJOH$POWFSUJCMFͰઆ໌͢Δͱ จࣈྻͱͯ͠ද͖ͰݱΔੑ࣭͕උΘΔ ςΩετදݱՄೳͳܕ
7/7 ࿈ଓతͳΛड͚औΔ 10*/5⭐ੑ࣭ʹணͯ͠ػೳΛΈཱͯΔ
࿈ଓతͳΛड͚औΔ ΛਐΊΔ্Ͱͷલఏ ⾣ มqPBU7BMVFT͕"SSBZͰܕఆٛ͞Ε͍ͯΔ ⾣ "SSBZͷ෦ྻ<qPBU7BMVFT // 変数 floatValues が Array<Float> で定義されている let floatValues: [Float] // サブスクリプトに範囲を渡すと、部分配列が取れる let slice = floatValues[2 ..< 6] // 部分配列の型は ArraySlice<Float> になる type(of: slice) == ArraySlice<Float>.self
࿈ଓతͳΛड͚औΔ
%PVCMFྻͷ߹ܭΛٻΊΔ
⾣
%PVCMFܕΛྻͰड͚औͬͯ͢ࢉܭΔ
⾣
ཁ͓ͲٻΓͼݺʹີݫग़͢ඞཁ͕͋Δ
// 一般的な定義の仕方
func sum(of values: [Double]) -> Double {
return values.reduce(0, +)
}
// Float 型の配列の部分配列の合計を計算
sum(of: floatValues[2 ..< 6].map(Double.init))
εϥΠεͨ͠"SSBZ4MJDFͷ༰Λ'MPBU͔Β%PVCMFʹมࢉܭͯ͠
࿈ଓతͳΛड͚औΔ
খͷ ྻͷ߹ܭΛٻΊΔ
⾣
ුಈখΛྻͰड͚औͬͯ͢ࢉܭΔ
⾣
খͰ͋ΕͳΜͰྑ͍
// 任意の浮動小数点数を対象にする
func sum<T: FloatingPoint>(of values: [T]) -> T {
return values.reduce(0, +)
}
// Float 型の配列の部分配列の合計を計算
sum(of: Array(floatValues[2 ..< 6]))
εϥΠεͨ͠"SSBZ4MJDFΛ"SSBZʹมࢉܭͯ͠
࿈ଓతͳΛड͚औΔ 4FRVFODFϓϩτίϧ ⾣ ཁૉͷ࿈ଓΞΫηεੑΛࣔ͢ϓϩτίϧ ⾣ ͜ͷੑ࣭Λ͔࣋ͭܕΒɺΛॱ࣍औಘͰ͖Δ ⾣ "SSBZ"SSBZ4MJDF4FRVFODFʹ४͍ͯ͠ڌΔ // 標準ライブラリーに規定されている性質 /// A type that provides sequential, /// iterated access to its elements. public protocol Sequence { /// A type that provides the sequence's iteration /// interface and encapsulates its iteration state. associatedtype Iterator : IteratorProtocol
࿈ଓతͳΛड͚औΔ
খͷ߹ܭΛٻΊΔ
⾣
࿈ଓ͢ΔුಈখΛड͚औͬͯ͢ࢉܭΔ
⾣
ࢉܭର࿈ଓ͍ͯ͠ΕͳΜͰྑ͍
// 連続する任意の浮動小数点数を対象にする
func sum<S: Sequence, T: FloatingPoint>
(of values: S) -> T where S.Iterator.Element == T {
return values.reduce(0, +)
}
// 連続する浮動小数点数の部分配列の合計を計算
sum(of: floatValues[2 ..< 6])
εϥΠεͨ͠"SSBZ4MJDFͰࢉܭ
10*/5 ੑ࣭ʹணͯ͠ػೳΛΈཱͯΔͱ ʹܕറΒΕͳ͍ੈքΛඳ͚Δ ࿈ଓతͳΛड͚औΔ
4XJGUΒͦ͠͞ΕͧΕͷݸੑ ΈΜͳͷ4XJGUΒ͠͞ Λ୳ͯ͠Έ·͠ΐ͏ʂ
&OKPZ4XJGU 4XJGUΒ͍͠දݱΛࢦͦ͏ ໊ؔͱϝιου໊ "1*ࣗମ͕จষදͱݱ৺ಘΔ ΠχγϟϥΠβʔ໊ ܕΛੑ࣭Ͱઆ໌͢Δ શ෯ม͋ͰΔ͔Λҙࣝ͢Δ ػೳͷॴଐ ৼΔ͍ͱͯ͠ओମʹඋ͑Δ ൺֱՄೳͳܕ ςΩετදݱՄೳͳܕ $VTUPN4USJOH$POWFSUJCMFͰදݱ ࿈ଓతͳΛड͚औΔ ੑ࣭ʹணͯ͠ػೳΛΈཱͯΔ ܕΩϟετ มઌͷͰม͢Δ ۽୩༑ IUUQF[OFUKQ