arraysfunctiondelphi

Not able to get average from function


I wrote a function to try and determine the average of all the values entered into an array however it keeps spitting out a value of around 50. There is an easy way around this but I'd really like to know what I'm going wrong. Bear in mind that I'm still learning Delphi so I still have a lot to learn.

I was trying to write a function that takes an array and determines the Average of all the values in the array by adding them all together and dividing the end product by the number of values in the array.

Function Average(x : array of Integer; var y : Integer): Real;
var
  e : Integer;
  fTotal : Real;
begin

  //Initializing the variable

  fTotal := 0;
  Result := 0;

  //Adding values to fTotal
  
  for e := 1 to y do
  begin
  
    fTotal := fTotal + x[e];
    
  end;

  //Determining Average

  fTotal := fTotal / y;

  //Assigning Average to Result

  Result := fTotal;

  //End of Function
  
end;

x is the array from which the values are gotten. y is the amount of values stored in the array.

upon running the code however I have found that no matter what the values in the array are the result of the function always seems to be around 50.

I tried using these values.

arrVal : array[1..50] of integer;
var
  iCount : Integer;
  fAverage : Real;
begin
  arrVal[1] := 70;
  arrVal[2] := 80;
  arrVal[3] := 90;

  iCount := 3;  //The amount of values in the array

  fAverage := Average(arrVal, iCount);

I ran the code and the function gave me the value of 50. I tested the values and the actual average is 80.


Solution

  • You have declared

    function Average(x: array of Integer; var y: Integer): Real;
    

    First, x is here an open array parameter. It's a "magical" kind of parameter that will accept a static array, a dynamic array, or an open array literal.

    In any case, it contains information about the length of the array, so you don't need your y parameter at all, so you should remove it. (And even if this was not the case, you should not have used var for it.)

    Also, you need to use const x: array of Integer for performance reasons -- and not to risk a stack overflow.

    Inside the body of the function, the indices of x range from Low(x) to High(x), and the length of x is Length(x).

    In this case, when x is an open array parameter, it is guaranteed that Low(x) = 0. And High(x) = Length(x) - 1, so if x is empty, High(x) = -1 making a loop like for var i := 0 to High(x) do perform no iterations.

    So, you should write

    function Average(const X: array of Integer): Double;
    begin
      if Length(X) = 0 then
        raise Exception.Create('Cannot compute average of empty array.');
      Result := 0;
      for var i := 0 to High(X) do
        Result := Result + X[i];
      Result := Result / Length(X);
    end;
    

    The Length(X) = 0 check is done for purely mathematical reasons: A list of zero numbers doesn't have an average. (But the sum of such a list is 0 and the product is 1.)

    In fact, you can even use a for in loop:

    function Average(const X: array of Integer): Double;
    begin
      if Length(X) = 0 then
        raise Exception.Create('Cannot compute average of empty array.');
      Result := 0;
      for var a in X do
        Result := Result + a;
      Result := Result / Length(X);
    end;
    

    In theory, the Result variable may overflow during the computation. To avoid this, you can do instead

    function Average(const X: array of Integer): Double;
    begin
      if Length(X) = 0 then
        raise Exception.Create('Cannot compute average of empty array.');
      Result := 0;
      const F = 1 / Length(X);
      for var a in X do
        Result := Result + F * a;
    end;
    

    And in practice, there's hardly any need to reinvent the wheel (the Math unit):

    function Average(const X: array of Integer): Double;
    begin
      Result := SumInt(X) / Length(X);
    end;