詳解Swift#4(chapter7 演算子)
昨日に続いて、詳解Swiftの写経を続ける。
- 作者: 荻原剛志
- 出版社/メーカー: SBクリエイティブ
- 発売日: 2015/12/25
- メディア: 単行本
- この商品を含むブログを見る
chapter7 演算子
演算子だけのchapterがあるってすごい。内容はさらっと。
Swiftの演算子
目についた部分だけメモ。
- シフト演算
- 四則演算
- ビット操作
などが優先。
- 比較
- 条件判断
などは優先度低。
// オーバーフロー演算子 z - 1 //z - 2 <- error z &- 2 // 剰余演算子 11 % 2.5 11.0 % 2.5 var x: Int = 10 x = 5 x % 2 x // 5 x %= 2 // 複合代入演算子 x // 1 // ビット演算子 func odd(i:Int) -> Bool { print(i); return i&1 == 1 } var b = true var c1 = b || odd(1) // oddは実行されない var c2 = !b && odd(3) // oddは実行されない
&&
, ||
は短絡評価。
演算子の定義
Swiftでは演算子を独自に定義したり、既存の定義をオーバーライドすることもできる(できないものもある)。
infix operator 演算子 { precedence 数字(優先度) associativity 規則(left|right|none) } func 演算子(左項: 型, 右項: 型) { return 戻り値 }
- 前置演算子
prefix operator 演算子 {}
- 後置演算子
postfix operator 演算子 {}
でそれぞれ定義できる。
- サンプル
// ~ を演算子として定義した例 infix operator 〜 { // 二項(中置)演算子として宣言 precedence 20 // 他の演算子より低い優先度 associativity none // 結合規則は「なし」 } func 〜 (n:Int, w:Int) -> String { var str = String(n) // Stringのイニシャライザで、整数を文字列にする let pad = w - str.characters.count // 左に詰める空白の個数 if pad > 0 { str = String(count:pad, repeatedValue:Character(" ")) + str } return str }
- パターンマッチ演算子
タプル、列挙型などのパターン、整数と区間についてのマッチングを行う演算子。 ~=
で利用できる。また、~=
演算子を独自定義することで必要に応じた場合分けができる。が、グローバルに影響を与えるので、使うのはあまり、、、、的な記述が最後にあった。
enum Rank : Int { case Ace = 1, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King } // 実体型の値と等しい時もマッチしたものとする func ~= (p:Int, r:Rank) -> Bool { return p == r.rawValue } let hand:[Rank] = [ .Queen, .Ace, .Ten, .Six, .King, .Jack ] for card in hand { switch card { case 1: print("Ace") case 13: print("King") case .Queen: print("Queen") default: print(".") } }
実体型の値もマッチするので、 "Ace", "King" も出力される。
所感・雑感
演算子
- 独自定義
明日も続ける。
詳解Swift#3(chapter6 パターン)
昨日に続いて、詳解Swiftの写経を続ける。
- 作者: 荻原剛志
- 出版社/メーカー: SBクリエイティブ
- 発売日: 2015/12/25
- メディア: 単行本
- この商品を含むブログを見る
chapter6 パターン
いくつかのデータを組にして扱うことができる タプルと列挙型 についての章。
タプル
複数個のデータを組にしてまとめたもの。データは値型。
といったシーンで活躍する。
- シンプルなタプルの例
let m = ("monkey.jpg", 161_022) let m2: (String, Int) = ("monkey.jpg", 161_022) let cat: (String, Int, Int) = ("cat.jpg", 1024, 768) var img = cat //img = m2 <- error var img2 = cat print(cat.0) // cat.jpg img.1 = 1920 // img == img2 <- error let m3 = (file: "tiger.jpg", width: 1024, height: 768) m3.file m3.0
変数名: (1つ目の要素の型, 2目の要素の型, .....) = (要素1の値, 要素2の値, .....)
で記述する。タプルの要素へのアクセスは タプル変数.添え字
と書く。要素にキーワードが付いている場合は タプル変数.要素キーワード名
でもアクセスできる。要素の値を書き換えることもできる。タプルの要素の1つにタプルを入れることもできる。
タプルの代入には要素の個数と型が一致していなければいけない、タプルの比較はできないなどの制約がある。
- タプルを返す関数
func BMI(tall:Double, _ weight:Double) -> (bmi: Double, idealWeight: Double) { let ideal = 22.0 // 理想的な値 let t2 = tall * tall / 10000.0 // cm を m に換算して二乗 let index = weight / t2 // BMIを計算 return (index, ideal * t2) // 目標体重も計算して返す } var ret = BMI(172.0, 58.5) print(ret.bmi) print(ret.idealWeight)
関数の戻り値を (Double, Double)
とすることで、2つのDouble型の値を含むタプルを返却できる。これは便利。
- キーワード付きのタプル
let img = ("phoenix.png", 1200, 800) let photo = (file:"tiger.jpg", width:640, height:800) print(img) print(photo) var v1 : (String, Int, Int) = img var v2 : (file:String, width:Int, height:Int) = img var v3 : (image:String, x:Int, y:Int) = img v1 = photo v2 = photo // v3 = photo // エラーになる print("v1 = \(v1)") print("v2 = \(v2)") v3 = photo as (String, Int, Int) print("v3 = \(v3)")
キーワード付きのタプルへの代入は同じキーワードと型を持つタプルかキーワードを持たないタプルでないとできない。↑の例だと異るキーワードを持つタプルv3への代入はエラーとなる。
列挙型
Swiftでの列挙型は列挙型の中にメソッドやプロパティの定義ができたり、イニシャライザも定義できたり拡張性が高い。
- シンプルな列挙型
enum Direction { case Up case Down case Right case Left } Direction.Up print(Direction.Up, terminator: "") // Up
- メソッド付きの列挙型
enum Direction { case Up, Down, Right, Left func clockwise() -> Direction { switch self { case Up: return Right case Down: return Left case Right: return Down case Left: return Up } } mutating func changeClock() { switch self { case Up: self = Right case Down: self = Left case Right: self = Down case Left: self = Up } } } let d = Direction.Up var d2 = Direction.Up d.clockwise() print(d) // 値は変わらない //d.changeClock() <- error d2.changeClock() // ここで値を変更(Up -> Right) print(d2)
メソッド付きの列挙型。 mutating
キーワードを付けると列挙型自身の値を変更するメソッドも作れる。計算型のプロパティも定義できる。
- 列挙体のタイプメソッド、タイププロパティ
enum Direction : Int { case Up = 0, Right, Down, Left static var defaultDirection = Direction.Up init() { self = Direction.defaultDirection } static func arrow(d:Direction) -> String { return ["↑", "→", "↓", "←"][d.rawValue] } func clockwise() -> Direction { let t = (self.rawValue + 1) % 4 return Direction(rawValue:t)! } var horizontal: Bool { switch self { case Right, Left: return true default: return false } } mutating func turnBack() { self = Direction(rawValue:((self.rawValue + 2) % 4))! } } Direction.defaultDirection = .Right var e = Direction() print(Direction.arrow(e))
構造体と同様にタイプメソッド、タイププロパティは static
, イニシャライザは init
で定義できる。
共用型の列挙型
enum WebColor { case Name(String) // 色の名前 case Code(String) // 16進数によるカラーコード case White, Black, Red, Blue, Green, Yellow, Gray } let indigo = WebColor.Name("indigo") // インディゴブルー let turquoise: WebColor = .Code("#40E0D0") // ターコイズ let textColor = WebColor.Black
値型の列挙型とはまったくユースケースが異る。同じ役割のオブジェクトなんだけど、使い方(定義の仕方)が違う。みたいな場合に使えそう。
- if-case文
enum Ticket { case 切符(Int, Bool, 回数券:Bool) // 普通券:価格、小人、回数券かどうか case カード(Int, Bool) // プリペイドカード:残高、小人 case 敬老パス // 敬老パス } var list:[Ticket] = [ .切符(250, false, 回数券:false), .カード(3300, false), .敬老パス, ] for p in list { /* エラー if p == .カード { print("プリペイドカード") } */ switch p { // switch文ではこのように記述できる case .カード: print("プリペイドカード") default: break } } // if-case文を使うとよりシンプルに for p in list { if case .カード = p { print("プリペイドカード") } }
共用型の列挙型の配列から条件にあうものだけで処理したい場合に使える。残念だけど今のところ、どれくらい便利なのかがわからん。
- for-in文でもcaseパターンを使える
enum Ticket { case 切符(Int, Bool, 回数券:Bool) // 普通券:価格、小人、回数券かどうか case カード(Int, Bool) // プリペイドカード:残高、小人 case 敬老パス // 敬老パス } var passes:[Ticket] = [ .切符(320, false, 回数券:false), .切符(120, true, 回数券:true), .切符(240, false, 回数券:true), .切符(240, true, 回数券:false), .敬老パス, .カード(3300, false) ] for case let .切符(fare, child, coupon) in passes where fare > 220 { var k = coupon ? "回数券" : "普通券" if child { k += "(小人)" } print(k, "\(fare)円") }
ここまで読んで有用性が少し見えてきた気がする。列挙型の配列の中から複数条件を指定して処理を書く場合にうまく使えばシンプルに書ける。ただ、caseパターンが複数あったり、条件が複雑過ぎて横に記述が長くなるので万能ではない印象。
- 再帰的な列挙型
enum メッセージ : CustomStringConvertible { case 文書 (String, String) // 差出人、文書 case データ(String, [Int8]) // 差出人、データ列 indirect case 転送(String, メッセージ) // 差出人、メッセージ var description: String { switch self { case let 文書(from, str): return from + "(" + str + ")" case let データ(from, _): return from + "[データ]" case let 転送(from, msg): return from + "←\(msg)" } } } let m1 = メッセージ.文書("伊藤", "休みます") print(m1) // 伊藤(休みます) let m2 = メッセージ.転送("白石", m1) print(m2) // 白石←伊藤(休みます) let m3 = メッセージ.転送("山田", m2) print(m3) // 山田←白石←伊藤(休みます)```
再帰的に列挙型のタプルに列挙型自身を要素として指定する場合は indirect
キーワードをcaseパターンの前に記述する。
パターンマッチ
どの構造に何が当てはまるのかというルールを定義して、ルールに基づいたデータ処理をパターンマッチという。
- タプル
- 共用型の列挙型
で利用できる。
- ワイルドカード
_
- 変数/定数
- Switch文においての整数、区間
などでパターンを書いて、それぞれにマッチする場合の処理を記述する。
所感・雑感
- タプル
- 代入の要素/型制約などを見ても型についての厳格さを感じる。個人的には嫌いではないものの、実戦では少し窮屈に思う時があるかもしれない
- タプルの要素にはキーワードを付けるのをデフォルトにしたい。添字だけだと、コードが大きくなった場合に見通しが悪くなりそう。
- 列挙体
- 構造体と同様に確かに拡張高い。コード系の管理とか、コードに関する振る舞いを列挙体自身のメソッドにしておくと、一貫性あるコードを書けそう。
- 「共用型」という名前の意味がわからん。何を共用するのか。
- if-case / for-in でのcaseなどはライブラリのコードを読んでみたい
- パターンマッチ
- 例が少なかったので、逆引きリファレンスとかレシピがほしい
今日はあんまり時間とれなかったので1章分。夜に時間あったらもうちょい進める。
詳解Swift#2(chapter3 構造体~5 基本的なデータ型)
昨日に続いて、詳解Swiftの読み込みと写経を続ける。
- 作者: 荻原剛志
- 出版社/メーカー: SBクリエイティブ
- 発売日: 2015/12/25
- メディア: 単行本
- この商品を含むブログを見る
chapter3 構造体
構造体(Structure)についての章。
構造体の定義
struct 型名 { 変数/定数定義 イニシャライザ定義 メソッド定義 その他の定義(型、プロパティなど) }
で書く。
用語の話
- イニシャライザ: 構造体のインスタンス生成時の手続き
- 既定イニシャライザ: 各プロパティの初期値を記述してある構造体を引数なしで生成できるイニシャライザ
- 全項目イニシャライザ: 既定イニシャライザの逆。個々のプロパティの値を指定して生成するイニシャライザ
シンプルな構造体 / 初期値 / イニシャライザ
// 初期値のない構造体 struct Date { var year: Int var month: Int var day: Int } // 初期値のある構造体 struct DateInit { var year: Int = 2016 var month: Int = 5 var day: Int = 3 } var dx = DateInit() print(dx, terminator:"") var d = Date(year: 2016, month: 5, day: 3) // "."でプロパティ名を指定して参照可能 print(String(d.year), terminator:"") // "2016" // 演算もできる d.day++ ++d.day print(String(d.day), terminator:"") // "5"
構造体の要素(プロパティ)は初期値があったり、なかったりする。構造体を定義しておくと、構造体のインスタンスを生成して、各プロパティに".プロパティ名"でアクセスできるし、プロパティの値を変更したりもできる。
- 定数プロパティと構造体
struct DateX { var year: Int var month: Int var day: Int } // 定数としても代入できる let l = DateX(year: 2016, month: 5, day: 3) //l.day++ <- error (変更できない) struct Time { let in24h: Bool = false var hour = 0, min = 0 } var t1 = Time() var t2 = Time(hour: 11, min: 0) //var t3 = Time(in24h: true, hour: 11, min: 0) <- error
プロパティには定数も定義できる。当然、定数のプロパティは変更不可。イニシャライザによってインスタンス生成時に定数のプロパティに値をセットするケースと、初期値が定義されているケースとある。
- イニシャライザの定義
struct Date { var year, month, day: Int init() { year = 2016 month = 5 day = 3 self.year++ } } var d = Date() d.year // 2017
構造体に init
キーワードでイニシャライザを定義できる。
- 複数のイニシャライザ
struct Time { let in24h: Bool var hour = 0, min = 0 // 1つめのイニシャライザ init(oHour: Int, oMin: Int) { in24h = false self.hour = oHour self.min = oMin } // 2つめのイニシャライザ init(hourIn24 h: Int) { in24h = true self.hour = h } // 3つめのイニシャライザ init(_ hour: Int) { self.init(hourIn24: hour) } } var a = Time(oHour: 11, oMin: 30) var b = Time(hourIn24: 12) var c = Time(13) //var d = Time() <- error //var e = Time(in24h: true, hour: 12, min: 0) <- error
イニシャライザは複数定義できる。またイニシャライザが定義されている場合は既定イニシャライザと全項目イニシャライザは使えなくなる。
- 構造体を含む構造体とネスト型
struct DateWithTime { var date = Date() var time = Time(oHour: 12, oMin: 30) } var u = DateWithTime() u.date.year u.time.hour
構造体を含む構造体も作ることができる。"."の繰り返しで必要なプロパティにアクセスできるので宣言的に書ける。
struct SimpleTime { var hour, min: Int init(_ hour:Int, _ min: Int) { self.hour = hour self.min = min } } struct PointOfTime { struct Date { var year, month, day: Int } typealias Time = SimpleTime var date: Date var time: Time init(year: Int, month: Int, day: Int, hour: Int, min: Int) { date = Date(year: year, month: month, day: day) time = Time(hour, min) } }
メソッド
struct Clock { var hour = 0 var min = 0 func time() -> Clock { return Clock(hour: hour, min: min) } mutating func advance(min: Int) { let m = self.min + min if m >= 60 { self.min = m % 60 self.hour = (self.hour + m / 60) % 24 } else { self.min = m } } func nonAdvande(min: Int) { //self.min += min <- error } static func isPm(h: Int) -> Bool { return h > 12 } }
構造体にはメソッドを定義できる。構造体の内容を変更するメソッドには mutating
キーワードを付与する必要がある。オブジェクト指向でのクラスメソッドに相当するタイプメソッドは static
キーワードを付与して定義する。タイプメソッドはイニシャライザの中でも使えるが、インスタンスメソッドはプロパティの初期設定が完了していないと使えない。
プロパティ
- タイププロパティ
タイププロパティはクラス変数的なもの。
イニシャライザからタイププロパティ/タイプメソッドにアクセスするときは構造体の修飾子が必要。一報でタイプメソッドからタイププロパティにアクセスする場合は修飾子不要。タイププロパティを変更するメソッドには mutating
キーワードは不要。
- 格納型プロパティ
var serialNumber = 2127 struct LCD { struct Size { var width, height : Int } static var stdHeight = 1080 static var stdWidth = 1920 static var stdSize = Size(width: stdWidth, height:stdHeight ) var size: Size let serial = "CZ:\(++serialNumber)" init(_ w:Int, _ h:Int) { size = Size(width: w, height: h) } } let small = LCD(800, 600) print(small.serial) LCD.stdHeight = 1200 print(LCD.stdSize) LCD.stdWidth = 2560 print(LCD.stdSize)
プロパティに式を指定することもできる。タイププロパティの初期化のための式は値を必要としたタイミングで初めて評価され、その後は使われない(変更されない)。値を都度変更して使いたい場合は 計算型プロパティ を使う。
この辺り、慣れないと読み違えそうな雰囲気...
- 計算型プロパティ
struct Ounce { var ml: Double = 0.0 static let ounceUS = 29.5735 var ounce: Double { get { return ml / Ounce.ounceUS } set { ml = newValue * Ounce.ounceUS } } init(ounce: Double) { self.ounce = ounce } } var a = Ounce(ounce: 1.5) a.ml // 44.3625 a.ounce = 3 // ounce代入によってmlも値が変わる a.ml // 88.7205 a.ounce // 3
クラス変数のアクセサ的なもの。set節とget節によって参照/更新を行う。set節の引数は省略可能で、 newValue
という名前で引数を利用できる。
読み書き可能なプロパティを関数のinout引数に渡すこともできる。この辺り、多分実戦になると構造体のプロパティを引数に関数を呼ぶことがある気がするのでなかなか気の利いた設計になってるように感じた。
- プロパティオブザーバ
struct Stock { let buyingPrice: Int var high = false var count = 0 init(price:Int) { buyingPrice = price self.price = price } var price:Int { // プロパティオブザーバ(更新前に呼び出される) willSet { ++count high = newValue > buyingPrice } // プロパティオブザーバ(更新後に呼び出される) didSet { print("\(oldValue) => \(price)") } } } var st = Stock(price:400) st.price = 410 st.price = 380 st.price = 430 print("\(st.count), \(st.high)") st.price++ print("\(st.count), \(st.high)")
格納型プロパティの値が更新されるタイミングで別の手続きを呼び出す。更新前、更新後それぞれの手続を定義できる。Railsで言うところのフィルタみたいな機能。 willSet
, didSet
というコードブロックで定義する。インスタンス生成時にはプロパティオブザーバは発動しない。
添字付けはあんまり用途なさそう、というか実装したあとで可読性下がりそうなので説明は割愛
プロトコル
後のchapterで説明があるので、ここでは概念だけ。 型が持つべきメソッドやプロパティなどの宣言をまとめる仕組み をプロトコルと呼ぶ。インタフェースみたいなやつ。
- 例
- Int - Double - String
などのデータ型はComparableプロトコルに準拠しているので、比較演算を行うことができる。
chapter4 オプショナル
Swiftの特徴の1つ。「扱うデータがない」という状態をとりうる型。頻出、超重要。
オプショナル型
- オプショナル型とnil
var a: Int = 0 var b: Int? = 100 b = nil //a = nil <- error // このようにも書ける var c: Optional<Int> = 0 c = nil c = 5
nilを取りうる変数は型名の末尾に ?
をつける。オプショナルではない変数にはnilを代入できないので、Null Pointer例外による実行時エラーを少なくできる(と期待されている)。たとえば、nilを返す可能性がある関数やイニシャライザの戻り値を受け取る変数の型はオプショナルでなくてはいけない。
- 開示
var a: Int = 10 var c: Int? = 5 //a + c <- error a + c! c = nil //a + c! <- error
オプショナルInt型とInt型は厳密には異なるので、そのまま演算できない。オプショナル型の変数に !
を付けてデータを開示して利用する。値がnilだった場合は開示しても実行時にエラーとなる。
オプショナル束縛構文
let year: Int? = 2016 if let y = year { print("yearはnilじゃない。\(y)だ。") } else { print("yearはnil") } // letじゃなくてvarでも受けられる if var ye = year { print("yearはnilじゃない。\(y)だ。") } else { print("yearはnil") } // 特別な意味を持って開示された状態で代入されるのは if/while の条件式で書いた時のみ var z = year // これだとフツーにオプショナル型になる
とある変数がnilじゃなかったときに必要な処理をしたいユースケースに適用できる。if-let文というらしい。条件式で代入している変数(この例だと y
)はオプショナル型ではなくなるので、開示は不要となる。オプショナル束縛構文ともいうらしい。ちなみにelseに入った場合は条件式で代入した変数は使えない。
これは結構使えそうだが、1つ残念なのは三項演算子では使えないこと。三項演算子で使えないのはなんでなんだろ。
var opv: Int? = 0 (opv != nil) ? opv! : 5 opv ?? 5 //↑と同じ意味
自己代入的なあれ。これも結構使いそう。
有値オプショナル型
var year: Int! = 2020 print("あと\(year - 2016)年") //year++ <- error year = nil var month: ImplicitlyUnwrappedOptional<Int> = 2020
開示しなくても通常の変数のように使える型のこと。nilを代入できてしまうが、開示は不要。はじめはnilを許可しておくのだけど、どこかのタイミングで値が入ることが保証されている場合に開示を意識せずにコードを書けるので、実戦では役に立ちそう。
func nickname(name:String?, age:Int) -> String { let s = name ?? "名無し" return "浪速の" + s + "(\(age)歳)" }
これはめっちゃ実践的なコードな気がする。関数の引数にnilが入って来る場合を考慮して let s
で受けて自己代入して実際の処理を行うパターン。
失敗のあるイニシャライザ
struct Time { let in24h: Bool var hour = 0, min = 0 init?(_ h:Int, _ m:Int, in24h:Bool = false) { let maxh = in24h ? 23 : 11 if h < 0 || h > maxh || m < 0 || m > 59 { return nil } self.in24h = in24h hour = h min = m } init(time:Time, in24h:Bool) { var h = time.hour if !in24h && time.hour > 11 { h -= 12 } self.in24h = in24h hour = h min = time.min } }
インスタンス初期化時にnilを返す可能性があるイニシャライザ。データが作れなかったり、引数に不備があったりして初期化に失敗した場合にnilを返す。 init?
でイニシャライザを定義する。イニシャライザの中で例外ケースの場合に return nil
する。
chapter5 基本的なデータ型
この章はデータ型メインの話なのでさらっと。
整数と実数
- 数値型リテラル
- 桁数の大きい数値を見やすくするために
_
を使える - 16進数の場合は
0x
, 8進数の場合は0o
で表す(両方小文字)
- 桁数の大きい数値を見やすくするために
- Swiftには暗黙の型変換がないので、異なる型の演算には型変換が必要
- 型変換例
Int.init() Int.init(_: Double) Int.init(_: UInt) . . .
- 範囲演算子
型 | 演算子 | 意味 |
---|---|---|
半開区間型 | A..<B | A ≦ x < B |
閉区間型 | A...B | A ≦ x ≦ B |
範囲型 | A..<B | A ≦ x < B |
範囲型 | A...B | A ≦ x ≦ B |
半開区間型と閉区間型はわかるけど、範囲型で書き直す意味がよくわからない。
配列
コレクションの1種。最初に宣言した型の値しか格納できない。配列はCollectionTypeというプロトコルに準拠している。
- 部分配列
var days = ["日", "月", "火", "水", "木", "金", "土"] let sub = days[2..<5] // sub は ArraySlice型 print(sub) // [火, 水, 木] を出力 print(sub.count) // 3 を出力。要素は3つ。 print(sub.startIndex) // 2 を出力。配列の最初の添字を表すプロパティ。 print(sub[2]) // "火" を出力 print(sub[4]) // "木" を出力
添字を指定して元ある配列から部分的に抽出した配列を作れる。Array型ではなくてArraySlice型となる。添え字は0から始まらず、部分的に切り取った添え字がそのまま残る。
若干わかりづらくて、バグの温床になりそうな気配。あまり使いたくない印象。
配列のプロパティ
- count
- first
- isEmpty
- last
配列のメソッド
- append
- appendContentsOf
- insert
- removeAll
- removeAtIndex
- removeLast
- reverse
だいたいどれもメソッド名見れば振る舞いがわかる。
- 多次元配列
var table: [[String]] = [["A","B"],["X","Y","Z"]] print(table[0]) // ["A", "B"] を出力 print(table[1][0]) // X を出力 table[0].removeAtIndex(1) // "B" が削除される print(table) // [["A"], ["X", "Y", "Z"]] を出力
[[型]]
で定義する。
文字列と文字
String型のプロパティ
- isEmpty
- characters
- unicodeScalars
- utf8
- utf16
式展開
\(変数)
で式展開できる
var str: String = "kentana20" print("わたしは \(str) です")
文字列のところは、実戦でつかうときに辞書的に引きそうなので割愛。文字コードとかUnicodeの概念がわかってれば大丈夫。
辞書
Dictionary, Hash的なもの。これも配列同様に始めに定義した型のデータのみを扱える。Swiftは型による制約が厳しい。
- シンプルな辞書
var d: [String: Int] d = ["swift": 2014, "Objective-C": 1983] d["swift"] // 2014
- 辞書とfor-in
let dic = ["Mars": "火星", "Venus": "金星"] for (en, ja) in dic { print("\(en) = \(ja)") } // こんな感じでも書ける for t in dic { print("\(t.0) = \(t.1)") }
- 辞書のプロパティとメソッド
- updateValue
- removeValueForKey
- count
集合
配列に似ているが、以下の点で異る。
- 同一の要素を重複格納できない
- 要素の順序に意味が無い
集合演算を行う必要がある場合は集合を使う。
- 集合のメソッド
- contains
- insert
- remove
- removeFirst
- isSubsetOf
- union
だいたいわかる。
所感・雑感
3~5章まで読了。気になったことをいくつか書いてみる。
- 構造体はかなり便利に使えそう。イニシャライザが複数定義できるので、用途に応じて初期化の仕方が変わることは多々あるので良さそうな気配
- オプショナル/有値オプショナルもうまく使えば厳密なコードになりそうなので好印象
- 式展開や自己代入など、よく使う機能もしっかり整備されてるのでやっぱりSwift扱いやすくて、且つ型定義に厳しいので保守性高いコードが継続的に書けそうで、多少人数増えても統一性のあるコードにできそう
- 以下細かな疑問
明日も進めるぞ。
詳解Swift#1(chapter1~2)
7月下旬までの3ヶ月でアプリ1本作るために、Swift再入門する。 GWで詳解Swift進捗させたいので、Chapterごとのまとめを書いておく。
- 作者: 荻原剛志
- 出版社/メーカー: SBクリエイティブ
- 発売日: 2015/12/25
- メディア: 単行本
- この商品を含むブログを見る
chapter1 Swiftでプログラミング
1章は型とか、変数、定数、構文、プレイグラウンドなどの第一歩的な話なので部分的に掻い摘んで書く。
データ型
種類 | 型名 | 説明 |
---|---|---|
整数型 | Int | 整数全般はコレ |
整数型 | UInt | 符号なし整数 |
実数型 | Float | 浮動小数点 |
実数型 | Double | 高精度浮動小数点 |
論理型 | Bool | true/false |
文字 | Character | Unicodeの1文字 |
文字 | UnicodeScalar | Unicodeの文字コード |
文字列 | String | Unicode文字列 |
変数 / 定数 / 文字列 / 配列
- 変数: var
var x: 型 = 初期値
で書く
- 定数: let
let y: 型 = 代入値
で書く
型推論が使えるので、型は省略可能。
文字列
- Stringのインスタンス
"
で囲う+
で連結可能
print
print(xxxx)
で標準出力に書けるseparator: ""
,terminator: ""
で区切りと最終文字を定義できる
配列
var a : [Int] = [1,2,3,4,5]
var s : [String] = []
制御構文
- while
var i = 1, j = 2 while i < 2 { ++i }
- repeat-while
var n = 10 repeat { print("\(n) ", terminator:"") --n } while n > 1
- ラベル付き
let days = 31 let firstDay = 2 var w = 0 for ; w < firstDay; w++ { print(" ", terminator:"") } var d = 1 loop: while true { for ; w < 7; w++ { let pad = d < 10 ? " " : "" print(pad + " \(d)", terminator:"") if ++d > days { print("") break loop } } print("") w=0 }
コマンドラインからSwift
- swiftc
$ swiftc xxxx.swift
でバイナリ作れる。
chapter2 関数
関数定義
var total = 0 func count(n: Int) -> Int { total += n return total } func reset() { total = 0 }
呼び出すときは↓。戻り値を使わない場合は受け取らなくてもいい。
count(10) var ret = count(5) reset()
- 外部引数名
func buyProduct(product: Int, price: Int, quantity: Int) { print("Product: \(product), amount: \(price*quantity)") } buyProduct(19090, price:18000, quantity:1)
第一引数は外部引数名を指定しない。 (指定するとコンパイルエラーになる)
Objective-C, Swiftの流儀として、 関数名には関数の役割+第一引数の意味を説明したものにする ため、第一引数に外部引数名を指定しないらしい。
- 内部引数名
func area(height h: Double, width w: Double) -> Double { return h * w } area(height: 10.2, width: 1.5) //area(10.2, width: 1.5) <- error //area(width: 1.5, height: 10.2) <- error
外部引数名が指定されている場合は、呼び出し時の引数名指定が必須となる。 外部引数名を指定しているからといって、引数の順序を入れ替えられるわけではない。順番通りにしないといけない。
func areaNew(h: Double, _ w: Double) -> Double { return h*w } areaNew(10.2, 1.6)
第二引数の先頭に_
をつけると、外部引数名の呼び出しを省略できる。呼び出し側を考えるとこれが一番シンプル。
- 内部引数の省略
func compare(a:Int, _ b:Int, _:Bool) -> Bool { return a > b } compare(10, 15, true) func compareX(a:Int, _ b:Int, option _:Bool) -> Bool { return a > b } compareX(10, 11, option: true)
インタフェースを揃えるために引数定義はするけど使わない場合は _:Bool
のように内部引数名を省略できる。
- 下線(ワイルドカード)
_
は何とでも組み合わせて使えるワイルドカード。繰り返しの値を使わない場合とかに以下のように書ける。
for _ in 1...10 { print("hoge") }
関数のいくつかの設定
- inout引数 (参照渡し)
func mySwap(inout a:Int, inout b:Int) { let t = a; a = b; b = t } var x = 1; var y = 3 mySwap(&x, b: &y) print(x, terminator:"") print(y, terminator:"")
引数の値を関数内で変更する場合に inout
を付与した引数を使う。その場合は関数呼び出し時に &
を付けた変数を指定する必要がある。
- 引数の既定値
let fontSize: Float = 12.0 func setFont(name: String, size: Float = fontSize, bold: Bool = false) { print("\(name) \(size)" + (bold ? "[B]" : "")) } setFont("ReglanPunch") setFont("Times", size: 15.0, bold: false) setFont("Times", bold: false, size: 15.0) func setFontX(name: String, size: Float, bold: Bool) { print("\(name) \(size)" + (bold ? "[B]" : "")) } setFontX("Meryo", size: 16.0, bold: false) //setFontX("Meryo", bold: false, size: 14.0) <- error
いわゆるoptional引数。既定値を定義しておくと呼び出し時に省略可能。optionalじゃないときは順番変えたらダメだったくせに、optionalのときは順番変えてもフツーに動く。書籍には既定値ありの引数についての制約みたいのがだ~っと書いてある。 こういうのはシンプルじゃなくてあまり好きじゃない。極力optional引数はナシでコード書きたい。
- 引数の値を変更できるようにする
func dayOfWeek(var m:Int, _ d:Int, var year y:Int = 2016) -> Int { if m < 3 { m += 12; --y } let leep = y + y / 4 - y - y / 100 + y / 400 return (leep + (13 * m + 8) / 5 + d) % 7 } dayOfWeek(5, 9)
引数に var
を付けると、関数内で引数を変更可能になる。付けない場合と let
を付けた場合は関数内での変更は不可。
ちょっと構文的に複雑になる。
- 関数のネスト
func printMonth(firstDay:Int, _ days:Int) { func daystr(d:Int) -> String { if d <= 0 { return " " } else { return (d < 10 ? " \(d)" : " \(d)") } } var d = 1 - firstDay while d <= days { var line = "" for _ in 0 ..< 7 { line += daystr(d) if ++d > days { break } } print(line) } } printMonth(4, 31) //daystr(1) <- error
クロージャ。この例で行くと daystr
関数は外部からは呼べない。とある関数内でしか使わない関数はこの記法で良い。
関数のオーバーロード
ここはコード無しで。
- 引数の数、型、外部引数名などを変えて関数のオーバーロードが可能
所感・雑感
半日ゆっくり読み進めた。序章感があったので、大きなトピックはなし。関数のOptionalの部分は少し違和感はあるものの、外部引数名のくだりは自分は結構好みかも。 しっかり読むと理解が進む。明日はもうちょい進捗させよう。
Retty Tech Cafe #5 (テーマ: SRE) に行ってきた
3.12(土) に五反田のRettyオフィスで開催されたRetty Tech Cafe #5 に行ってきました。
http://connpass.com/event/26679/
タイムテーブルはこんな感じ。今回のテーマは "SRE" ということで、自分が一休.comで担当している業務に近しいものがあったので興味を持って参加されていただきました。
# | Speaker | Title |
---|---|---|
1 | Retty CTO 樽石さん(@taru0216) | Retty SRE のご紹介〜 元 SRE エンジニアによる SRE の概要と Retty での適用事例について 〜 |
2 | All About 鈴木さん (@jp_taku2) | オールアバウトをオンプレミスで支える技術 |
3 | mercari 長野さん (@kazeburo) | メルカリにおける、継続的なアプリケーションの改善を支える技術 |
4 | Retty teemuさん | Ensuring highly available services. Infrastructure and service monitoring |
あんまりメモれてなかったのでスライドが公開されたらアップデートしたいです。
Retty SRE のご紹介〜 元 SRE エンジニアによる SRE の概要と Retty での適用事例について 〜
CTO 樽石さんによるSREの紹介。樽石さんはRedhat -> Google -> 楽天 -> Rettyという経歴でGoogle時代にSREを担当されていたそうです。
Retty
食を通じて世界中の人々をHappyに。 というコンセプトの元、実名のクチコミを特徴としたグルメ系キュレーションサービスを展開していて、食という領域はまだまだ伸びると思っている。とのこと。
懇親会で中の人とお話する機会があったのですが、全社では60名くらいでエンジニアは20名くらいだとおっしゃっていました。一休.comとくらべて比率的にはエンジニアが多い印象ですね。
SRE
直訳するとサイト信頼性エンジニアリング。
ユーザがサービスを快適に利用できるようにシステムを設計・構築・運用するエンジニアリング活動
を指す。
指標は主に3つ
- QPS(Traffic)
- 稼働率(Availability)
遅延(Latency)
インフラエンジニアとの比較
- 似ているスキルセットやマインドを持っていることが多い
- ITサービスの基盤に関わるエンジニアがインフラエンジニア
- 基盤の信頼性が低いとサービスに関わるのでSRE的考えが自然発生していく
アプリSREの例(単純例)
- アプリからサーバAPIにリクエストを送ったらエラーが返ってきたのでリトライした
SREというワードはあまり耳慣れませんが、アプリケーションエンジニアにしても、インフラエンジニアにしても、サービスに長く関わっているとこういった改善活動は必要になってきて、普段意識せずにやっていることがSREの一環だった、ということが多いのではないかと思いました。
- 3つの指標を達成するために重要な2つの要素
- 障害発生時の応急処置・調査
- サービス規模が拡大すると障害発生から対応までのスピードが重要性を増す
- 障害に強い開発
- 指標を定量化するための開発なども行うようになる
- 障害発生時の応急処置・調査
SRE in Retty
ふりかえってみると、4つくらいのフェーズがあったように思う。
- シンプルな構成だった時代
- キャッシュとスケールアウトを進めた時代
- 監視力を強化していった時代
- ビッグデータ解析ができるようになった時代 <- イマココ
フェーズのはじめの方はインフラ的な要素が多く
- インフラのスケール
- 見える化
などを推進してSRE活動を行ってきた。
後半のフェーズではインフラだけでなく、アプリのクラッシュ率改善のようなアプリ側でのSRE活動が成果を生んだりした。
future of SRE in Retty
これからRettyは海外展開をしようとしている。で、まずは海外展開可能なインフラの整備から進めていて、Cloud on Cloudでサービスを提供できるようにしたい。
これをCTOの樽石さんが1人でやっているらしく、「誰か一緒にやりませんか」的な採用募集でのクロージングでした。
オールアバウトをオンプレミスで支える技術
オールアバウトインフラエンジニア @jp_taku2 さんによるセッション。障害からの改善、自動化といった事例を紹介されていました。
オールアバウトとインフラ構成
メディアサービス。
- 月間 1億5020万PV
- 記事 170,000本
くらいの規模感で運用している。
構成
LBの下にVarnishがプロキシ兼キャッシュサーバとして存在していて、その下にPHPアプリケーションがApacheで動いているという構成でした。DBはMySQL。
Varnish
- cacheサーバ兼プロキシサーバとして活用している
障害事例と改善の話
監視改善の話
- zabbixによるログ監視(初期)はアラートが飛びすぎてメールが埋もれていた
といった形で徐々に監視を改善していった。
自動化事例
Chef導入前
- 構築用のShellScriptがメンテされていない
- 属人化してブラックボックス化したサーバ(つらい)
Chef導入後
- サーバ構築手順をChefのレシピでコード化
- BitBucketでコードを管理してプルリクベースでレビュー
- レビューを通った変更のみが本番/Stagingへデプロイ
属人性が排除され、サーバに直接ログインして設定変更等する必要がなくなった。
Jenkins
特に説明はなし。JenkinsジョブでChefのレシピ適用をしているとのこと。
メルカリにおける、継続的なアプリケーションの改善を支える技術
ご存知 @kazeburo さんによるメルカリのSREについてのお話。個人的にはこのセッションがとても勉強になりました。
http://tech.mercari.com/entry/2015/11/18/153421
主にはこの話をベースにした発表でした。
Mercai & Infrastructure
- 3200万DLのアプリ
- GMV(総取扱額)月間100億以上
- 1日数十万出品以上
1,200,000 reqs/min (API/HTTPS)
をさばいている(スゴイ!)。
Infrastructure
- Jp: さくらインターネット石狩DC
- Us: AWS Oregon Region
mercariさんはJpはAWSではなく、さくらインターネットの物理サーバを使っているそうです。
Jp, Usで同じ構成をとっている。
Software
- nginx
- Apache
- Solr
- ...
- widebullet
- Gaurun
などなど、多くのSoftwareを使っていて、OSSを積極的に活用している。mercari内で作られたOSSも多数あり、 github.com/mercari で公開している。
10+ Deploys/day
Cookpadでも同じような話がありましたが、mercariでも1日に1x回デプロイしているそうです。
PHPのデプロイ
動的言語なので、変更ファイルを配置したらデプロイできるのだが、事はそんなに単純でもない。
依存関係を解決しないといけない
- 参照するモジュールを増やした場合、参照される新規モジュールが既にデプロイされている必要があったり...etc
解決方法案
- Blue-Green Deployment
- Symlink Swapping Deployment
- ref. Capistrano
- キャッシュのコントロールが結構シビア
- Request Pausing Deployment
- デプロイ中のサーバにリクエストが来ないようにする方式
mercariでは Request Pausing Deployment の方式を採用している。
ngx_dynamic_upstream
- @cubicdaiya さん作のNginxモジュール
- up/downコマンド1発で対象サーバへのリクエストをOn/Offできる
- AWSのELBのように反映に時間がかかることなく、一瞬での切り替えが可能なスグレモノ
ansibleのデプロイジョブ
Monitoring
Log monitoring
- Fluentd で一旦ログ中継サーバへ
- 中継ログサーバからBigQuery, Norikraへ転送し、活用している
- NorikraからX分間隔でSlack, Mackerel, DBへ
Agent based monitoring
- Mackerel, NewRelic, Kurado(自作)のデータをpagerduty, slackへ通知
- Mackerel
- 25を超えるpluginやutility commandを書いてる
- Kurado
- グラフを画像で表示できるOSS(kazeburoさん作)
まとめ
- SRE@mercari
- 自分達で問題発見・解決して、サービスの信頼性を向上
- スケールさせるミドルウェアの開発と運用
Ensuring highly available services. Infrastructure and service monitoring
ほぼメモれていないので割愛。スミマセン。。
所感
SREについて
SREについて少しだけ理解できました。
自分たちが日頃行っているサービス改善や監視強化、ログの見える化といった作業もSREの活動であることもわかりましたし、SREによって
- アプリの評価(レビュー)が改善できた
- 特定のAPIが遅いことがわかって改善できた
という成果を生み出すことができるということも再認識できました。
オールアバウト
オールアバウトさんはオンプレでサービスを運用していて、現在クラウドに移行する計画を進めているとのことでした。一休の状況も少し似ている部分があるので、別途情報交換の機会を作ってお互いのサービスにフィードバックできるようにアクションしたいです。
mercari
@kazeburo さんのセッションはさすがでした。
- mackerel, NewRelic, Kurado(自作のグラフダッシュボード)などでサービス・システム的なメトリクスを見える化して改善を促進する
- 10+ deploy/day なデリバリーについての足回りを整える
といった自分が担当している部分に直結する話が多く、めちゃ参考になりました。良い部分は少しでも取り込んで改善につなげたいですね。
Retty
良い勉強会を企画してくださり、ありがとうございました。
Rettyさんのオフィスはステキでした。懇親会でもピザとビールに加えてオシャンな料理が振る舞われたり、ウェルカムスイーツが出てきたり、普段の勉強会と比べて女子力高めでビックリしました。
懇親会でRettyのエンジニアの方々と少しお話をさせていただいたのですが、みなさん楽しそうにサービスの話をしていて、とても好感が持てました。
一休でもレストラン予約の事業をやっているので、「レストランのエンジニアと合同で情報交換とかできたら良いですね〜」なんて話をしていたので、うまいこといけば実現するかもしれません。
おまけ
- ウェルカムスイーツ
- オープンなスペースでの勉強会でした
- 懇親会時にふるまわれた料理(美味しかったです)