MyEnigma

とあるエンジニアのブログです。#Robotics #Programing #C++ #Python #MATLAB #Vim #Mathematics #Book #Movie #Traveling #Mac #iPhone

C++, PythonユーザのためのSwift3基本文法まとめ

Swift実践入門 ── 直感的な文法と安全性を兼ね備えた言語 (WEB+DB PRESS plus)

Swift実践入門 ── 直感的な文法と安全性を兼ね備えた言語 (WEB+DB PRESS plus)

目次

 

はじめに

最近、新しい言語の勉強として

Swiftを勉強しているのですが、

developer.apple.com

自分がC++とPythonユーザなので、

C++, Pythonユーザの観点から

Swiftの基本的な文法をまとめておきます。

 

hello world

Swiftでは標準出力に出力する関数は

pythonと同じprintなので、

下記でhello worldできます。

print("hello world")

文末のセミコロンは通常つけないようです。

(つけてもOK)

コメント

Swiftにおけるコメントは

C++と同じで、一行コメントはスラッシュ2個、

複数行コメントはスラッシュアスタリスクです。

//一行コメント
/*
 複数行
 コメント
 */

ちなみににxcodeの場合、

cmd+/のショートカットでコメントアウトができます。

定数

定数は再代入不可能な変数です。

(C++のconst付き変数のようなもの)

Swiftで定数を設定する場合、

letという予約語を使って宣言します。

定数の宣言には2つ方法があり、

一つ目は定数宣言と同時に値を設定するものです。

let msg = "test"
print(msg)

二つ目は定数の宣言と値の設定を分ける方法です。

この方法の場合、下記のサンプルのように、

定数宣言をする時に、型を指定する必要があります。

(一つ目の方法では、値から自動的に型が設定されていた)

let msg2: String
msg2 = "test2"
print(msg2)

 

変数

再代入可能な変数はvarという予約語で宣言します。

使い方はletの場合と同じです。

var msg = "test"
print(msg)
msg = "test3"
print(msg) //再代入
var msg2: String
msg2 = "test3"
print(msg2)

Swiftの基本的な型

Swiftには下記のような型があります。

  • Int 整数値

  • Float, Double 小数値 (デフォルトはDouble)

  • String 文字列

  • Bool 真偽値

演算

基本的な演算方法は、他の言語とあまり変わりません。

// 数値演算
print(8 / 3) // 2
print(8.0 / 3) // 2.666..
print(8 % 3) // 2 余り

var x = 10
x += 5
print(x) // 15

// 文字列演算
print("hello " + "world")
print("x * 2 is \(x * 2)") //Stringへの埋め込み

// 論理演算
print(true && false) // false
print(true || false) // true
print(!false) // true

一つ注意点はStringの中に変数の値を埋め込む場合は、

スラッシュの後に丸括弧で囲う必要があります。

if文

Swiftのif文はCの条件文の括弧の無い場合と同じような文法です。

let score = 50
let result: String

if score > 80 {
    result = "great"
}
else if score > 60 {
    result = "good"
}
else {
    result = "bad"
}
print(result) // bad

switch文

Swiftのswitch文はC言語のものに似ていますがが、

条件の複数指定や、範囲指定、

または条件文に変数を取り込むことなどが可能です。

またC言語と違い、caseのそれぞれにbreakは不要です。

let num = 16

switch num {
case 0:
    print("0")
case 1, 2, 3: //複数指定
    print("1/2/3")
case 4...6: //範囲指定
    print("4/5/6")
case 7..<9: // 範囲指定(末尾を含まない)
    print("7/8")
case 15:
    break //何もしない場合はbreak
case let n where n > 20: //変数を受け取ることも可能
    print("\(n) is huge!")
default:
    print("default ")
}

 

while文

while文もC言語と同じで、

条件文に括弧が無い形をしています。

var n = 0
while n < 3 {
    print(n)
    n += 1
}

またC言語のdo while文は少しSwiftでは異なり

repeat whileという予約語で実行されます。

n = 9
//実行されない
while n < 3 {
    print(n)
    n += 1
}

//一度実行される
repeat {
    print(n)
    n += 1
} while n < 3

for文

for文はpythonの文法に似ていて、

in という予約語の後に、

範囲演算子(…)で数列を与えることで、

ループを回すことができます。

for i in 0...5 {
    print(i)
}

また、Cやpythonにある、

breakやcontinueは同じようにそのまま使えます。

nilとオプショナル型

CでのNULL, pythonでのNoneのように、

