【问题标题】:Delphi dynamic array with tlist带 tlist 的 Delphi 动态数组
【发布时间】:2015-02-17 19:45:53
【问题描述】:

我正在 Delphi/Lazarus 中为类似于 BASIC 的语言创建一个简单的解释器。 我已经实现了很多功能。目前,我正在尝试创建一个类似 DIM 的命令来处理多维数值数组。 我的想法是使用 TList 来模拟仅受可用内存限制的多维数组。例如,当我在解释器中声明如下命令时:

DIM num_arr[3,3,3]

我想创建一个双精度三维数组,每个索引从 0 到 2 不等。

到目前为止,我只有创建“TList 数组”的功能。我正在使用两个 TList 对象来保存数组维度和数据项列表,我还有第三个对象来保存存储/检索数据的索引。我想不通的是如何将索引列表与 TList 中的特定条目相关联。当数组最多为二维时,这很简单,我可以将每对索引转换为数字序列,但不能成功转换为三个及更多维。 有什么算法可以用来解决这个问题吗?真的很难找到与这件事相关的东西。 欢迎任何有关如何实现类似功能的建议。下面我将发布我目前开发的部分代码:

//Creates a "n" dimensional array
function array_allocate(Dim: TList; var DataArray: TList): integer;
var
  i: integer;
  s: string;
begin
  Result := 1;
  //Simple way to find the array length
  //For example. An array with dimensions [3,3,3] will handle 27 items
  for i := 0 to Dim.Count-1 do
  begin
    Result := Result * Integer(Dim[i]);
  end;
  Result := Result;
  DataArray.Capacity := Result; //DataArray now handles 27 items

  //************************************
  //Every line below is just for testing
  //************************************
  fmMain.Memo1.Lines.Add('Allocating size for array with '+IntToStr(Dim.Count)+' dimension(s).');
  s := '';
  for i := 0 to Dim.Count-1 do
    s := s + '['+IntToStr(Integer(Dim[i]))+']';
  fmMain.Memo1.Lines.Add('DIM Sizes: '+s);
  fmMain.Memo1.Lines.Add('Manage: '+IntToStr(Result)+' Items.');
end;

{*************************************}
{NOT functional, this is the challenge}
{*************************************}
function calculate_offset(Dim, Indexes: TList; var DataArray: TList; BaseAddr: integer): integer;
var
  i, Depth, dimIdx: Integer;
  k,index,sizeProduct: integer;
begin
  for Depth := 0 to Dim.Count-1 do
    for dimIdx := 0 to Integer(Dim[Depth])-1 do
      fmMain.mmOut.Lines.Add('Dim: '+IntToStr(Depth)+' ,Index: '+IntToStr(dimIdx));

  result := 0;
end;

procedure TfmMain.FormShow(Sender: TObject);
var
  dataList: TList; //keep the data
  dimList: TList; //keep the dimensions
  indexList: TList; //keep the indexes
  offset: integer;
begin
  dimList := TList.Create; //create the dim array
  //simulate the creation of an array with dimension [3,3,3]
  //something like DIM myVar[3,3,3]
  dimList.Add(Pointer(3));
  dimList.Add(Pointer(3));
  dimList.Add(Pointer(3));

  dataList := TList.Create; //create the data list
  array_allocate(dimList, dataList); //allocates memory

  //indexList is the way to indicate which index to retrieve/store data
  indexList := TList.Create;
  indexList.Add(Pointer(1));
  indexList.Add(Pointer(1));
  indexList.Add(Pointer(1));
  indexList.Add(Pointer(1));

  //The idea is to relate indexes like [0,0,0], [0,0,1], [0,1,1] to
  //a numeric sequence between 0 to 26 in this case (DIM = [3,3,3])
  offset := calculate_offset(dimList, indexList, dataList, 1, 0);

  indexList.Free;
  dimList.Free;
  dataList.Free;
end;

【问题讨论】:

  • 我对您选择的数据结构持怀疑态度。我认为您想要一个数据类型来保存维度,以及一个用于内容的平面数组。我会将其设为元素类型的动态数组。尺寸将保存在TArray<Integer> 中。动态数组的长度是维度的乘积。
  • 如果它意味着灵活,更高级别的方法可能更合适。一个选项可能是一个TNDimPoint 类,它至少包含一个整数来表示维度的数量,而TList<integer> 则表示每个维度的坐标条目。然后可以将数据保存在像TNDimArray<T> = class(TObjectDictionary<TNDimPoint, T>)(字典拥有键)这样的类中,并为TNDimPoint编写合适的比较器 - 这将保证条目的唯一性,让您快速随机访问数据,并且在问题的大小或维度。
  • 上述方法对于(不规则、非对称等)稀疏矩阵特别有用,例如,您只需要存储定义的数据而不需要分配整体如果只有少量点将被占用,则为数组。这真的取决于最终目标是什么。
  • 问问自己你认为Result := Result 做了什么。您还缺少用于生命周期管理的 try/finally。当然,您不会构建与 GUI 混合的解释器。肯定 TLIST 不是容器的最佳选择。即使您被困在前泛型 Delphi 上,您也可以制作一些类型安全的容器来避免那些繁琐且容易出错的转换。
  • 嗨,J...,这是一个非常好的方法。谢谢。

标签: arrays delphi multidimensional-array lazarus tlist


【解决方案1】:

当我阅读您的问题时,您希望将索引元组转换为线性索引,假设行主要存储。

假设维度保存在动态数组dim 中,索引保存在动态数组idx 中。然后线性指数是这样计算的:

function LinearIndex(const dim, idx: array of Integer): Integer;
var 
  i: Integer;
begin
  Assert(Length(dim)=Length(idx));
  Assert(Length(dim)>0);
  Result := idx[0];
  for i := 1 to high(dim) do
    Result := Result*dim[i-1] + idx[i];
end;

【讨论】:

  • 感谢大卫的提示,实际上我上面所做的只是第一次尝试。现在我有了一些新想法。
  • 哇!你的代码正是我需要的灵感。谢谢。天哪。就是这么简单。我试图给你一个 1+,但我在 Stackoverflow 上太新了。
【解决方案2】:

上述 LinearIndex 函数对某些不平衡数组不起作用。例如,如果我正确分配“dim”来保存值 [2,3],该函数将返回 2 作为“idx”两个内容的索引:[0,2], [1,0]。 我基于在 StackOverflow 上找到的 another post 创建了一个 Pascal 函数,这个函数似乎可以工作。

function LinearIndex2(const dim, idx: array of Integer): Integer;
var
  i,index,mult: Integer;
begin
  Assert(Length(dim)=Length(idx));
  Assert(Length(dim)>0);

  index := 0; mult := 1;
  for i := 0 to High(dim) do
  begin
    index := index + idx[i] * mult;
    mult := mult * dim[i];
  end;

  result := index;
end;

【讨论】:

    猜你喜欢
    • 2020-08-08
    • 1970-01-01
    • 2011-08-13
    • 2014-04-08
    • 2023-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多