【问题标题】:C#: Setting all values in an array with arbitrary dimensionsC#:在任意维度的数组中设置所有值
【发布时间】:2011-01-11 15:57:53
【问题描述】:

我正在寻找一种将多维数组中的每个值设置为单个值的方法。问题是在编译时维数是未知的——它可能是一维的,也可能是四维的。由于foreach 不允许您设置值,我可以实现此目标的一种方法是什么?非常感谢。

【问题讨论】:

  • 锯齿状数组还是多维数组?

标签: c# c#-4.0


【解决方案1】:

虽然这个问题表面上看起来很简单,但实际上比看起来要复杂。 但是,通过认识到访问多维(甚至锯齿状)数组中的每个位置是一个笛卡尔对数组索引集的乘积操作 - 我们可以简化解决方案......并最终编写一个更优雅的解决方案。

我们将利用 Eric Lippert's LINQ Cartesian Product 实现来完成繁重的工作。如果您愿意,可以在他的博客上阅读有关其工作原理的更多信息。

虽然此实现特定于访问多维数组的单元格 - 应该相对容易看出如何扩展它以访问锯齿状数组。

public static class EnumerableExt
{
    // Eric Lippert's Cartesian Product operator...
    public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(
          this IEnumerable<IEnumerable<T>> sequences)
    {
        IEnumerable<IEnumerable<T>> emptyProduct = 
                   new[] { Enumerable.Empty<T>() };
        return sequences.Aggregate(
          emptyProduct,
          (accumulator, sequence) =>
            from accseq in accumulator
            from item in sequence
            select accseq.Concat(new[] { item }));
    }
}

class MDFill
{
    public static void Main()
    {
        // create an arbitrary multidimensional array
        Array mdArray = new int[2,3,4,5];

        // create a sequences of sequences representing all of the possible
        // index positions of each dimension within the MD-array
        var dimensionBounds = 
            Enumerable.Range(0, mdArray.Rank)
               .Select(x => Enumerable.Range(mdArray.GetLowerBound(x),
                      mdArray.GetUpperBound(x) - mdArray.GetLowerBound(x)+1));

        // use the cartesian product to visit every permutation of indexes
        // in the MD array and set each position to a specific value...
        int someValue = 100;
        foreach( var indexSet in dimensionBounds.CartesianProduct() )
        {
            mdArray.SetValue( someValue, indexSet.ToArray() );
        }
    }
}

现在可以轻松将此代码分解为可重用的方法,该方法可用于锯齿状或多维数组...或任何可被视为矩形数组的数据结构。

【讨论】:

  • 哇,读起来真的很有趣。非常感谢您的链接和解决方案!
【解决方案2】:

Array.Length 会告诉你数组被声明要存储的元素数量,因此可以按如下方式遍历数组数组(无论是矩形还是锯齿状):

for(var i=0; i<myMDArray.Length; i++)
   for(var j=0; j < myMDArray[i].Length; i++)
      DoSomethingTo(myMDArray[i][j]);

如果数组是矩形的(所有子数组的长度相同),可以获取第一个数组的Length并存入一个变量;它会稍微提高性能。

当维数未知时,可以递归:

public void SetValueOn(Array theArray, Object theValue)
{
   if(theArray[0] is Array) //we haven't hit bottom yet
      for(int a=0;a<theArray.Length;a++)
         SetValueOn(theArray[a], theValue);
   else if(theValue.GetType().IsAssignableFrom(theArray[0].GetType()))
      for(int i=0;i<theArray.Length;i++)
         theArray[i] = theValue;
   else throw new ArgumentException(
         "theValue is not assignable to elements of theArray");
}

【讨论】:

  • @JustOnePixel - 注意这是锯齿状数组myArray[][] 解决方案
  • 真;我没有注意到这是未知的维度数,而不是长度。我会编辑
【解决方案3】:

我认为没有直接的方法可以做到这一点,因此您需要使用Rank 属性来获取维度的数量和SetValue 方法(该方法将每个维度的索引数组作为参数)。

一些帮助您入门的代码 sn-p(用于标准多维数组):

bool IncrementLastIndex(Array ar, int[] indices) {
  // Return 'false' if indices[i] == ar.GetLength(i) for all 'i'
  // otherwise, find the last index such that it can be incremented,
  // increment it and set all larger indices to 0
  for(int dim = indices.Length - 1; dim >= 0; dim--) { 
    if (indices[dim] < ar.GetLength(dim)) {
      indices[dim]++;
      for(int i = dim + 1; i < indices.Length; i++) indices[i] = 0;
      return;
    }
  }
}

void ClearArray(Array ar, object val) {
  var indices = new int[ar.Rank];
  do {
    // Set the value in the array to specified value
    ar.SetValue(val, indices);
  } while(IncrementLastIndex(ar, indices));
}

【讨论】:

  • @JustOnePixel - 注意这是多维数组myArray[,] 解决方案
【解决方案4】:

Array.Clear 方法可以让您清除(设置为默认值)多维数组(即int[,])中的所有元素。所以如果只想清空数组,可以写Array.Clear(myArray, 0, myArray.Length);

似乎没有任何方法可以将数组的所有元素设置为任意值。

请注意,如果您将其用于锯齿状数组(即int[][]),您最终会得到一个数组的空引用数组。也就是说,如果你写:

int[][] myArray;
// do some stuff to initialize the array
// now clear the array
Array.Clear(myArray, 0, myArray.Length);

那么myArray[0] 将是null

【讨论】:

    【解决方案5】:

    您是说要遍历数组的每个元素和(如果可用)每个维度并设置每个值吗?

    如果是这种情况,您将创建一个迭代维度并设置值的递归函数。查看 MSDN 上的Array.Rank 属性和 MSDN 上的Array.GetUpperBound 函数。

    最后,我确信通用 List&lt;T&gt; 有某种方法可以做到这一点。

    【讨论】:

    • 实际上,List&lt;T&gt; 似乎没有这样的东西。 List.Clear() 会将每个项目设置为其默认值,但不会设置为任意值。
    猜你喜欢
    • 2013-11-11
    • 2022-01-16
    • 2012-04-12
    • 1970-01-01
    • 1970-01-01
    • 2017-12-24
    • 1970-01-01
    • 2020-09-23
    • 1970-01-01
    相关资源
    最近更新 更多