65.9K
CodeProject 正在变化。 阅读更多。
Home

Python 3.2 中的多维数组实现,以及与 C# 的相似之处

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.88/5 (7投票s)

2012 年 3 月 8 日

GPL3

2分钟阅读

viewsIcon

23825

一个具有负索引功能的的多维数组类。使用方式类似于 a[-2, -5, 3],适用于任何维度。

下载 multiray.py.zip (适用于 Python 3.x)[^]

下载 multiray.cs.zip (更新于 2011.08.05)[^]

引言

您可以轻松使用 Multiray 类来处理多维数组相关的问题。现在您可以下载 C# 版本 的 multiray,其结构与 Python 版本完全相同。

Using the Code

使用方法很简单
  1. 使用预定义的维度初始化 Multiray 对象
    Python
    a = Multiray(2, 5, 20, 6);
    
    C#
    using multiray;
    MultiRay a = new MultiRay(2, 5, 20, 6);
    
  2. 将您的数据值分配给指定多索引的单元格
    Python
    #a[1, 4, 21, 1] will assert an overflow.
    a[1, 2, 18, 5] = "Any Object";
    
    C#
    //a[1, 4, 21, 1] will raise an exception.
    a[1, 2, 18, 5] = "Any Object";
    
  3. 循环方式类似于锯齿数组。使用 GetLength(d) 获取任何维度在索引 d 处的值。
    Python
    for i in range(a.GetLength(0)):
      for j in range(a.GetLength(1)):
        for k in range(a.GetLength(2)):
          for l in range(a.GetLength(3)):
            #do anything you want with
            #a[i, j, k, l];
            print("a[{1:3}]={0}".format(a[i, j, k, l], a.Position));
    
    C#
    for (int i = 0; i < a.GetLength(0); i++)
        for (int j = 0; j < a.GetLength(1); j++)
            for (int k = 0; k < a.GetLength(2); k++)
                for (int l = 0; l < a.GetLength(3); l++)
                {
                    a[i, j, k, l] = "Hello World";
                    Console.WriteLine("a[{0},{1},{2},{3}] = {4}"
                        , i, j, k, l, a[i, j, k, l]);
                }
    

_EvaluateIndex 根据此原理计算给定索引元组的实际索引

  • 如果 a 是一个 (I x J x K) 的三维数组;那么 a[i][j][k] 是数组 a 中的第 n 个元素。在 Multiray 实现中,n 的值由 _EvaluateIndex() 方法调用计算得出。
  • 对于 D = 3,n 的计算方式如下
    • n = i*J*K + j*K + k;
  • 对于 D = 4 (I x J x K x T :: i, j, k, t),n 的计算方式如下
    • n = i*J*K*T + j*K*T + k*K + t;
  • 我们将第一个键乘以 J、K 和 T;第二个键乘以 K 和 T;第三个键乘以 T;第四个键乘以 1
  • 因此,我们需要将第一个索引乘以 keys[1]*keys[2]*keys[3],第二个索引乘以 keys[2]*keys[3],依此类推...
  • _EvaluateIndex 肯定会这样做
    Python
    def _EvaluateIndex(self, keys, k):
        dLen = len(self.Dimensions);
        #attention!
        #check recursive deadline:
        if k == dLen: return keys[-1];
        t = 1; #accumulator
        #multiply t with the given dimensions k to dLen
        for i in range(k, dLen):
          t *= self.Dimensions[i]
        t *= keys[k - 1];
        return t + self._EvaluateIndex(keys, k=k+1);
    C#
    private int _EvaluateIndex(int[] keyTuple, int k)
    {
        int dLen = this.Dimensions.Length;
        if (k == dLen)
            return keyTuple[keyTuple.Length - 1];
        int t = 1;
        for (int i = k; i < dLen; i++)
        {
            t *= this.Dimensions[i];
        }
        t *= keyTuple[k - 1];
        return t + this._EvaluateIndex(keyTuple, k + 1);
    }
    

如何使用索引器使用 Multiray 对象?

  • 解决方案是实现 __getitem____setitem__
    Python
      
    def __getitem__(self, keyTuple):
        keys = self._CheckKeys(keyTuple);
        self.Position = self._EvaluateIndex(keys, k=1);
        return self._Arr2D[self.Position];
  • self._CheckKeys 进行检查和负索引的转换 (a[2,3,-2]);然后计算 self.Position,并将其作为索引传递给 self._Arr2D 数组。
    Python
      
    def __setitem__(self, keyTuple, item):
        keys = self._CheckKeys(keyTuple);
        self.Position = self._EvaluateIndex(keys, k=1);
        self._Arr2D[self.Position] = item
    C#
    public object this[params int[] keyTuple]
    {
        get
        {
            int[] keys = this._CheckKeys(keyTuple);
            this.Position = this._EvaluateIndex(keys, 1);
            return this._Arr2D[this.Position];
        }
        set
        {
            int[] keys = this._CheckKeys(keyTuple);
            this.Position = this._EvaluateIndex(keys, 1);
            this._Arr2D[this.Position] = value;
        }
    }
    
  • 再次,self._CheckKeys() 进行检查和操作
    Python
    def _CheckKeys(self, keyTuple):
        assert(len(keyTuple) == len(self.Dimensions));
        keyList = list(keyTuple);
        limit = len(keyList);
        for i in range(limit):
          assert(keyList[i] < self.Dimensions[i] \
                 and keyList[i] >= -self.Dimensions[i]);
          #for minus indexes:
          #like a[1,-2]
          if keyList[i] < 0:
            keyList[i] += self.Dimensions[i];
        return keyList;
    
    C#
    private int[] _CheckKeys(int[] keyTuple)
    {
        Debug.Assert(keyTuple.Length == this.Dimensions.Length);
        int limit = keyTuple.Length;
        int[] keyList = new int[limit];
        for (int i = 0; i < limit; i++)
        {
            Debug.Assert(keyList[i] < this.Dimensions[i]
                && keyList[i] >= -this.Dimensions[i]);
            keyList[i] = keyTuple[i];
            //for negative indexes
            //like a[1,-2]
            if (keyList[i] < 0)
                keyList[i] += this.Dimensions[i];
        }
        return keyList;
    }
    

最后的话

  • 我发现用逗号分隔的索引以元组的形式传递。因此,当我使用 a[1,2,3] 时,__getitem__ 接收 (1,2,3) 元组。
  • 因此,目前不需要位置参数。

关注点

我遇到了无效的数学计算问题。在归纳法的帮助下,我最终实现了正确的功能。

历史

之前的版本缺少 C# 的实现。

© . All rights reserved.