目的
C#でpythonのリストのようなものを扱いたい
ここでいうpythonのリストとは以下の2点ができること。
マイナスにインデックスが入っている場合は後ろからの順番に返すこと
list = [0,1,2,3]のように数値が入っているとき
list[-1]=>3
list[-2]=>2スライスが簡単にできる
list = [0,1,2,3]のように数値が入っているとき
list[1:3]=>[1,2]
list[1:]=>[1,2,3]
list[:-1]=>[0,1,2]
のように返す感じ
ソース
githubに置きました
https://github.com/kskhsn/PyList
コメントとかなくすいません。
PyListというクラスがそうです。
基本的にはIListを継承して
内部にListを持たせて
インデクサ周辺をいじって欲しい機能を付けました。
- マイナスにインデックスが入っている場合は後ろからの順番に返すことのコア部分
//インデクサの部分
//インデックスを↓の関数で計算してマイナスを代入可能にしている
public T this[int index]
{
get { return this.list[this.CalcIndex(index)]; }
set { this.list[this.CalcIndex(index)] = value; }
}
//インデックスを計算する関数
//マイナスの数値の場合は後ろから何番目かを示すインデックスに変換する
private int CalcIndex(int index)
{
if (-1 < index && index < this.list.Count)
return index;
else if (index < 0 && -this.list.Count <= index)
return this.list.Count + index;
else
throw new ArgumentOutOfRangeException();
}
- スライスが簡単にできるのコア部分 こちらもインデクサをいじりました。 インデクサに複数の引数を渡せるとは知らなかった。
//インデクサの部分
//aaa[start,end]と書けるようにaaa[start:end]の代わり
public PyList<T> this[int start, int end]
{
get
{
start = this.CalcIndexSafe(start);
end = this.CalcIndexSafe(end);
if (start < end)
return new PyList<T>(this.list.Skip(start).Take(end - start + 1));
else
return PyList<T>.Empty();
}
set{省略下記参照}
}
//クラスの外にenumを定義
//aaa[:1]の表現の代替としてaaa[ListIndex.Empty,1]と書くようのもの
public enum ListIndex { Empty }
//aaa[:1]の表現の代替としてaaa[ListIndex.Empty,1]と書くようのもの
public PyList<T> this[ListIndex start, int end]
{
get { return new PyList<T>(this.list.Take(this.CalcIndexSafe(end))); }
set { this[0, end] = value; }
}
//aaa[1:]の表現の代替としてaaa[1,ListIndex.Empty]と書くようのもの
public PyList<T> this[int start, ListIndex end]
{
get { return new PyList<T>(this.list.Skip(this.CalcIndexSafe(start))); }
set { this[start, this.list.Count] = value; }
}
//インデックスを計算する関数
//↑のCalcIndexSafeと違い例外を出さないようにしている
//理由はpythonだとそうだったから
private int CalcIndexSafe(int index)
{
if (index < -this.list.Count)
return 0;
else if (index < 0)
return this.list.Count + index;
else if (index < this.list.Count)
return index;
else
return this.list.Count;
}
一緒にある。
PyArrayは中途半端です。
そんなものあげるなって話ですがあげてしまっています。
結果
こんな感じで値を返すようにしました。
表現は変わったけど大体OKなはず。
pythonだとa[1:3]みたいに各部分はa[1,3]と
「:」から「,」で区切りに変更しました
通常の配列やリストにする場合は、ToArray,ToListで変えます。
var pyList = new PyList<int>();
pyList.Add(100);
pyList.Add(101);
pyList.Add(102);
pyList.Add(103);
pyList.Add(104);
var pyList1 = pyList[-1];//=>104
var pyList2 = pyList[0];//=>100
var pyList3 = pyList[1];//=>101
var pyList4 = pyList[1,3];//=>[101,102,103]
var pyList5 = pyList[2,ListIndex.Empty];//=>[102,103,104]
var pyList6 = pyList[ListIndex.Empty, 1];//=>[100]
おまけ1
aaa[2,4]=3のように書いたとき
aaa[2]=aaa[3]=3と複数範囲に代入可能にしてみた
numpyの代入のイメージ
implicitで暗黙的な変換を使うことと
インデクサのsetの部分で実現
//使い方
pyList[2, 4] = 1;//[*,1,1,*,*...]
//この定義によりで無理やり代入を実装
static public implicit operator PyList<T>(T value)
{
return new PyList<T>() { value };
}
//インデクサの部分
//↑での暗黙的変換でサイズ1のリストを作成してその最初の値を代入
public PyList<T> this[int start, int end]
{
get{省略上記参照}
set
{
start = this.CalcIndexSafe(start);
end = this.CalcIndexSafe(end);
for (int i = start; i < end; i++)
this.list[i] = value[0];
}
}
おまけ2
pythonだと[1]*5とすると[1,1,1,1,1]となるので
そんなこともオペレータの実装で追加
//使い方
new PyList<int>(new int[] { 10, 99 }) * 2;//[10,99,10,99]
new PyList<int>() { 1 } * 4;//[1,1,1,1]
//*したときに複製をするようにした
static public PyList<T> operator *(PyList<T> list, int rate)
{
var temp = new PyList<T>(list);
if (temp.Count != 0)
{
for (int i = 0; i < rate-1; i++)
foreach (var item in list)
temp.Add(item);
}
else
{
for (int i = 0; i < rate; i++)
{
temp.Add(default(T));
}
}
return temp;
}
感想
自分としては初めてオペレータの*の実装と
複数のインデクサの実装をして勉強になった。
でもこれ使うかな?