swift で一次元、二次元配列を取り扱うクラスを作る


# swift で一次元、二次元配列を取り扱うクラスを作る

swiftで一次元、二次元配列を操作するクラスを作成しKMeansで有用性を確認しました
ソースはこちら
配列クラス： https://github.com/ikuo0/swift_number/blob/main/numxd.swift
KMeans実行部： https://github.com/ikuo0/swift_number/blob/main/main.swift

コマンド実行する場合は各々同じディレクトリに設置し

```
swiftc -emit-executable main.swift numxd.swift
```
とします、コマンドを実行すると main という名前のファイルが出来ますのでそれを実行してください

## KMeans実行部
KMeans部分のソースです、７０行ほどでKMeansが実行できていますのでだいぶ簡単に配列操作ができていると思います。

```swift
func Test2D() {
    do {
        let n1dI = Num1D<Int>()
        let n1dD = Num1D<Double>()
        let n2d = Num2D<Double>()
        let tsvData = try TSV.Read("seeds_dataset.txt")
        let data = try TSV.ToDouble(tsvData)
        let xIndexes = n1dI.Arange(0, 7) 
        let xData = data.IndexingT(xIndexes)
        let yData = data.LineT(7)
        
        ////////////////////////////////////////
        // scaling
        ////////////////////////////////////////
        let scaleMean = xData.Mean()
        let scaleVriance = xData.Variance()
        let scaleStddev = scaleVriance.Sqrt()
        let scaledX = (xData - scaleMean) / scaleStddev
        
        ////////////////////////////////////////
        // KMeans
        ////////////////////////////////////////
        let Clusters = 3
        let maxIter = 100
        let tol = 1e-5
        
        // Initialize
        let rowIndexes = n1dI.Arange(0, yData.Count)
        let shuffled = rowIndexes.Shuffle()
        let initIndexes = shuffled.Slice(0, Clusters)
        let initMeans = scaledX.Indexing(initIndexes)
        
        var means = initMeans
        let predict = n1dI.Create(xData.Row)
        
        for iter in 0..<maxIter {
            
            // EStep
            for m in 0..<xData.Row {
                let distances = n1dD.Create(Clusters)
                for cluster in 0..<Clusters {
                    let subtract = scaledX.Line(m) - means.Line(cluster)
                    let power = subtract.Power(2)
                    let total = power.Total()
                    let distance = sqrt(total)
                    distances[cluster] = distance
                }
                predict[m] = distances.ArgMin()
            }
            
            // MStep
            let newMeans = n2d.Create(Clusters, scaledX.Col)
            for cluster in 0..<Clusters {
                let cIndexes = predict.WhereEq(cluster)
                let cMean = scaledX.Indexing(cIndexes)
                newMeans[cluster] = cMean.Mean().Value
            }
            
            // threshold
            let subtract = means - newMeans
            let power = subtract.Power(2)
            let total = power.TotalX()
            let meansDistance = sqrt(total / Double(Clusters))
            means = newMeans
            print("iter=\(iter), means distance=\(meansDistance)")
            if meansDistance < tol {
                break
            }
        }
        
        Dump2D(means)
        Dump1D(predict)
        
    } catch {
        print("exception", error)
    }
}
```

## わざわざクラス化する必要があったかどうか
Ｃ／Ｃ＋＋と異なりメモリ確保の面倒さが無いためわざわざクラス化する事も無かったんじゃないかという気もしましたが、作ってみたら思いの外短く書けているので有りかなと思いました

## ジェネリクス関数の書き込みの仕組み
https://github.com/ikuo0/swift_number/blob/main/numxd.swift#L27
自作の WriteGVariable という関数でジェネリクス関数内の代入処理を行っています
WriteGVariable関数を使用しない場合に型を確定させてからキャストをするコードをその都度書かなければいけません

```swift
            if T.self is Double.Type {
                dst[i] = Double(r) as! T
            } else if T.self is Int.Type {
                dst[i] = Int(r) as! T
            }
            // 冗長になってしまう
```

WriteGVariable関数を使用することで１行で代入処理を書くことができます

```swift
            WriteGVariable(&dst[i], r)
            // １行で済む
```

swiftが厳格な型付け言語であるためにジェネリクス型を使用した場合に冗長になるか冗長回避のため対策が必要になるのは仕方のないことだと思います。
ジェネリクス型に限らず型違い同士の代入処理等については下記URLにて答えが出ているように思います
https://academy.realm.io/jp/posts/richard-fox-casting-swift-2/
https://qiita.com/ObuchiYuki/items/a945efd14d3a05a19f75

答えが出ているにも関わらず真似しないのはoperator演算子のようなソースを隠蔽してしまう仕様が好みではないのと私のswift理解度が低いからです、理解しきれていない物をコピーするのは気が引けますので。

swiftの勉強のためC++で既に実装した物をswiftで実装し直しました、色々と発見があったりしておもしろかったです。
以上です。