将 c# `Index` 和 `Range` 与锯齿状数组一起使用
Use c# `Index` and `Range` with jagged arrays
我正在尝试使用 Index
和 Range
对象来分割锯齿状数组。但在下面的示例中,我无法提取列,只能提取行。
var A = new float[3][];
A[0] = new float[] { 1.1f, 1.2f, 1.3f };
A[1] = new float[] { 2.1f, 2.2f, 2.3f };
A[2] = new float[] { 3.1f, 3.2f, 3.3f };
// This works as intended to extract rows
var A_row1 = A[0][..]; // { 1.1, 1.2, 1.3 }
var A_row3 = A[^1][..]; // { 3.1, 3.2, 3.3 }
var A_row23 = A[1..^0][..]; // { { 2.1, 2.2, 2.3}, { 3.1, 3.2, 3.3 } }
// This does not work as intended to extract columns
var A_col1 = A[..][0]; // { 1.1, 1.2, 1.3 } ** WRONG
var A_col3 = A[..][^1]; // { 3.1, 3.2, 3.3 } ** WRONG
var A_col23 = A[..][1..^0]; // { { 2.1, 2.2, 2.3}, { 3.1, 3.2, 3.3 } } ** WRONG
var A_sub22 = A[0..2][0..2];
// { { 1.1, 1.2, 1.3 }, { 2.1, 2.2, 2.3 } } ** WRONG
// { { 1.1, 1.2 }, { 2.1, 2.2 } } <= WHAT I AM EXPECTING
为什么 A[..][0]
returns 与 A[0][..]
的结果完全一样?为什么最后一条语句不是每行包含 2 列,而是 3 列?
这里的大局是我正在构建一个 Matrix
对象,它将数值数据存储在 double
的锯齿状数组中,我想使用 Index
和 Range
。我以为我可以用以下方法让它支持切片
public class Matrix
{
readonly double[][] Elements;
///<summary>Reference a single element</summary>
public ref double this[Index row, Index column]
=> ref Elements[row][column];
///<summary>Extract a sub-matrix</summary>
public double[][] this[Range rows, Range columns]
=> Elements[rows][columns];
///<summary>Extract a row sub-vector</summary>
public double[] this[Index row, Range columns]
=> Elements[row][columns];
///<summary>Extract a column sub-vector</summary>
public double[] this[Range rows, Index column]
=> Elements[rows][column];
}
但这在我的单元测试中失败了。看来即使C#支持语法,结果也是出乎意料的。
有没有一种方法可以使用现有的切片框架实现我想要的功能,而 C# 正在尝试 实现?
抱歉,我来自 Fortran 背景,切片很自然。
此外,我是否是唯一一个发现数组中的最后一个元素被索引为 [^1]
但最后一个元素的范围是 [..^0]
非常令人困惑的人。 ^0
对 Index
的含义与对 Range
.
的含义不同,这在 Microsoft 方面似乎非常不一致
列是三个不同的数组。切片运算符不适用于那些不同的数组,只能在一个数组内工作。
您所做的唯一切片是根据运算符对行数组和 returns 列数组进行切片。
A[0][..]
returns A
的第一个数组(即第一列)。 [..]
然后 returns 整列。
A[..][0]
returns 整个数组(又是 A
),然后是 A
中的第一个条目,又是第一列。
这就是我使用锯齿状数组作为扩展方法实现切片和注入的方法
public static class Extensions
{
public static TData Slice<TData>(this TData[][] matrix, Index row, Index column)
{
return matrix[row][column];
}
public static TData[] Slice<TData>(this TData[][] matrix, Index row, Range columns)
{
var slice = matrix[row];
return slice[columns];
}
public static TData[] Slice<TData>(this TData[][] matrix, Range rows, Index column)
{
var slice = matrix[rows];
var result = new TData[slice.Length];
for (int i = 0; i < result.Length; i++)
{
result[i] = slice[i][column];
}
return result;
}
public static TData[][] Slice<TData>(this TData[][] matrix, Range rows, Range columns)
{
var slice = matrix[rows];
for (int i = 0; i < slice.Length; i++)
{
slice[i] = slice[i][columns];
}
return slice;
}
public static void Inject<TData>(this TData[][] matrix, Index row, Index column, TData value)
{
matrix[row][column] = value;
}
public static void Inject<TData>(this TData[][] matrix, Index row, Range columns, TData[] values)
{
int n = matrix.Length;
int m = matrix.Max((row)=>row.Length);
(int offset, int count) = columns.GetOffsetAndLength(m);
var slice = matrix[row];
var data = new ReadOnlySpan<TData>(values);
var span = new Span<TData>(slice, offset, count);
data.CopyTo(span);
}
public static void Inject<TData>(this TData[][] matrix, Range rows, Index column, TData[] values)
{
var slice = matrix[rows];
for (int i = 0; i < slice.Length; i++)
{
slice[i][column] = values[i];
}
}
public static void Inject<TData>(this TData[][] matrix, Range rows, Range columns, TData[][] values)
{
int n = matrix.Length;
int m = matrix.Max((row)=>row.Length);
(int offset, int count) = columns.GetOffsetAndLength(m);
var slice = matrix[rows];
for (int i = 0; i < slice.Length; i++)
{
var data = new ReadOnlySpan<TData>(values[i]);
var span = new Span<TData>(slice[i], offset, count);
data.CopyTo(span);
}
}
}
我正在尝试使用 Index
和 Range
对象来分割锯齿状数组。但在下面的示例中,我无法提取列,只能提取行。
var A = new float[3][];
A[0] = new float[] { 1.1f, 1.2f, 1.3f };
A[1] = new float[] { 2.1f, 2.2f, 2.3f };
A[2] = new float[] { 3.1f, 3.2f, 3.3f };
// This works as intended to extract rows
var A_row1 = A[0][..]; // { 1.1, 1.2, 1.3 }
var A_row3 = A[^1][..]; // { 3.1, 3.2, 3.3 }
var A_row23 = A[1..^0][..]; // { { 2.1, 2.2, 2.3}, { 3.1, 3.2, 3.3 } }
// This does not work as intended to extract columns
var A_col1 = A[..][0]; // { 1.1, 1.2, 1.3 } ** WRONG
var A_col3 = A[..][^1]; // { 3.1, 3.2, 3.3 } ** WRONG
var A_col23 = A[..][1..^0]; // { { 2.1, 2.2, 2.3}, { 3.1, 3.2, 3.3 } } ** WRONG
var A_sub22 = A[0..2][0..2];
// { { 1.1, 1.2, 1.3 }, { 2.1, 2.2, 2.3 } } ** WRONG
// { { 1.1, 1.2 }, { 2.1, 2.2 } } <= WHAT I AM EXPECTING
为什么 A[..][0]
returns 与 A[0][..]
的结果完全一样?为什么最后一条语句不是每行包含 2 列,而是 3 列?
这里的大局是我正在构建一个 Matrix
对象,它将数值数据存储在 double
的锯齿状数组中,我想使用 Index
和 Range
。我以为我可以用以下方法让它支持切片
public class Matrix
{
readonly double[][] Elements;
///<summary>Reference a single element</summary>
public ref double this[Index row, Index column]
=> ref Elements[row][column];
///<summary>Extract a sub-matrix</summary>
public double[][] this[Range rows, Range columns]
=> Elements[rows][columns];
///<summary>Extract a row sub-vector</summary>
public double[] this[Index row, Range columns]
=> Elements[row][columns];
///<summary>Extract a column sub-vector</summary>
public double[] this[Range rows, Index column]
=> Elements[rows][column];
}
但这在我的单元测试中失败了。看来即使C#支持语法,结果也是出乎意料的。
有没有一种方法可以使用现有的切片框架实现我想要的功能,而 C# 正在尝试 实现?
抱歉,我来自 Fortran 背景,切片很自然。
此外,我是否是唯一一个发现数组中的最后一个元素被索引为 [^1]
但最后一个元素的范围是 [..^0]
非常令人困惑的人。 ^0
对 Index
的含义与对 Range
.
列是三个不同的数组。切片运算符不适用于那些不同的数组,只能在一个数组内工作。
您所做的唯一切片是根据运算符对行数组和 returns 列数组进行切片。
A[0][..]
returns A
的第一个数组(即第一列)。 [..]
然后 returns 整列。
A[..][0]
returns 整个数组(又是 A
),然后是 A
中的第一个条目,又是第一列。
这就是我使用锯齿状数组作为扩展方法实现切片和注入的方法
public static class Extensions
{
public static TData Slice<TData>(this TData[][] matrix, Index row, Index column)
{
return matrix[row][column];
}
public static TData[] Slice<TData>(this TData[][] matrix, Index row, Range columns)
{
var slice = matrix[row];
return slice[columns];
}
public static TData[] Slice<TData>(this TData[][] matrix, Range rows, Index column)
{
var slice = matrix[rows];
var result = new TData[slice.Length];
for (int i = 0; i < result.Length; i++)
{
result[i] = slice[i][column];
}
return result;
}
public static TData[][] Slice<TData>(this TData[][] matrix, Range rows, Range columns)
{
var slice = matrix[rows];
for (int i = 0; i < slice.Length; i++)
{
slice[i] = slice[i][columns];
}
return slice;
}
public static void Inject<TData>(this TData[][] matrix, Index row, Index column, TData value)
{
matrix[row][column] = value;
}
public static void Inject<TData>(this TData[][] matrix, Index row, Range columns, TData[] values)
{
int n = matrix.Length;
int m = matrix.Max((row)=>row.Length);
(int offset, int count) = columns.GetOffsetAndLength(m);
var slice = matrix[row];
var data = new ReadOnlySpan<TData>(values);
var span = new Span<TData>(slice, offset, count);
data.CopyTo(span);
}
public static void Inject<TData>(this TData[][] matrix, Range rows, Index column, TData[] values)
{
var slice = matrix[rows];
for (int i = 0; i < slice.Length; i++)
{
slice[i][column] = values[i];
}
}
public static void Inject<TData>(this TData[][] matrix, Range rows, Range columns, TData[][] values)
{
int n = matrix.Length;
int m = matrix.Max((row)=>row.Length);
(int offset, int count) = columns.GetOffsetAndLength(m);
var slice = matrix[rows];
for (int i = 0; i < slice.Length; i++)
{
var data = new ReadOnlySpan<TData>(values[i]);
var span = new Span<TData>(slice[i], offset, count);
data.CopyTo(span);
}
}
}