空を意味するものでSwiftではnilという予約語を使います。

 

しかし、swiftの基本的な型では、

nilの代入を禁止しているため、

nilを使いたい変数はオプショナル型という型として

事前に初期化する必要があります。

 

下記のように、通常のStringにnilを代入しようとすると

エラーになりますが、

のこりの3つの方法でオプショナル型の変数として

初期化できるので、nilを代入できるようになります。

s2とs3は同じ意味です。

またオプショナル型として変数を初期化すると、

自動的に初期値はnilで初期化されます。

//let s1: String = nil //error
let s2: Optional<String> = nil
let s3: String? = nil
let s4: String?

 

nilかどうがを判定して、

値を表示するには、

下記のようにします。

オプショナル型の値を取得する場合には

変数名の後に!が必要です。

// nil判定
if s2 != nil {
    print(s2!) // オプショナル型のデータを取り出す時は、!を付ける (unwrap)
}

また下記のようにnilかどうかを判定する方法もあります。

s3がnil出ない場合のみ、valueに値がわたり、

その結果が表示されます。

// s3がnil出ない場合のみ、値を表示(Optional Binding)
if let value = s3 {
    print(value)
}

また、オプショナル型をprint文で表示させる場合は、

下記のような簡略型も使えます。

変数がnilの場合はthis is nilが表示され、

nilでない場合は、値が表示されます。

// s3がnilでなければ値を表示
print(s3 ?? "this is nil!")

 

Array(配列)

Swiftの配列(Array)は、Cに似ていて、

下記のように初期化できます。

// initialize
var scores: [Int] = [50, 40]
var scores2 = [50.0, 40.0] //型推論
var scores3: [String] //空Arrayで初期化
var scores4 = [String]() //上記と同じ

要素へのアクセスや代入も下記のようにできます。

要素数の計算や空集合判定が、

配列のメソッドで可能であることに注意しましょう。

print(scores[0]) // Arrayの要素を表示
scores[1] = 30  //Arrayの要素に代入
print(scores)   //Array全体を表示

print(scores.count) // Arrayの要素数を計算
print(scores.isEmpty) // Arrayが空集合か判定

 

要素の追加に関しては、

pythonと同じく、appendでも可能ですし、

+= の演算子でも末尾に追加は可能です。

var names = [String]()
names.append("Tom")
names.append("Jon")
names += ["Lisa"]

for name in names {
    print(name)
}

tuple

SwiftのタプルはPythonのtupleに近いですが、

若干文法が異なります。

まず、タプルの初期化はPythonと同じ丸括弧です。

異なる型のデータを入れることができます。

var items1 = ("cat", 5) //タプル作成

 

また、Pythonとは異なり、

タプルの要素にアクセスする場合は、

ドット(.)の後にインデックスを指定します。

printでタプルのすべての要素を表示することも可能です。

print(items1.0) //cat
items1.1 = 8
print(items1)

 

また、それぞれの要素に名前をつけることもできます。

下記のコードの初めの例では、

タプルの返り値をそれぞれの名前の定数に割り当てています。

また二つ目の例のように、

使わない要素に関しては、アンダーバーで受け取ることにより、

データを使用しないことを意図できます。

最後の例は、tupleの初期化時に、

各要素に名前を付ける方法です。

この方法で初期化すると、

インデックスではなく、

要素の名前で要素にアクセスできます。

let (product, amount) = items1
print(product)
print(amount)

let (product, _) = items
print(product)

var items = (product: "milk", amount: 5)
print(items.product)

Set

SwiftのSetはPythonのSetと目的は一緒で、

順序を持たない、重複を許さないデータを管理できます。

 

SwiftでSetを使う場合、

下記のように初期化します。

型推論も可能です。

var set: Set<Int> = [3, 5, 8, 8] //Setの初期化
var set2: Set = [3, 5, 8, 8] //型推論
print(set)
var s = Set<Int>() //空のSetを作る

 

Setの中に値があるかのチェックや、

データの追加、削除、

そして要素数の取得は下記のメソッドできます。

print(set.contains(3))//3が入っているか?
set.insert(10)//10を追加
set.remove(5)//5を削除
print(set.count)//要素の数を数える

 

PythonにおけるSetの集合演算も

下記のように実行できます。

let a: Set = [1, 3, 5, 8]
let b: Set = [3, 5, 8, 9]

print(a.union(b)) //和集合
print(a.intersection(b)) //積集合
print(a.subtracting(b)) //差集合

 

Dictionary

