伙计们,
请任何人都可以想象在 C# 中定义一个结构的“好方法”,它的行为类似于一个数组(int[,]
在我的情况下是一个),除了(像所有结构一样)结构的副本被传递给任何/所有调用的方法。
下面是我丑陋的第一次尝试(一个模拟简单的结构int[]
,还不是矩阵),并带有一个小测试工具来使它做一些事情。我想我会有一个struct Matrix of Row's,它会遵循类似的模式。但这只是太冗长了。一定有更好的办法!
请问有什么想法吗?
测试线束
using System;
struct Row {
public const int COLS = 16;
public int _0;
public int _1;
public int _2;
public int _3;
public int _4;
public int _5;
public int _6;
public int _7;
public int _8;
public int _9;
public int _10;
public int _11;
public int _12;
public int _13;
public int _14;
public int _15;
public int this[int index] {
get {
switch ( index ) {
case 0: return _0;
case 1: return _1;
case 2: return _2;
case 3: return _3;
case 4: return _4;
case 5: return _5;
case 6: return _6;
case 7: return _7;
case 8: return _8;
case 9: return _9;
case 10: return _10;
case 11: return _11;
case 12: return _12;
case 13: return _13;
case 14: return _14;
case 15: return _15;
}
throw new IndexOutOfRangeException("Index="+index+" is not between 0 and 15");
}
set {
switch ( index ) {
case 0: _0 = value; break;
case 1: _1 = value; break;
case 2: _2 = value; break;
case 3: _3 = value; break;
case 4: _4 = value; break;
case 5: _5 = value; break;
case 6: _6 = value; break;
case 7: _7 = value; break;
case 8: _8 = value; break;
case 9: _9 = value; break;
case 10: _10 = value; break;
case 11: _11 = value; break;
case 12: _12 = value; break;
case 13: _13 = value; break;
case 14: _14 = value; break;
case 15: _15 = value; break;
}
}
}
public override string ToString() {
return String.Format("{0}{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}{11}{12}{13}{14}{15}"
,_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15);
}
}
class TestClass
{
public static void ProcessRow(Row row, int index) {
if (index == Row.COLS) return;
row[index] = 1;
Console.WriteLine("{0,2} {1}", index, row);
ProcessRow(row, index+1);
Console.WriteLine("{0,2} {1}", index, row);
}
public static void Main() {
var row = new Row();
ProcessRow(row, 0);
}
}
输出:
0 1000000000000000
1 1100000000000000
2 1110000000000000
3 1111000000000000
4 1111100000000000
5 1111110000000000
6 1111111000000000
7 1111111100000000
8 1111111110000000
9 1111111111000000
10 1111111111100000
11 1111111111110000
12 1111111111111000
13 1111111111111100
14 1111111111111110
15 1111111111111111
15 1111111111111111
14 1111111111111110
13 1111111111111100
12 1111111111111000
11 1111111111110000
10 1111111111100000
9 1111111111000000
8 1111111110000000
7 1111111100000000
6 1111111000000000
5 1111110000000000
4 1111100000000000
3 1111000000000000
2 1110000000000000
1 1100000000000000
0 1000000000000000
何苦?
该测试验证:
- 将 Row 结构的 COPY 传递给 ProcessRow,保持本地 Row 不变。
- 结构为 16 个整数的 16 个调用不会弹出调用堆栈。
如果你有兴趣......我真正的问题是我目前正在将一个 int[16,16] “映射”传递给一个递归调用树,该树处理编程挑战 62 - Bendorf Weed Invasion中的每个“日常移动” 。 . 目前我正在为评估的每一个动作做一个 Array.Copy (实际上是一对),并且有大约 1.3 个 quintillion 组合需要考虑 10 个有效动作,所以(有趣的是)它太慢了,在 abour 33.5小时. 我认为传递结构而不是数组引用可能会显着加快速度,但我仍然怀疑我需要一种完全不同的方法(即完全重新考虑我的算法)来接近“期望的”100 秒。我需要做的是“快速尝试”,所以我想我会先找到一些效率……我现在确切地知道这些人在穿孔磁带时代的感受。为你的输出等待一天半完全糟透了!
编辑:包括我的答案,这是基于 AbdElRaheim 接受的答案。
它使用“平面”固定整数数组来模拟双下标整数数组。
它所做的只是递归ProcessDay 10 次,传递map和day index。在每个“天”中,它只是将一个随机单元格设置为day,并打印出两次地图:第一次是在调用堆栈下的路上,然后在再次返回的路上......这样你就可以,如果你愿意,验证ProcessDay的每个调用都有自己的地图副本。
我正在以两种不同的方式执行此操作:使用Matrix struct和使用标准的int[,] ...惊喜(至少对我而言)是该结构更慢...叹息!
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.IO;
using System.Diagnostics;
[StructLayout(LayoutKind.Sequential, Size=64)]
unsafe struct Matrix
{
public const int ROWS = 16;
public const int COLS = 16;
private fixed int Cells[ROWS*COLS];
public int this[int row, int col] {
get {
fixed ( int* addressOfCells = Cells)
return *(addressOfCells+row*16+col);
}
set {
fixed ( int* addressOfCells = Cells)
*(addressOfCells+row*16+col) = value;
}
}
public override string ToString() {
var sb = new StringBuilder(COLS+2*ROWS);
for ( int row=0; row<ROWS; ++row) {
for ( int col=0; col<COLS; ++col)
sb.Append(ToChar(this[row,col]));
sb.Append(Environment.NewLine);
}
return sb.ToString();
}
private char ToChar(int cellValue) {
return cellValue==0 ? '.' : (char)('0'+cellValue);
}
}
class TestClass
{
private static readonly Random RANDOM = new Random();
private static StreamWriter _output;
public static void Main() {
using ( _output = new StreamWriter("TestClass.out") ) {
// priming goes
ProcessDay(new Matrix(), 0);
ProcessDay(new int[16,16], 0);
// timed runs
Time("Using a struct", delegate() {
for (int i=0; i<1000; ++i)
ProcessDay(new Matrix(), 0);
});
Time("Using an array", delegate() {
for (int i=0; i<1000; ++i)
ProcessDay(new int[16,16], 0);
});
}
Console.WriteLine("See: "+Environment.CurrentDirectory+@"\TestClass.out");
Pause();
}
#region Using a plain int[,]
private static void ProcessDay(int[,] theMap, int day) {
if ( day == 10 ) return;
var myMap = (int[,])theMap.Clone();
myMap[RANDOM.Next(Matrix.ROWS),RANDOM.Next(Matrix.COLS)] = day;
WriteMap(day, myMap);
ProcessDay(myMap, day + 1);
WriteMap(day, myMap);
}
private static void WriteMap(int day, int[,] map) {
_output.Write("\r\n{0}:\r\n", day);
for ( int row=0; row<16; ++row) {
for ( int col=0; col<16; ++col)
_output.Write(map[row,col]==0 ? '.' : (char)('0'+map[row,col]));
_output.WriteLine();
}
}
#endregion
#region Using the Matrix struct
public static void ProcessDay(Matrix map, int day) {
if ( day == 10 ) return;
map[RANDOM.Next(Matrix.ROWS),RANDOM.Next(Matrix.COLS)] = day;
WriteMap(day, map);
ProcessDay(map, day + 1);
WriteMap(day, map);
}
private static void WriteMap(int day, Matrix map) {
_output.Write("\r\n{0}:\r\n{1}", day, map);
}
#endregion
private delegate void TimedTask();
private static void Time(string desc, TimedTask task) {
Console.WriteLine();
Console.WriteLine(desc);
var sw = new System.Diagnostics.Stopwatch();
sw.Start();
task();
sw.Stop();
Console.WriteLine(desc +" took "+ sw.ElapsedMilliseconds + " ms");
}
[Conditional("DEBUG")]
private static void Pause() {
Console.Write("Press any key to continue . . .");
Console.ReadKey();
}
}
干杯。基思。
编辑2:这是我对“为什么结构不更快?”问题的调查结果。毕竟它“应该”是,不是吗?我的意思是在堆栈上创建大量的大型结构应该比在堆上创建所有那些等效的对象和垃圾收集它们更有效......那么故事是什么?
假设“结构创建和传递”更有效,我认为我们在那里取得的收益一定会被效率较低的单元格访问所抵消......所以我对我的结构索引器进行了下面的小小调整 -(row<<4)
而不是row*16
- 和现在 struct 比数组快 30% 左右......这是我最初敢于希望的收益,所以是的,很酷 ;-)
public int this[int row, int col] {
get {
fixed ( int* p = Cells)
return *(p+(row<<4)+col);
}
set {
fixed ( int* p = Cells)
*(p+(row<<4)+col) = value;
}
}
我还尝试将所有内容输出两次以测试我的矩阵访问仍然不如本机二维数组访问效率低的理论......确实如此。当您输出地图两次时,结构和类解决方案又恢复了并驾齐驱(一次是在调用堆栈的途中,另一次是在备份它的路上)......所以我很想知道索引器是如何在原生二维数组中实现。
新问题:任何关于如何加快我的索引器实现的建议,或对原生二维数组的 C# 编译器实现的引用都将受到欢迎。TIA ;-)
干杯。基思。