読者です 読者をやめる 読者になる 読者になる

Pebble's Diary

プログラマーの作業メモ

swift3 IntegerArithmaticプロトコル

IntegerArithmaticプロトコルを実装するには、以下8つの関数を定義する必要がある。 最初の2つはComparableである。

static func ==(lhs: M, rhs: M) -> Bool
static func <(lhs: M, rhs: M) -> Bool
static func addWithOverflow(_ lhs: M, _ rhs: M) -> (M, overflow: Bool) 
static func subtractWithOverflow(_ lhs: M, _ rhs: M) -> (M, overflow: Bool) 
static func multiplyWithOverflow(_ lhs: M, _ rhs: M) -> (M, overflow: Bool) 
static func divideWithOverflow(_ lhs: M, _ rhs: M) -> (M, overflow: Bool) 
static func remainderWithOverflow(_ lhs: M, _ rhs: M) -> (M, overflow: Bool) 
func toIntMax() -> IntMax

最後の一つはswiftの最大Int(=UInt64)へ変換するものである。 このプロトコルを実装すると以下の5つの演算ができるようになる。

public static func +(lhs: Self, rhs: Self) -> Self
public static func -(lhs: Self, rhs: Self) -> Self
public static func *(lhs: Self, rhs: Self) -> Self
public static func /(lhs: Self, rhs: Self) -> Self
public static func %(lhs: Self, rhs: Self) -> Self

このプロトコルを用いると、通常とは異なる演算規則を持つ整数を作ることができる。 複素整数や桁数が無制限の整数などを自作することができる。

swift3 Comparableプロトロル

Comparableプロトコルを作るには、
static func <(lhs: Even, rhs: Even) -> Bool
static func ==(lhs: Even, rhs: Even) -> Bool
の2つを実装すればよい。

struct M : Comparable 
{
    var val:Int
    
    static func <(lhs: M, rhs: M) -> Bool 
    {
        return lhs.val < rhs.val
    }
    
    static func ==(lhs: M, rhs: M) -> Bool
    {
        return lhs.val == rhs.val
    }
}

let r1 = M(val:1)
let p1 = M(val:2)
assert(r1 < p1)

let r2 = M(val:1)
assert(r1 == r2)

swift3 Collectionプロトコル

Collectionプロトコルを作るには、
startIndexプロパティ,endIndexプロパティ,subscript,index(after:) の4つを実装する必要がある。
Collection - Swift Standard Library | Apple Developer Documentation

ここでは奇数を返すコレクションOddを作ってみた。

struct Odd : Collection {
    var startIndex: Int
    var endIndex: Int
    subscript(i: Int) -> Int {
        return 1 + 2 * i  
    }
    func index(after: Int) -> Int {
        return after + 1
    }
}

let odd = Odd(startIndex:1, endIndex:4)
for i in odd {
    print("\(i)")
}
3
5
7

swift3 Sequenceプロトコル

Sequenceプロトコルの例 ここでは1以上の奇数を順番に返すものを作ってみた。ちなみにIntの範囲を超えるとクラッシュするが気にしないでもらいたい。(^ ^)

struct Odd : Sequence, IteratorProtocol {
    typealias Element = Int
    private var v:Element = 1
    public mutating func next() -> Element? {
        defer { v += 2 }
        return v
    }
}

var odd = Odd()
for _ in 0..<5 {
    print("\(odd.next())")
}

結果

Optional(1)
Optional(3)
Optional(5)
Optional(7)
Optional(9)

swift preconditionとassertの違い

swiftにはassertさせる関数がいっぱいあって困ります。

1) assert()

let ary = [1, 2, 3]
let i = 3
assert(i < ary.count)
print("\(ary[i])")

assertion failed: file /develop/aaa/aaa/ViewController.swift, line 18

2) precondition()

let ary = [1, 2, 3]
let i = 3
precondition(i < ary.count)
print("\(ary[i])")

precondition failed: file /develop/aaa/aaa/ViewController.swift, line 18

3) fatalError()

let ary = [1, 2, 3]
let i = 3
if i < ary.count {
} else {
    fatalError()
}
print("\(ary[i])")

fatal error: file /develop/aaa/aaa/ViewController.swift, line 19

1) -> 2) -> 3) の順番で致命的なエラーになっていくようです。 fatalErrorはコンパイラがその部分での戻り値の存在チェックをしなくなるので、仮の戻り値を書かずに済みます。

正直な感想として、preconditionはいらねえと思います。

swfit 負数の除法

swift,C,Javaruby, python,perlでは、 負数を割り算や剰余に使った場合、結果が異なる。

python 剰余

7 % 3 -> 1
7 % -3 -> -2
-7 % 3 -> 2
-7 & -3 -> -1

swift 剰余

7 % 3    -> 1
7 % -3   -> 1
-7 % 3   -> -1
-7 % -3  -> -1

python

7 / 2 -> 3
7 / -2 -> -4
-7 / 2 -> -4
-7 / -2 -> 3

swift 商

7 / 2 -> 3
7 / -2 -> -3 
-7 / 2  -> -3
-7 / -2  -> 3

swiftの除法をユークリッド除法と呼び、 剰余を割られる値のsignに合わせる。

pythonの除法を絶対的最小剰余と呼び、割り切れない場合はマイナス無限大方向に丸める。

除法 - Wikipedia

ios10.3 ファイルパス問題

環境 ios10.3.1
Xcode 8.3.2

        guard let path:String = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first else {
            return
        }
        let filename0 = path + "/" + "a.txt"
        if let fp0 = fopen(filename0, "wb") {
            print("fp0 \(fp0)")
        } else {
            print("fp0 is nil")
        }
        
        let filename1 = path + "/" + "ぱ.txt"
        if let fp1 = fopen(filename1, "wb") {
            print("fp1 \(fp1)")
        } else {
            print("fp1 is nil")
        }
        
        let fm = FileManager.default
        try? fm.createDirectory(atPath: path + "/ぱ", withIntermediateDirectories: true, attributes: nil)
        
        let filename2 = path + "/ぱ/" + "ぴ.txt"
        if let fp2 = fopen(filename2, "wb") {
            print("fp2 \(fp2)")
        } else {
            print("fp2 is nil")
        }
        
        let filename3 = path + "/ぱ/" + "b.txt"
        if let fp3 = fopen(filename3, "wb") {
            print("fp3 \(fp3)")
        } else {
            print("fp3 is nil")
        }
        
        let filename4 = (filename3 as NSString).fileSystemRepresentation
        if let fp4 = fopen(filename4, "wb") {
            print("fp4 \(fp4)")
        } else {
            print("fp4 is nil")
        }
[結果]
fp0 0x0000000100d01c78
fp1 0x0000000100d11808
fp2 is nil
fp3 is nil
fp4 0x0000000100c16da8

fopenでのファイル作成では、濁点、半濁点名を付けたフォルダ名をパスに含むとfopenに失敗するようです。
fopenに渡す場合はNSStringのfileSystemRepresentationで変換すればよいようです。

参考:

https://developer.apple.com/library/content/documentation/FileManagement/Conceptual/APFS_Guide/FAQ/FAQ.html#//apple_ref/doc/uid/TP40016999-CH6-DontLinkElementID_3:Apple APFS Guide

iOS10.3のファイルパス問題について