二维向量vector の 邪恶的多维数组 (二) 降维

来源:互联网 发布:广东电信 网络测速 编辑:程序博客网 时间:2024/05/25 05:36

Killing MD: 1D Mimics

数组降维:一维模拟

An MD array can be simulated in a 1D array as long as you know the dims. For example, if you want a 2x3 array, it would conceptually look like the following:

如果你知道一个MD(多维 == multi-dimensional)数组的维度,它就可以用一维数组来模拟。例如,你想要一个2×3的数组,概念上它看起来像这样:

0 12 34 5

This array has 6 elements (2 * 3). And you'll notice that each row starts with a multiple of the width (2). Therefore you can simulate a 2D array in a 1D array like so:

这个数组有6个元素2*3而且你会发现,每行宽度都为2因此,你可以用一个一维数组来模拟这个二维数组,如下所示:

// int evil[5][6];      // desired 2D arrayint mimic[5*6];         // mimicing 1D array// evil[2][3] = 1;      // desired [2][3] indexmimic[ (2*6) + 3] = 1;  // mimiced [2][3] index 
All that math looks kind of dumb and inefficient. Really, though, the compiler does all that math for MD arrays too, so this isn't any less efficient. It is pretty ugly though, granted.
所有这一切的数学看起来那种愚蠢的和低效的真的,虽然,编译器D数组也是这样做的,所以这不并是什么低效率
So you might be asking yourself... "what is the benefit of this?"
所以,你可能会问自己......这样做有什么好处
Well, unlike MD array flavors which are all different and totally incompatible, the respective1D flavors are all compatible!
额,不想MD数组的特性,所有的特性都不同,而且互不兼容。一维数组却完全兼容!
Consider the following:考虑以下几点:
int func2(int p[], int width);  // remember this function?                                // it'll work with all flavors of mimicing 1D arrays!int a[ 5 * 6 ];func2( a, 6 );  // works//---int* b = new int[5*6];func2( b, 6 );  // works//---vector<int> c(5*6);func2( &c[0], 6 );  // works (but is a little ugly, granted) 

See how graceful it all is? Aside from the ugly math in the indexing, everything flows together so much nicer. This is the first step to killing MD arrays. The next step is even more fantastic.

Killing MD: Abstracted array classes

数组降维:抽象数组类

So while 1D arrays are easier to work with, They do suffer from a few problems:

虽然一位数组很容易使用,但是也受一些问题困扰:
- look up requires you to know the dims of the array (can't do y*width without knowing the width).
- look up syntax is ugly and easy to botch. It only gets worse as you get into larger dimensions. Imagine a mimiced 3D array:z*width*height + y*width + x.

- 查找时,需要你知道数组的维数(如果不知道数组的宽度就不能使用 y*width)。

- 查找语句看起来很很难看,而且很容易弄糟。只有在你面对更多的维数时,变得更加糟糕。想象一下模拟的3D数组:z*width*height + y*width + x

C++ provides an excellent solution to this problem: classes. Just make an Array2D/Matrix/whatever class to wrap all of this behavior.

C ++为这个问题提供了很好的解决方案:类。只要让 2D数组、矩阵、诸如此类 变成一个 类,然后包装所有的行为。
(Or don't -- there are probably many premade ones available online that you can use, I never actually bothered looking. Boost probably has one, I'd imagine -- I'll have to research this more some day. If you are reading further in this article I'm going to assume you don't have / don't want to use such a library and want to write your own)

While you can't overload the [bracket] operator to take two parameters*, you can overload the parenthesis operator. So syntax is a little different:

虽然你不能重载[ ]操作符来包含两个参数,你可以重载 括号操作符。因此,语法有点不一样:

// int bad[4][5];Array2D<int> good(4,5);// bad[1][2] = 3;good(1,2) = 3;
(* Yes you can overload the brakets and return a weird type to make double brakets work, but it causes all sorts of problems and defeats all the elegance of using a fully contained class)

*是的,你可以重载括号,并返回一个奇怪的类型让双方括号得以工作。但是它会导致各种各样的问题,违背类自我包含的优雅性质)
So how do you make this Array2D class? As long as you don't want bells and whistles, it can be very simple.
那么,你如何创建这个Array2D只要你不想花哨它可以非常简单

Here is a very simple class with no bells or whistles:

这是一个非常简单的类,非常的朴实

template <typename T>class Array2D{public:  // constructor  Array2D(unsigned wd,unsigned ht)    : nWd(wd), nHt(ht), pAr(0)  {    if(wd > 0 && ht > 0)      pAr = new T[wd * ht];  }  // destructor  ~Array2D()  {    delete[] pAr;  }  // indexing (parenthesis operator)  //  two of them (for const correctness)  const T& operator () (unsigned x,unsigned y) const  {  return pAr[ y*nWd + x ];   }  T& operator () (unsigned x,unsigned y)  {  return pAr[ y*nWd + x ];   }  // get dims  unsigned GetWd() const { return nWd; }  unsigned GetHt() const { return nHt; }  // private data membersprivate:  unsigned nWd;  unsigned nHt;  T*       pAr;  // to prevent unwanted copying:  Array2D(const Array2D<T>&);  Array2D& operator = (const Array2D<T>&);

That's all there is to it!这一切就是这么简单

If you want to get fancy you can add in resizing stuff, copy-on-write reference counting, boundary checking, etc, etc.

如果你想让它变得更加理想,可以添加上调整大小的函数,写时复制引用计数边界检查等等等等。

这部分内容仅做参考,有多种实现必然是好事,但是为何 straight array, nested new, vectors of vectors依然存在?存在即合 理,simple is beautiful...

写时复制(copy-on-write COW)http://www.cnblogs.com/MuyouSome/archive/2012/12/05/2802787.html

维基百科:    写入时复制(Copy-on-write)是一个被使用在程式设计领域的最佳化策略。其基础的观念是,如果有多个呼叫者(callers)同时要求相同资源,他们会共同取得相同的指标指向相同的资源,直到某个呼叫者(caller)尝试修改资源时,系统才会真正复制一个副本(private copy)给该呼叫者,以避免被修改的资源被直接察觉到,这过程对其他的呼叫只都是通透的(transparently)。此作法主要的优点是如果呼叫者并没有修改该资源,就不会有副本(private copy)被建立。第一代Unix系统实现了一种傻瓜式的进程创建:当发出fork()系统调用时,内核原样复制父进程的整个地址空间并把复制的那一份分配给子进程。这种行为是非常耗时的,因为它需要:    - 为子进程的页表分配页帧    - 为子进程的页分配页帧    - 初始化子进程的页表    - 把父进程的页复制到子进程相应的页中 这种创建地址空间的方法涉及许多内存访问,消耗许多CPU周期,并且完全破坏了高速缓存中的内容。在大多数情况下,这样做常常是毫无意义的,因为许多子进程通过装入一个新的程序开始它们的执行,这样就完全丢弃了所继承的地址空间。 现在的Linux内核采用一种更为有效的方法,称之为写时复制(Copy On Write,COW)。这种思想相当简单:父进程和子进程共享页帧而不是复制页帧。然而,只要页帧被共享,它们就不能被修改,即页帧被保护。无论父进程还是子进程何时试图写一个共享的页帧,就产生一个异常,这时内核就把这个页复制到一个新的页帧中并标记为可写。原来的页帧仍然是写保护的:当其他进程试图写入时,内核检查写进程是否是这个页帧的唯一属主,如果是,就把这个页帧标记为对这个进程是可写的。



0 0
原创粉丝点击