LoginSignup
6
7

More than 5 years have passed since last update.

Swiftのサブスクリプトで割と汎用的な多次元配列を作る

Last updated at Posted at 2015-10-08

Swiftではこんな感じにサブスクリプトを使うことで多次元配列っぽい構造体を作れる。
これをさらに汎用的にして多次元配列の機能を再現させてみたい。
(それなら多次元配列を使えばいいじゃないかという声が聞こえてきそう。)

board.swift
struct Board{

    var size:[Int]
    var data: [Float]

    init(x:Int ,y:Int){
        size=[Int](count: 2, repeatedValue: 0)
        size[0]=x
        size[1]=y
        data = [Float](count: x * y, repeatedValue: 0.0)
    }

    func indexIsValid(x:Int, y:Int) -> Bool{
        return x>=0 && x<size[0] && y>=0 && y<size[1]
    }

    subscript(x:Int,y:Int) -> Float{
        get{
            assert(indexIsValid(x,y: y),"index out of range")
            return data[x + y * size[0]]
        }
        set{
            assert(indexIsValid(x,y:y),"index out of range")
            data[x + y * size[0]] = newValue
        }
    }
}

var b = Board(x: 3,y: 3)
b[1,1] = 1

上の2次元配列っぽいものを3次元配列に拡張してみる。
ジェネリクスで型指定も可能にしてみた。
これなら3次元グリッドを表現する場合に実際に使えそうだ。
あと一般化するための規則性も見えてきた。

grid.swift
struct Grid<T>{

    var size:[Int]
    var data: [T?]

    init(x:Int ,y:Int, z:Int){
        size=[Int](count: 3, repeatedValue: 0)
        size[0]=x
        size[1]=y
        size[2]=z
        data = [T?](count: x * y * z, repeatedValue: nil)
    }

    func indexIsValid(x:Int, y:Int, z:Int) -> Bool{
        return x>=0 && x<size[0] &&
               y>=0 && y<size[1] &&
               z>=0 && z<size[2]
    }

    subscript(x:Int,y:Int,z:Int) -> T{
        get{
            assert(indexIsValid(x,y:y,z:z),"index out of range")
            return data[x + y * size[0] + z * size[0] * size[1]]!
        }
        set{
            assert(indexIsValid(x,y:y,z:z),"index out of range")
            data[x + y * size[0] + z * size[0] * size[1]] = newValue
        }
    }
}

var g = Grid<Float>(x: 10, y: 10, z: 10)
g[1,1,1] = 1

上を元にしてさらに汎用的にして多次元配列っぽくしてみる。
これならIntの範囲の大きさで任意の次元の配列も扱うことができるはずだ。
いつ役に立つか分からないけど、いつか役立つことを信じてみます。

MultiArray.swift
class MultiArray<T>{

    var size:[Int]
    var data: [T?]

    init(size:Int...){
        self.size = size
        let multiply = { (a:Int, b:Int) -> Int in a * b}
        data = [T?](count: size.reduce(1,combine: multiply), repeatedValue: nil)
    }

    func indexIsValid(indeces:[Int]) -> Bool{
        var v:Bool = true
        for (i,index) in indeces.enumerate() {
           v = v && index>=0 && index<size[i]
        }
        return v
    }

    func createIndex(n:[Int]) -> Int{
        var index:Int = 0
        for (i,v) in n.enumerate(){
            var s:Int = 1
            for j in 0..<i{
               s *= size[j]
            }
           index += v * s
        }
        return index
    }

    subscript(n:Int...) -> T{
        get{
            assert(indexIsValid(n),"index out of range")
            return data[createIndex(n)]!
        }
        set{
            assert(indexIsValid(n),"index out of range")
            data[createIndex(n)] = newValue
        }
    }
}

var mf = MultiArray<Float>(size:10,10,10)
mf[1,1,1] = 1
var ms = MultiArray<String>(size:1,2,3,4)
ms[1,1,1,1]="a"

※createIndexの所はもう少し関数型っぽく書けそう。

6
7
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
7