Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Swift らしい表現を目指そう #eventdots

6,908 views

Published on

iOS オールスターズ2でお話しした資料です! Swift らしさってなんだろう、そんなところを API デザインガイドラインと Swift 標準ライブラリの表現に着目して 7 つほど紹介してみました。あくまでも "指針" なので『そういう風に考えていくのね』みたいに捉えて、そこからは "自分らしい" 言葉を紡いでいってくれたらいいのかなって思います。

Published in: Software

Swift らしい表現を目指そう #eventdots

  1. 1. 

  2. 2. 
 
 
 
 
 
 

  3. 3. ⭐ 1 / 7
  4. 4. // 定義は、外部引数名、内部変数名、型名を活用して紡ぐ func insert(_ newItem: Item, into position: Int) // 使用時は、変数名も活用して紡ぐ bookshelf.insert(newBook, into: freeSpace) // item が冗長だったり、freeSpace が説明不足だったり bookshelf.insert(item: newBook, position: freeSpace)
  5. 5. // 定義を見れば、意図を汲めるけれど… 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)
  6. 6. // 動詞系で、自身を書き換えないなら、末尾に“ed”とか nonmutating func advanced(by n: Int) -> Int // 動詞系で、自身を書き換えないなら、末尾に“ing”とか nonmutating func adding(_ other: Int) -> Int // 動詞系で、自身を書き換えるなら、動詞そのもの mutating func add(_ other: Int)
  7. 7. // 名詞系で、自身を書き換えないなら、名詞そのもの nonmutating func squareRoot() -> Double // 名詞系で、自身を書き換えるなら、先頭に“form”付与 mutating func formSquareRoot() // 引数を書き換える場合も Mutating と同じく“form”付与 nonmutating func formIndex(after i: inout Int)
  8. 8. // 素直に命名するとラベルがアンバランスになる func cell(atColumn: Int, row: Int) -> Cell // at をメソッド名に含めることでラベルが整う func cellAt(column: Int, row: Int) -> Cell // タプルで表現することでもバランスが取れる func cell(at location: (column:Int, row:Int)) -> Cell
  9. 9. ⭐ 2 / 7
  10. 10. // bytes と count をとって初期化する init(bytes: UnsafeRawPointer, count: Int) // value と radix と uppercase をとって初期化する init(_ value: Int, radix: Int, uppercase: Bool) // lower と upper をとって初期化する init(lower: Bound, upper: Bound)
  11. 11. // Int 型の値を String 型で忠実に再現(全幅変換) struct String { init(_ value: Int, radix: Int = 10) } // UInt32 型の値をビットパターンとして Int32 型に再解釈 struct Int32 { init(truncatingBitPattern pattern: UInt64) }
  12. 12. // Int 型の値を String 型にキャスト(全幅変換) let string = String(number, radix: 16) // ビットパターンを指定して Int32 型を生成(Narrow 変換) let value = Int32(truncatingBitPattern: number) // 範囲を生成 let range = MyRange(lower: start, upper: last)
  13. 13. ⭐ 3 / 7
  14. 14. // 自作の型なら、直接でも拡張でも自由に備えられる struct Bookshelf : Collection { mutating func arrange() { … } } // 他者が作ったものにも、拡張で備えられる extension Collection where Iterator.Element == Book { func sortedByTitle() -> [Book] { … } }
  15. 15. // 全ての値が対等で、主体が定まらない func average(of values: Double...) -> Double // 具体的な主体が定義に現れないため、所属できない func convert<T: Any, U: Compatible>(from: T) -> U // プログラミングの分野では、絶対値は関数 abs で取得する func abs(_ x: Double) -> Double
  16. 16. ⭐ 4 / 7
  17. 17. struct Int128 { fileprivate var low: UInt64 fileprivate var high: UInt64 // Int 型の値を Int128 型に全幅変換 init(_ value: Int) { low = UInt64(value) high = 0 } }
  18. 18. extension Int { // Int128 型の値を Int 型に全幅変換 init(_ value: Int128) { guard value.high == 0 else { fatalError("Overflow") } self = Int(value.low) } }
  19. 19. 
 // お馴染みの Double から Int へのキャスト例 let taxIncluded = Int(Double(price) * (1 + tax)) // お馴染みのキャストと同じ書き方になる let endOfInteger = Int128(Int64.max) + 1 // 自作の Int128 から Int への変換もいつも通り let stride = Int(Int128.max / samples)
  20. 20. ⭐ 5 / 7
  21. 21. // 標準ライブラリーに規定されている性質 /// 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 }
  22. 22. // プロトコルで比較可能性を説明すると… 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)
  23. 23. // 比較可能な要素を扱う場合だけ使えるようになっている 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))
  24. 24. ⭐ 6 / 7
  25. 25. // 標準ライブラリーに規定されている性質 /// A type with a customized textual representation. protocol CustomStringConvertible { /// A textual representation of this instance. var description: String { get } }
  26. 26. enum Device : CustomStringConvertible { case iPhone, iPad, appleWatch var description: String { switch self { case .iPhone: return "iPhone" case .iPad: return "iPad" case .appleWatch: return " Watch" } }
  27. 27. let device = Device.appleWatch // テキスト表現への Narrow 変換 let displayText = String(describing: device) // テキスト表現に変換して、テキストコンソールに出力 print(device) // String 型の文字列補完構文は、テキスト表現を使用 let message = "I love (device)"
  28. 28. ⭐ 7 / 7
  29. 29. // 変数 floatValues が Array<Float> で定義されている let floatValues: [Float] // サブスクリプトに範囲を渡すと、部分配列が取れる let slice = floatValues[2 ..< 6] // 部分配列の型は ArraySlice<Float> になる type(of: slice) == ArraySlice<Float>.self
  30. 30. // 一般的な定義の仕方 func sum(of values: [Double]) -> Double { return values.reduce(0, +) } // Float 型の配列の部分配列の合計を計算 sum(of: floatValues[2 ..< 6].map(Double.init))
  31. 31. // 任意の浮動小数点数を対象にする func sum<T: FloatingPoint>(of values: [T]) -> T { return values.reduce(0, +) } // Float 型の配列の部分配列の合計を計算 sum(of: Array(floatValues[2 ..< 6]))
  32. 32. // 標準ライブラリーに規定されている性質 /// 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
  33. 33. // 連続する任意の浮動小数点数を対象にする func sum<S: Sequence, T: FloatingPoint> (of values: S) -> T where S.Iterator.Element == T { return values.reduce(0, +) } // 連続する浮動小数点数の部分配列の合計を計算 sum(of: floatValues[2 ..< 6])
  34. 34. // 自身の値でストライドできる値を対象にする func sum<S: Sequence, T:Strideable>(of values: S) -> T where S.Iterator.Element == T, T.Stride == T { return values.reduce(0) { $0.advanced(by: $1) } } // 連続する値の部分配列の合計を計算 sum(of: floatValues[2 ..< 6]) sum(of: integerValues[2 ..< 6])
  35. 35. // 整数を対象とした合計関数 func sum<S:Sequence, T:Integer>(of values:S) -> T where S.Iterator.Element == T { return values.reduce(0, +) } // 浮動小数点数を対象とした合計関数 func sum<S:Sequence, T:FloatingPoint>(of values:S) -> T where S.Iterator.Element == T { return values.reduce(0, +) }
  36. 36. 
 
 
 
 
 
 


×