2013-06-17 1 views
3

Это несколько продолжение моего предыдущего вопроса, found here. По сути, я пытаюсь протестировать dll/functions с помощью базового примера, но я получаю 'E2010 - несовместимые типы: AInteger/ADouble и Set 'и' E1012 - постоянное выражение нарушает границы поддиапазона 'ошибки на моих массивах. Я получаю (несколько) то, что он пытается сказать, но не может понять, что я должен исправлять. Например:Инициализация Delphi Array

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 

где AInteger и ADouble типы мои массивы:

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

и должен быть инициализирован таким образом (по странице Delphi Rudy и другие C-к-Паскаля источников), так как они были написаны как double Ax[] в C. Я уверен, что есть что-то простое, что я делаю неправильно или могу изменить ради тестирования моей dll, но, возможно, я ошибаюсь, потому что не могу найти пример/решение. Так, в форме вопроса:

Q1: Является ли E1012 со ссылкой на

«И если вы делаете такие вещи, как эти [AInteger и AЩелкните двойным], убедитесь, что не слишком близко к High (Integer), поскольку компилятор может пожаловаться на то, что структура данных будет слишком большой ». (Цитата из страницы Руди)

Q2: Как я должен изменить этот код?

Заранее благодарим за любую помощь.

+0

Это может помочь, если вы точно определите, что вы пытаетесь сделать с помощью своих массивов. Код, который вы написали для инициализации ваших массивов, никогда не работал ни в одной из версий Delphi/Pascal, а страницы Руди не говорят, что они должны быть AFAICT, потому что это недопустимый синтаксис, и Руди знает лучше (если он на самом деле говорит, что, пожалуйста укажите ссылку). Delphi позволяет инициализировать постоянные массивы (не с этим синтаксисом) или динамические массивы с использованием синтаксиса псевдоконструктора. Трудно сказать, как вы должны его изменить, потому что вы не дали понять, что вы пытаетесь сделать. –

+0

Это выглядит как разреженный матричный решатель. Какой из них вы используете из интереса. Мне нравится CSparse Тима Дэвиса, который я завернул, скомпилировав код C в .obj, а затем связав его точно так же, как и вы. –

+0

Кен - извините, ссылка, на которую я имею в виду, включает преобразования C-to-pascal. На странице Руди упоминается это [здесь] (http://rvelthuis.de/articles/articles-convert.html здесь) в разделе «Параметры массива». @David Это точно решатель, с которым я работаю, рад узнать, что путь, который я решил пойти, сделал кто-то еще ха-ха. –

ответ

8

Вы можете сделать это с помощью такого синтаксиса.

Определение вашего массива, как например:

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

будет инициализировать массив целого размера всего 32 битной памяти! Вы никогда не сможете выделить такую ​​переменную (только на Win64, но вы будете использовать 4 ГБ ОЗУ для хранения всего 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); 
+6

Вы можете создать динамические массивы напрямую, 'Ai: = AInteger.Create (0,2,5,9,10,12);' –

+0

Спасибо! Сначала я работал с динамическими массивами, как вы их определили, но мне предложили изменить свой код так, как я определил его выше. Мне приятно чувствовать, что я изначально имел правильную идею. –

0

Этот вопрос не имеет ничего общего с открытым массивом, поэтому, пожалуйста, удалите этот тег из своего вопроса.

Используемый синтаксис скобки объявляет значения Set значений, которые в основном представляют собой особый тип битмаски, где каждый бит соответствует значению в этой позиции. Другими словами, [0, 2, 5, 9, 10, 12] является Set of Integer, содержащим 6 элементов, где бит 0 относится к значению 0, бит 1 относится к значению 2, бит 2 относится к значению 5 и так далее. A Set не может быть назначено непосредственно массиву, как вы пытаетесь сделать.

+2

Я не буду так груб. AFAIK '[0, 2, 5, 9, 10, 12]' также может быть параметром открытого массива, содержащим массивы, поэтому через функцию может влиять массив целых чисел. См. Мой ответ. –

2

Он смотрит на меня, как будто у вас есть редкий решатель код, написанный на C и пытаются ссылку на вашу программу Delphi. Я думаю, что у вас есть фундаментальная проблема в том, как вы заявили о своем внешнем импорте. Вместо того, чтобы ответить на заданный вами вопрос напрямую, я покажу вам, как я считаю, правильным способом объявить и называть такой внешний импорт.

Первое, что я бы сказал, это то, что большие статические типы массивов, которые вы объявили, не являются тем, что вам нужно здесь. Эти типы массивов иногда могут быть полезны, но только на самом деле, когда вы добавляете другой массив в PADouble = ^ADouble. В вашей ситуации вам вообще не нужны эти массивы, и я предлагаю удалить их.

я буду считать, что вы вызываете функцию с именем solve, который принимает n, nz, Ap, Ai, Ax и b в качестве входных параметров и возвращает x в качестве выходного параметра. Функция возвращает x такой, что A*x=b, где A - размерная квадратная разрешенная матрица n, указанная Ap, Ai и Ax. Параметр nz указывает количество ненулевых элементов. Несомненно, фактическая функция будет отличаться в деталях, но концепции будут одинаковыми. Например, обычно вызывается nz от Ap[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] := ...; 
.... 

Я ожидаю, что вы будете знать только значение n, nz и т.д. во время выполнения и что содержание матрицы будет заполнено с помощью петли и так далее. Код в вопросе - это, по-видимому, тестовый код, чтобы попробовать и проверить внешний код. Ответ Арно дает вам здравый совет о том, как заполнить динамический массив.

Вам также необходимо инициализировать b и x:

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)); 

Один конечный пункт, converning использование общих массивов. Поскольку вы используете современный Delphi, я предлагаю использовать общие динамические массивы. Итак, вместо array of ... вы должны использовать TArray<...>. Причина этого в том, что общие типы имеют разные правила совместимости типов из динамических массивов старого стиля. В приведенном выше коде, например, b и x являются совместимыми с назначением. Но если они были объявлены следующим образом:

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

тогда они не были бы совместимыми с присвоением. Вы можете обойти эту проблему, объявив тип, TDoubleArray = array of Double. Однако, если вы используете общий массив, вы можете использовать общие классы контейнеров, такие как TList<T>, чем возвращаемые значения типа TArray<T>, которые вы можете легко использовать.

Я знаю, что это не совсем тот вопрос, который вы задали, но у меня такое чувство, что оно может быть полезно для вас.

+0

+1 Я использую пакет TDavis Sparse, спасибо за советы! Я вообще не знал о TArray , рад, что узнал об этом, прежде чем я потратил часы на отладку проблем с совместимостью. Это может не отвечать на поставленные точные вопросы, но он дает много советов (и, скорее всего, сэкономит массу времени в целом). Я очень ценю это. –

+0

Какие процедуры в CSparse вы планируете вызывать. Я решаю LU. Мне также интересно, как вы планируете создавать свою матрицу. Я собираю триплеты, а затем конвертирую их в разреженное хранилище столбцов. Это преобразование довольно сложно. Я действительно сделал это, поместив некоторый код Тима в Delphi. Если это происходит в коммерческом продукте, надеюсь, вы не забыли заплатить Тиму за лицензию. Его код действительно превосходный. –

+0

Я также буду использовать LU решения, и в будущем будет предоставлена ​​матрица A, и мне придется «создать» требуемые векторы Ap, Ai и т. Д. Сами матрицы будут в среднем довольно большими (n = 10^5). Мне действительно нравится его код, но опять же я люблю численный анализ/интеграцию, линейную алгебру и программирование. –