SwiftのDictionaryは、

PythonのDictionaryや

C++のmapと同様に、

KeyとValueから成るハッシュマップです。

 

SwiftのDictionaryは下記のように宣言します。

こちらも型推論が可能です。

var scored: Dictionary<String, Int> = ["tom": 200, "ken": 300] //Dictionary 初期化
var scored2 = ["tom": 200, "ken": 300] //型推論
let emptyd = [String: Int]() //空Dictionary

 

下記は、Dictionaryの使い方です。

一つ注意点は、DictionaryのKeyを指定した時には、

そのKeyが無い場合があるので、

オプショナル型で返り値が返ってくることです。

 

scored["tom"] = 500 //要素に代入
print(scored["lina"] ?? "n.a.") //ある要素の表示
scored["lina"] = 100 //新しい要素を追加
print(scored.count) //要素の表示

// Keyとvalueを表示
for (key, value) in scored {
    print("\(key): \(value)")
}

function

関数を作る時は、下記のようにします。

関数の頭にfuncとつけて、

関数名, 返り値の型、引数の順番で書きます。

返り値が無い場合は、返り値の型は不要です。

func test1() {
    print("test1")
}
test1()

func test2() -> String {
    return "test2"
}

print(test2())

引数に関しては、

書きのように引数名名: 型 = “デフォルト引数”

で指定できます。(デフォルト引数は省略可能です)

実際に使うときには、引数名を指定します。

(test4のように省略も可)

またその下の関数の例のように、

複数の引数名を指定することも可能です。

func test3(input: String = "aaa") {//default
    print("\(input)")
}
test3(input: "bbb")


func test4(_ input: String) {
    print("\(input)")
}

test4("bbb")

func test5(str input: String) {
    print("\(input)")
}

test5(str: "bbb")

 

ちなみにSwiftでは、上記の引数の指定方法では、

C++で言う所のconst引数扱いになり、

関数の中で書き換えることはできません。

C++における参照渡しにしたい場合は、

inoutという予約語を引数に設定し、

関数を呼ぶ時に、引数の前に&を付ける必要がります。

結果はC++の参照と同じです。  

func add(x: inout Int) {
    x = x + 10
    print(x)
}

var i = 10
add(x: &i)
print(i)

Class and method

Swiftのクラスは下記のように作ります。

letでクラスのconstプロパティを、

varで通常のプロパティを設定できます。

C++でのコンストラクタは

initという関数(イニシャライザ)で表されます。

また、Pythonと同様、プロパティにはselfでアクセスします。

インスタンスの生成には、型推論を利用することも可能です。

class User {
    let name: String
    var age: Int
    init() {
        self.name = "Tom"
        self.age = 23
    }
}

let user: User = User()
let user2 = User() // 型推論
print(user.name)
print(user.age)
user.age = 26
print(user.age)

 

また下記のように、

イニシャライザをオーバライドしたり、

引数を与えたりすることができます。

 

クラスのメソッドは

クラスの中にfuncで指定すればOKです。

プロパティのselfは省略可能です。

class User {
    let name: String
    var age: Int
    init() {
        self.name = "Tom"
        self.age = 23
    }
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    func sayHi(msg: String) {
        print("\(msg) \(name)")
    }
}


let user: User = User()
let user2 = User() // 型推論
print(user.name)
print(user.age)
user.age = 26
print(user.age)


let tom = User(name: "tom", age: 23)
print(tom.name)
print(tom.age)
tom.sayHi("hi")

let bob = User()
print(bob.name)
print(bob.age)
bob.sayHi("hello")

一つswiftのクラスの注意点として、

クラスのオブジェクトを、

他のオブジェクトに=でコピーすると、

値ではなくポインタがコピーされる参照渡しになってしまいます。

値渡しのオブジェクトを作りたい場合は、

後述のstructを使う方法があります。

 

Computed Property

SwiftのComputed Propertyは、

C++にもPythonには無い文法だと思います。

これはいわゆるプロパティのゲッターとセッターを

簡単に記述する方法です。

 

下記のように、

それぞれのプロパティに対して、

そのプロパティにアクセスした時と、

そのプロパティに代入した時に呼ばれる処理を

記述することができます。

ちなみに、getterのみで良い場合は、

getやsetという予約語も不要な省略形を記述できます。

 

class User2 {
    let name: String
    var score: Int
    //computed property
    var level: Int {
        get {
            return Int(self.score / 10)
        }
        set {
            if newValue >= 0 {
                score = newValue * 10
            }
        }
    }
    // getterのみの場合は省略形が使える
    var level2: Int {
        return Int(score / 20)
    }
    init(name: String, score: Int) {
        self.name = name
        self.score = score
    }
}

