5

这在某种程度上是我之前的问题的延续,在这里找到。本质上,我正在尝试用一个基本示例测试 dll/函数,但我得到了“ E2010 - 不兼容的类型:AInteger/ADouble and Set ”和“ E1012 - 常量表达式违反了子范围界限”错误。我(有点)明白它想说什么,但不知道我应该修复什么。例如:

var
  n: Integer; 
  Ap, Ai: AInteger;
  Ax, b: ADouble;

begin
  // Initializations
  n := 5;
  Ap := [0, 2, 5, 9, 10, 12]; <- E2010
  Ai := [0, 1, 0, 2, 4, 1, 2, 3, 4, 2, 1, 4]; <- E2010
  Ax := [2, 3, 3, -1, 4, 4, -3, 1, 2, 2, 6, 1]; <- E2010 and E1012
  b := [8, 45, -3, 3, 19]; <- E1012

其中AIntegerADouble类型是我的数组:

ADouble = array[0..High(Integer) div SizeOf(Double) - 1] of Double;
AInteger = array[0..High(Integer) div SizeOf(Integer) - 1] of Integer;

并且应该以这种方式初始化(​​根据 Rudy 的 Delphi 页面和其他 C-to-Pascal 源),因为它们是用double Ax[]C 编写的。我确信我做错了一些简单的事情,或者为了测试我可以改变dll,但也许我在谷歌上搜索错了,因为我找不到示例/解决方案。所以,以问题的形式:

Q1 : E1012 是指

“如果你做这些[AInteger 和 ADouble]之类的事情,请确保不要太接近 High(Integer),因为编译器可能会抱怨数据结构太大。” (引自鲁迪的页面)

Q2 : 我应该如何更改此代码?

提前感谢您的帮助。

4

3 回答 3

10

您可以使用这样的语法来做到这一点。

像这样定义你的数组:

ADouble = array[0..High(Integer) div SizeOf(Double) - 1] of Double;

将初始化整个 32 位 RAM 大小的整数数组!您将永远无法分配这样的变量(仅在 Win64 上,但您将使用 4 GB 的 RAM 来存储仅 6 个整数)!:)

您的数组需要是动态的,即在运行时具有不断变化的大小。所以你必须这样定义它:

type
  AInteger =  array of integer;

在语言的当前状态 AFAIR 中,无法直接分配此类数组。

所以你需要编写这样一个函数:

procedure SetArray(var dest: AInteger; const values: array of integer);
begin
  SetLength(dest,Length(values));
  move(values[0],dest[0],length(values)*sizeof(integer));
end;

您可以使用常量数组作为源:

const
  C: array[0..5] of Integer = (0, 2, 5, 9, 10, 12);
var
  Ap: AInteger;
begin
  SetArray(Ap,C);

或使用开放数组参数

var
  Ai: AInteger;
begin
  SetArray(Ai,[0, 2, 5, 9, 10, 12]);

当然,第二种解决方案听起来更接近您的预期。

更新:对于较新的版本,您当然可以使用动态数组构造函数,例如:

var
  Ai: AInteger;
begin
  Ai := AInteger.Create(0,2,5,9,10,12);

更新 2:从 XE7 开始,您可以使用另一种更简洁的语法:

var
  Ai: AInteger;
begin
  Ai := [0,2,5,9,10,12];
于 2013-06-18T05:37:19.913 回答
3

在我看来,您好像有用 C 编写的稀疏求解器代码,并试图将其链接到您的 Delphi 程序。我认为您在声明外部导入的方式上存在根本问题。我不会直接回答您提出的问题,而是向您展示我认为声明和调用此类外部导入的正确方法。

我要说的第一件事是您声明的大型静态数组类型不是您需要的。这些数组类型有时很有用,但只有在将另一个数组转换为PADouble = ^ADouble. 在您的情况下,您根本不需要这些数组,我建议您删除它们。

我将假设您正在调用一个名为的函数,该函数将 、 、 、solven作为nz输入Ap参数并作为输出参数返回。该函数返回这样的结果,其中是由 和 指定的维方稀疏矩阵。该参数指定非零元素的数量。毫无疑问,实际功能在细节上会有所不同,但概念是一样的。例如,从推断是很常见的,但这些细节是由你来解决的。AiAxbxxA*x=bAnApAiAxnznzAp[n]

我建议您声明该函数以接收参数作为指向第一个元素的指针。所以函数声明看起来像这样:

function solve(
  n: Integer; 
  nz: Integer;
  Ap: PInteger;
  Ai: PInteger;
  Ax: PDouble;
  b: PDouble;
  x: PDouble
): Integer; cdecl; external;

然后你需要填充你的稀疏矩阵数组。将这些声明为动态数组:

var
  Ap: TArray<Integer>;
  Ai: TArray<Integer>;
  Ax: TArray<Double>;
....
SetLength(Ap, n);
Ap[0] := ...;
....
SetLength(Ai, nz);
Ap[0] := ...;
....
SetLength(Ax, nz);
Ax[0] := ...;
....

我希望您在运行时只知道 等的值nnz并且矩阵的内容将使用循环等填充。问题中的代码可能是测试代码,以尝试测试外部代码。Arnaud 的回答为您提供了有关如何填充动态数组的合理建议。

您还需要初始化bx

var
  b: TArray<Double>;
  x: TArray<Double>;
....
SetLength(b, n);
b[0] := ...;
....
SetLength(x, n);
// no need to initialise values of x[i] since it is the output

现在您可以调用该函数:

var
  retval: Integer;
....
retval := solve(n, nz, PInteger(Ap), PInteger(Ai), PDouble(Ax), 
  PDouble(b), PDouble(x));

最后一点,关于泛型数组的使用。由于您使用的是现代 Delphi,我建议您使用通用动态数组。因此,array of ...您应该使用TArray<...>. 原因是泛型类型与旧式动态数组具有不同的类型兼容性规则。例如,在上面的代码中,bx是赋值兼容的。但是如果他们是这样声明的:

var
  b: array of Double;
  x: array of Double;

那么它们将不兼容分配。你可以通过声明一个类型来解决这个问题,TDoubleArray = array of Double. 但是,如果您使用泛型数组,那么您可以使用泛型容器类,例如TList<T>返回TArray<T>您可以轻松使用的类型的值。

我知道这不是你问的问题,但我觉得它可能对你有用。

于 2013-06-18T08:15:32.153 回答
0

此问题与开放数组无关,因此请从您的问题中删除该标签。

您使用的括号语法声明了一个Set值,它基本上是一种特殊类型的位掩码,其中每个位对应于该位置的值。换句话说,[0, 2, 5, 9, 10, 12]Set of Integer包含 6 个元素的 a,其中第 0 位表示值 0,第 1 位表示值 2,第 2 位表示值 5,依此类推。ASet不能像您尝试做的那样直接分配给数组。

于 2013-06-17T22:03:28.937 回答