Błąd 'Type mismatch' przy ustalaniu długości tablicy

0

Witam,

Chciałem napisać procedurę, która przy wykorzystaniu pętli FOR, wyświetliłaby mi z góry do dołu wszystkie elementy tablicy , jednak nie jestem wstanie skrócić tablicy po każdej iteracji.

Tak wygląda procedura:

procedure TForm1.ArrToStr(arr: array of double);
var
  I: integer;
  res: string;
begin
  res:= '';
  if High(arr)<0 then
  ShowMessage('Wprowadzona tablica jest pusta') else
  begin
    For I:= High(arr) downto 0 do
      begin
        res:= res + FloatToStr(arr[high(arr)]) + #10#13;
        SetLength(arr,High(arr)-1);
      end;
  end;
  ShowMessage(res);
end;

I dla funkcji SetLength kompilator wskazuje błąd:

Error: Type mismatch

Rozumiem, że nie zgadzają się typy danych, ale parametr arr to tablica i poza tą procedurą funkcja ta działa dla tablic.

Od razu mówię, że problem jest z pierwszym argumentem (arr), bo jak za drugi ustawię np. 2 to jest tak samo. W dodatku jak dodałem na zewnątrz jakąś inną tablicę i wstawię ją zamiast parametru do procedury to też wszystko działa.

2

Używasz tzw. "open array" dla parametru arr, a to w połączeniu z SetLength wewnątrz metody nie jest dozwolone;

type
  TMyArray = array of Double;

{...}

procedure TForm1.ArrToStr(AArr: TMyArray);

Wszystko będzie działało, jeśli zadeklarujesz sobie osobny typ i jego użyjesz dla parametrów; Nie wiem co Ty kombinujesz z tą macierzą, jednak używanie SetLength w tym przypadku jest kompletnie bezcelowe; Macierze dynamiczne zwalniane są z automatu, więc o wycieki pamięci nie musisz się martwić, bo ich nie będzie;

PS: Wspomniane "otwarte macierze" istnieją po to, aby można było podać w parametrze dowolną macierz (o rozmiarze statycznym lub dynamicznym), dowolnie indeksowaną, byle tylko przechowywała była tego samego typu; Taki zabieg wyklucza konieczność rzutowania i nie powoduje błędów, ostrzeżeń czy hintów ze strony kompilatora.

2
procedure TForm1.ArrToStr(arr:array of double); // Możesz wywołać z tablicą z palca lub ze zmienną, ale zawsze będzie to kopia
type TDoubleArray=array of double;
procedure TForm1.ArrToStr(var arr:TDoubleArray); // możesz wywołać z dynamiczną tablicą, której rozmiar można zmienić wewnątrz funkcji
  1. Po kiego robisz to po każdym kroku, przetwórz całość po czym zmniejsz rozmiar do zera.
  2. SetLength(arr,High(arr)-1); - zmniejszasz tablicę o dwa elementy.
0
type
  TMyArray = array of double;

{...}

procedure TForm1.ArrToStr(arr: Tmyarray);
var
  I: integer;
  res: string;
begin
  res:='';
  if High(arr)<0 then
  ShowMessage('Wprowadzona tablica jest pusta') else
  begin
    For I:=High(arr) downto 0 do
      begin
        res:=res+FloatToStr(arr[high(arr)])+#10#13;
        SetLength(arr,High(arr)-1);
      end;
  end;
  ShowMessage(res);
end;
furious programming napisał(a):

Wszystko będzie działało, jeśli zadeklarujesz sobie osobny typ i jego użyjesz dla parametrów;

Teraz z kolei dostaję komunikat:

Error: function header doesn't match any method of this class "TForm1.ArrToStr(TMyArray),"

furious programming napisał(a):

Nie wiem co Ty kombinujesz z tą macierzą, jednak używanie SetLength w tym przypadku jest kompletnie bezcelowe; Macierze dynamiczne zwalniane są z automatu, więc o wycieki pamięci nie musisz się martwić, bo ich nie będzie;

Tak po prostu sobie ćwiczę tablice. Używam SetLength aby uciąć ostatni wyraz macierzy. Zauważ, że liczę od ostatniego wyrazu do pierwszego, a wynik powiększam o ostatni wyraz. Gdybym nie uciął ostatniego wyrazu, to by wszystkie wyrazy były równe ostatniemu.

@_13th_Dragon Dzięki za spostrzegawczość.

1

Jeśli zmieniasz cokolwiek w samym nagłówku metody to zmieniaj w dwóch miejscach - w deklaracji klasy oraz w definicji samej metody; Błąd jasno informuje, że nagłówek tej metody nie pasuje do żadnej metody istniejącej w klasie TForm1;

Zauważ, że liczę od ostatniego wyrazu do pierwszego, a wynik powiększam o ostatni wyraz. Gdybym nie uciął ostatniego wyrazu, to by wszystkie wyrazy były równe ostatniemu.

Bo niepotrzebnie napaliłeś się na funkcję High - przecież masz indeks bieżącego elementu w iteratorze I; Zapisz to w poniższy sposób:

for I := High(Arr) downto Low(Arr) do
  Res += FloatToStr(Arr[I]) + #10#13;
0