let tom2 = User2(name: "tom", score: 23)
print(tom2.level)
tom2.level = 5
print(tom2.score)
tom2.level = -3
print(tom2.score)
print(tom2.level2)

 

Observer property

Observe propertyもC++やPythonには無い

文法だと思います。

これはプロパティの監視をするための方法で、

あるプロパティが変更される時に、

変更される前と、変更された後に

自動実行される処理を簡単に記述できるようにしたものです。

 

Observer Propertyは各プロパティに

willSetとdidSetという予約語で指定され、

willSetはプロパティ変更の前、

didSetはプロパティ変更後に実行されます。

class User3 {
    let name: String
    var score: Int {
        willSet {// 変更前
            print(Before change: "\(score) -> \(newValue)")
        }
        didSet {// 変更後
            // after change
            print("After changed: \(score - oldValue)")
        }
    }
    init(_ name: String, _ score: Int) {
        self.name = name
        self.score = score
    }
}

let tom3 = User3("tom", 23)
tom3.score = 33
tom3.score = 10

 

Inheritance

Swiftでの継承はclassの名前の後に、

コロンで親クラスを指定することで実現可能です。

 

親クラスのメソッドをオーバライドする時は、

funcの前にoverrideと書き、

逆にオーバライドを許可しない場合は、

親クラスのメソッドの前にfinalをつけます。

class User4 {
    let name: String
    var score: Int
    init(name: String, score: Int) {
        self.name = name
        self.score = score
    }
    func sayHi() {
        print("hi \(name)")
    }
    final func sayHiHi() {//オーバライド禁止する時はfinalを使う
        print("hiHi \(name)")
    }
}

// inheritance
class AdminUser: User4 {
    func sayHello() {
        print("hello \(name)")
    }
    override func sayHi() { //親クラスのメソッドをオーバライド
        print("[admin] hi \(name)")
    }
}

let tom4 = User4(name:"tom", score: 23)
let bob3 = AdminUser(name:"bob", score: 33)
print(bob3.name)
print(bob3.score)
bob3.sayHi()
bob3.sayHello()

 

Static function and Static property

インスタンスではなく、

クラスそのものに所属するメソッドやプロパティは、

C++と同様にstaticで設定することができます。

下記のように

staticやfuncやvarの前に置き、

static propertyにアクセスする場合は、

クラス名.static変数名とします。

class User5 {
    let name: String
    var score: Int
    static var count = 0 //static property
    init(_ name: String, _ score: Int) {
        self.name = name
        self.score = score
        User5.count += 1
    }
    static func getInfo() { //static method
        print("number of instance: \(count) ")
    }
}


User5.getInfo()
let tom5 = User5("tom", 23)
User5.getInfo()

 

cast

クラスの型変換には、

swiftではasという予約語が使われます。

下記のように、継承された複数のクラスを

リストとしてまとめると、

swiftでは自動的に親クラスとしてまとめられます。

また、まとめられたリストの中で、

ある子クラスのものだけを取り出したい場合は、

下記のようにforループの中のif文の中で、

as?を使うと、AdminUser6のインスタンスのみを

抽出することができます。  

class User6 {
    let name: String
    init(_ name: String) {
        self.name = name
    }
}
class AdminUser6: User6 {}

let tom6 = User6("tom")
let bob6 = AdminUser6("bob")

let users = [tom6, bob6]//親クラスのUser6としてまとめられる。

for user in users {
    if let u = user as? AdminUser6 {//AdminUserのみ
        print(u.name)
    }
}

Protocol

SwiftにおけるProtocolは、

C++で言う所の純粋仮想関数のようなもので、

継承する先のクラスに、

必ず実装する必要のあるメソッドやプロパティを

指定することができます。

 

下記のサンプルコードのように、protocolの後に、

プロトコルの名前を指定し、その後メソッドやプロパティを指定します。

プロパティに関しては、getとsetを指定することで、

読み書きの可能不可能を指定することが可能です。

protocol Printable {
    var type: String { get }//Readのみ可能なプロパティ
    var count: Int { get set }//ReadとWriteが可能なプロパティ
    func printout()
}

class User7: Printable {
    let type = "Laser"
    var count = 0
    let name: String
    init(_ name: String) {
        self.name = name
    }
    func printout() {
        count += 1
        print("\(type): \(count)")
    }
}

