[Swift]extensionで実装するPerl風のsplice()

##Swiftの配列操作周りに不満を感じてる貴方へ捧ぐ
perl風に全て実装したバージョンを[[Swift]extensionで実装するPerl風の配列操作](http://qiita.com/worthmine/items/40bfdd8f1f75bfe3463a)にまとめましたのでご参照ください。
本稿は```splice```の実装にやたらと時間をかけたので**そちらのみに特化したTIPSと後日談**です。


##おさらい
swiftの```splice()```は下記のような構文で実行されますが、

```useDefaultSplice.swift
var a :Array = ["Four", "Seven", "Eight" ]
a.splice(["Five", "Six"], atIndex: 1)    // -> ["Four","Five","Six",”Seven”,”Eight"]
```
**配列の挿入しかできません**(返り値は挿入後の配列)

##perl風の```splice```を使いたい
一方、[perl](http://www.loose-info.com/main/memolist/perl/fnc_splice.html)や[javascript](https://msdn.microsoft.com/ja-jp/library/ie/wctc5k7s(v=vs.94).aspx)では**配列の挿入と同時に置換が可能**です。
あのう、これじゃちょっと弱いんじゃないですか？と率直に思いましたのでこれを```extension```で拡張したのが下記になります。

```extensionPerlLikeSplice.swift
extension Array {
    typealias Element = T
    
    mutating func splice(var _ atIndex :Int = 0, _ length :Int = 0, _ newElements: T...) -> Slice<T>? {
        description
        var theIndex = length + atIndex
        var spliced :Slice<T> = []
        if length < 0 {fatalError("minus length was set")}
        if atIndex >= 0 {
            if theIndex > self.endIndex {fatalError("Bad Access")}
            let preArray = atIndex > 0 ? self[self.startIndex..<atIndex] : []
            spliced = length > 0 ? self[atIndex..<theIndex] : []
            let postArray = self[theIndex..<self.endIndex]
            self = preArray + Array(newElements) + postArray
            return length > 0 ? Slice(spliced) : nil
        } else {    // you can use minus atIndex because it is like Perl
            let reversed = self.reverse()
            atIndex = abs(atIndex + 1)
            theIndex = length + atIndex
            if theIndex > self.endIndex {fatalError("Bad Access")}
            let postArray = atIndex > 0 ? reversed[reversed.startIndex..<atIndex].reverse() : []
            spliced = length > 0 ? reversed[atIndex..<theIndex].reverse() : []
            let preArray =   theIndex < reversed.endIndex ? reversed[theIndex..<reversed.endIndex].reverse() :[]
            self = preArray + Array(newElements) + postArray
            return length > 0 ? Slice(spliced) : nil
        }
    }
}
```
##perlのように扱えるメリット
* おおよそのメジャーな配列操作がこの実装だけで表現可能です。（返り値まで含むともう数行必要）

```
a.splice(-1, 0, "push" )    // push()
a.splice(-1, 1 )            // pop()
a.splice(0, 0, "unshift" )  // unshift()
a.splice(0, 1)              // shift()
```
* ご存知の通り、置換にも対応します。

```
a   // -> ["Four", "Five", "Six", "Seven", "Eight"]
a.splice(0, a.count, "other", "Strings", "like", "those")
a   // -> ["other", "Strings", "like", "those"]
```
* また、**マイナスのインデックスを受け付ける場合分けを施しました**ので後ろから2つ目〜3つ目（計2個）を取り出したい、などのニーズに応えることができます。この実装にはなかなか苦労しました。

```
a.splice(-2, 2)             // -> ["Strings", "like"]
```

注）perlで```perl -e 'my @list = (1,2,3,4,5,6,7);print splice(@list,-4, 3); # 456'```
などとして確認したところ、**マイナスの```length```は無視して1に、スライスを取得する方向はインデックスを基準に順方向**でした。これはperlに合わせて実装すべき点ですが、**個人的には現在の仕様(逆方向に取得して```reverce()```して返す)
の方が使い勝手が良いと考えています**ので保留で。
* スライスではなく、**配列として渡せる点も引数が何かと省略可能**で、タイプ数が減ってメリットかな、と考えています。

```
a = ["Four", "Seven", "Eight" ]
a.splice(["Five", "Six"], atIndex: 1)   // using a default splice
a.splice(2, 2, "Six2", "Seven2")        // -> {["Seven", "Eight"]} // using mutating splice
a.splice(0, 1)　                        // -> {[“Four"]}
```
ようは```[]```書きが必要なぶん手間かな？ということです。ないほうがよくないですか？

##デメリット
* 自動補完の時に二つ同名のメソッドがあるから戸惑うくらいでしょうか？
![スクリーンショット 2015-01-23 22.47.57.png](https://qiita-image-store.s3.amazonaws.com/0/10766/00e64942-dfe7-2c57-b019-2ee6d35071eb.png)
* ```mutating```で同名の関数を実装してますが、**コンパイラやインタラクティブインタプリタを惑わせることなく動作**しています。今のところは。
* あとは```atIndex```や```length```に過大な値を代入して参照範囲を外れ、Bad Accessになるケースには対応したほうが良いのかな〜くらいですね。これはデフォルトの```splice```でも起こりうる（```atIndex```に過大な値を入れると）エラーになるので、そこまで親切丁寧にしてあげなくてもいいかな〜。でも興味はあるかな〜くらいですね。ストック数が増えるようであれば考えます！

* 他に何か思い当たる点があれば**コメントなどでビシバシご指摘いただきたく**思います。

##まとめ
* perl風```splice```の実装は```extension```で初心者にも出来る！
* 別名のメソッドにする必要はない。
* perl風の配列操作を全て実装したバージョンを[[Swift]extensionで実装するPerl風の配列操作](http://qiita.com/worthmine/items/40bfdd8f1f75bfe3463a)にまとめましたのでご参照ください。