OK, Teraz poszło! :)
A mogę jeszcze prosić o wskazówkę, jak najlepiej uciąć pierwszy element macierzy?

1

Ja napisałbym to tak:

class function TForm1.ArrToStr(const arr:Tmyarray):String;
var I:Integer;
begin
  SetLength(Result,0);
  for I:=0 to Length(Arr)-1 do Result:=Concat(FloatToStr(arr[I]),#10#13,Result);
end;

Natomiast w miejscu wywołania możesz np wywołać jako: ShowMessage(ArrToStr(tb));

2

A mogę jeszcze prosić o wskazówkę, jak najlepiej uciąć pierwszy element macierzy?

@KaRolthas - zostaw te elementy w spokoju i nic nie ucinaj; Bo po co to robić? Koniecznie musisz pozbywać się tych komórek? Żeby przeiterować po wszystkich elementach macierzy, można skorzystać z co najmniej trzech sposobów, jeśli chodzi o iterowanie od pierwszego do ostatniego elementu:

1. Standardowy iterator (pętla For)

var
  I: Integer;

{...}

for I := Low(Arr) to High(Arr) do
  // iterator I zawieta indeks bieżącego elementu

2. Z użyciem pętli For In

var
  dblNumber: Double;

{...}

for dblNumber in Arr do
  // dblNumber zawiera liczbę z bieżącej komórki macierzy

3. Z użyciem pętli While i cudowaniem z SetLength

while Length(Arr) > 0 do
begin
  // Arr[0] to bieżąca komórka macierzy
  Move(Arr[1], Arr[0], High(Arr) * SizeOf(Double)); // pisane z głowy
  SetLength(Arr, High(Arr));
end;

Oczywiście ten ostatni przykład nie ma żadnego sensu, bo wykonuje zbędne rzeczy (niepotrzebną zmianę rozmiaru macierzy), w zamian za wykluczenie konieczności standardowego iterowania po elementach;

Jeśli ile iterujemy od pierwszego do ostatniego elementu oraz używamy macierzy tylko do odczytu - polecam używanie drugiego sposobu, czyli pętli For In; W innych przypadkach (np. iterowania od końca) odpowiednim jest pierwszy opisany sposób; Rozwiązań jest o wiele więcej (biorąc pod uwagę wszystkie pętle - For i jej odmiany, While Do oraz Repeat Until), jednak wszystko zależy od konkretnego przypadku - dlatego tych pętli jest tyle.

0

@_13th_Dragon Niestety jest dokładnie tak samo, czyli od tyłu.

1

Myślałem że właśnie tak chcesz, więc zamień sobie na:

for I:=0 to Length(Arr)-1 do Result:=Concat(Result,FloatToStr(arr[I]),#10#13);
0

@_13th_Dragon Tak, już sam się skapnąłem. Po prostu nie znałem funkcji concat.

Dzięki @furious programming za wyjaśnienie tych pętli.

Do zamknięcia.

1
KaRolthas napisał(a)

Na początku chciałem od pierwszego do ostatniego, ale nie umiałem uciąć pierwszego wyrazu, więc wstawiłem w pierwszym poście procedurę na od ostatniego do pierwszego, ale jak się okazuje nie muszę nic ucinać i już wiem jak to zrobić.

Skoro więc nie musisz nic ucinać, a na dodatek nie musisz pobierać elementów od końca - użyj pętli For In:

function TForm1.ArrToStr(AArray: array of Double): String;
var
  dblNumber: Double;
begin
  Result := '';

  for dblNumber in AArray do
    Result += FloatToStr(dblNumber) + #10#13;
end;

Ta pętla oraz operator += ukradziony z języka C (ale obsługiwany przez FPC na domyślnych ustawieniach kompilatora) pozwoli na krótki, zwięzły i czytelny zapis, natomiast użycie operatorów do konkatenacji łańcuchów jest szybsze od funkcji Concat; Jeżeli podana w parametrze AArray macierz będzie pusta - metoda zwróci pusty ciąg znaków;

PS: Pobieranie elementów od początku do końca da zupełnie inny rezultat, niż ich pobieranie od końca do początku;
PPS: Na pewno potrzebujesz separatora #10#13? A nie #13#10, jak w plikach tekstowych dla Windows?

0

Działa idealnie.

Czy w tym przypadku mogę już użyć open array?

Czy dzięki operatorowi +=

Result+=x;

oznacza to samo co? Result:= Result + x;

Czy od tego nie jest funkcja `Inc`?

A te separatory się czymś różnią? Znalazłem to gdzieś w necie.
1

Czy w tym przypadku mogę już użyć open array?

No pewnie - podany przeze mnie przykład w poprzednim poście implementuje właśnie "open array";

Co do operatora += - tak, to jest skrócona wersja zwykłego przypisania; Są jeszcze inne - -=, /= i *=, zgadnij do czego; Natomiast jeśli potrzebujesz dodać wartość całokowitoliczbową do bieżącej wartości jakiejś zmiennej, warto używać procedur Inc i Dec;

A te separatory się czymś różnią?

No tak - kolejnością znaków :]

Zobacz tutaj: https://en.wikipedia.org/wiki/Newline#Representations (patrz na Windows).

1 użytkowników online, w tym zalogowanych: 0, gości: 1