let tom7 = User7("tom")
tom7.printout()

extension

extensionは、既存の型・クラスに

追加でメソッドなどを追加する方法です。

下記のように、String型に

lengthというメソッドを追加することができます。

extension String {
    var length: Int {
        return self.characters.count
    }
}

let msg0 = "hello"
print(msg0.characters.count)
print(msg0.length)

 

struct

クラスと似たデータ構造にstructがあります。

structはプロパティを持てたり、

メソッドを持ったり、ほぼクラスと同じ動きをしますが、

下記の2つが大きくことなります。

  • 値型のデータで、=で値渡しが可能

  • メソッドの中でプロパティを変更するには、mutatingが必要

  • 継承ができない。

あまりデータが頻繁に変わらないデータに使うようです。

 

struct User {
    var name: String
    init(_ name: String) {
        self.name = name
    }
    mutating func changeName() {//メソッドの中でプロパティを変更する
        self.name = name.uppercased()
    }
}

var original = User("tom")
var copy = original // copy: originalの値
original.name = "bob"
print(original.name) // bob
print(copy.name) // tom

列挙型

C++で言う所のenumですが、

swiftのenumは,

それぞれの要素にcaseが必要です。

また、enumの要素に値をもたせることもできますが、

その値を利用するにはrawValueというプロパティに

アクセスする必要があります。

enum Direction {
    case right
    case left
}

var dir: Direction
dir = Direction.right

switch (dir) {
case .right:
    print("right")
case .left:
    print("left")
}

enum Direction2: Int {
    case right = 1
    case left = -1
}

print(Direction2.right.rawValue)

Generics

C++で言うところのテンプレートである、

Genericsは下記のように、

<>の中に汎用型名を指定して関数を作ればOKです。

様々な型に対応した関数を一つのコードで実現できます。

// Generics
func getThree<T>(x: T) {
    print(x)
    print(x)
}

getThree(x: 5)
getThree(x: "hello")
getThree(x: 2.3)

Subscript

SubscriptはC++やPythonには無い機能だと思いますが、

自分で定義したクラスのインスタンスに対して、

ArrayやDictionaryのように、

インデックスやキーでアクセスできるようにするものです。

 

下記のコードのように、

自作のクラス内のインスタンスのプロパティに対して、

インデックスやキーでアクセスした時の

振る舞いを簡単に記述できます。

class Team {
    var members = ["ichiro", "matsui", "yuu"]
    subscript(index: Int) -> String {
        get {
            return members[index]
        }
        set {
            members.insert(newValue, at: index)
        }
    }
}

var team1 = Team()
print(team1[1]) // matsui
team1[3] = "tanaka"
print(team1[3]) // tanaka

 

qiita.com

 

guard

guradはswirt特有のearly returnのための方法です。

下記のコードのように、

入力値がある条件を満たしているかを、

確認してから処理することは多いですが、

それをif文でやるとreturnを忘れたりするので、

明示的にguardという予約語を使うと、

コードの可読性が上がり、

また, return文を忘れるとエラーにしてくれます。

func say(_ msg: String?) {
    guard let s = msg else {
        print("value not set!")
        return
    }
    print(s)
}

say(nil)
say("hello")

Exception

swiftにおける例外は、

機能としてはC++やPythonのものと

あまり変わりませんが若干文法が異なります。

 

まず下記のように、

例外の種類をenumで宣言して、

そこからエラーを投げる所で、

エラーをraiseします。

またエラーをraiseする関数には、

関数名の後にthrowsを入れます。

あとは、doとcatchで例外を補足できます。

enum LoginError: Error {
    case emptyName
    case shortName
}

class User0 {
    let name: String
    init(_ name: String) {
        self.name = name
    }
    func login() throws {
        guard name != "" else {
            throw LoginError.emptyName
        }
        guard name.characters.count > 5 else {
            throw LoginError.shortName
        }
        print("login success")
    }
}

let tom0 = User0("")

do {
    try tom0.login()
} catch LoginError.emptyName {
    print("please enter your name")
} catch LoginError.shortName {
    print("too short")
}

 

参考資料

myenigma.hatenablog.com

myenigma.hatenablog.com

myenigma.hatenablog.com

myenigma.hatenablog.com

Swift実践入門 ── 直感的な文法と安全性を兼ね備えた言語 (WEB+DB PRESS plus)

Swift実践入門 ── 直感的な文法と安全性を兼ね備えた言語 (WEB+DB PRESS plus)