Delphi - 计算列表中项目的最小最大值和平均值

时间:2013-09-07 16:03:44

标签: delphi

我正在构建一个单独的应用程序来计算列表中的最小值和平均值。

实际上是温度。所以我认为我几乎是正确的,但有2个错误。

var
  Count, Average, Sum,i, Max, Min, K   : Integer;
  Temperatures : Array of Integer;
  NoItems : Double;
begin
  Count := 0;
  Sum := 0;
  Max := 0;
  Min := 0;
  Average := 0;

  Count := lstTemp.Items.Count;

  {Calculate Sum of Values in the list}
  for i := 0 to Count - 1 do
    Sum := Sum + StrToInt(lstTemp.Items[i]);

  {Calculate Min and Max}
  SetLength(Temperatures,Count);
  for K:=0 to Count-1 do
    Temperatures[K] := lstTemp.Items[K];
  if (Temperatures[K] > Max) then
    Max := Temperatures[K];
  if (Temperatures[K] < Min) then
    Min := Temperatures[K];
  {Calculate Average}
    Average := Sum / Count;

  edtAvg.Text:=IntToStr(Average); //Display Average
  edtAvg.Text:=IntToStr(Min); //Display Minimum Temp.
  edtAvg.Text:=IntToStr(Max); //Display Maximum Temp.
end; 

所以2个错误是 错误:不兼容的类型:得到“AnsiString”预期“LongInt” 这是针对平均值:=总和/计数; 错误:不兼容的类型:得到“Set Of Byte”预期“Double” 此错误适用于Temperatures [K]:= lstTemp.Items [K];

任何想法如何解决这个问题?

Sum和Count都是整数,所以我不知道为什么它不起作用!

由于

6 个答案:

答案 0 :(得分:7)

存在许多问题。 第一次,当你写

for K:=0 to Count-1 do
Temperatures[K] := lstTemp.Items[K];
if (Temperatures[K] > Max) then
Max := Temperatures[K];
if (Temperatures[K] < Min) then
Min := Temperatures[K];

你实际上是

for K:=0 to Count-1 do
  Temperatures[K] := lstTemp.Items[K];

if (Temperatures[K] > Max) then
  Max := Temperatures[K];
if (Temperatures[K] < Min) then
  Min := Temperatures[K];

这是胡说八道。您希望所有这些行成为for循环的一部分:

for K:=0 to Count-1 do
begin
  Temperatures[K] := lstTemp.Items[K];
  if (Temperatures[K] > Max) then
    Max := Temperatures[K];
  if (Temperatures[K] < Min) then
    Min := Temperatures[K];
end;

第二,为了使此算法有效,MinMax)的初始值需要比列表中的值更大(更小)。这可能适用于Max := 0,但可能不适用于Min := 0。显然,在运行循环之前,需要将Min设置为一个非常大的值。您可以使用的最佳值是最高可能的带符号32位整数值,即2 ^ 31 - 1,这是MaxInt常量的值。

<强>第三

Temperatures[K] := lstTemp.Items[K];

可能是错的。 Temperatures整数的数组,而lstTemp.Items[K]字符串(至少根据StrToInt(lstTemp.Items[i])),所以你需要< / p>

Temperatures[K] := StrToInt(lstTemp.Items[K]);

第四,您将Average声明为integer,但它必须是一个浮点数(显然),如real或{{ 1}}。

<强>第五

double

在技术上不正确,但很可能不会做你想要的。

第六次,虽然不是错误,但您无需初始化edtAvg.Text:=IntToStr(Average); //Display Average edtAvg.Text:=IntToStr(Min); //Display Minimum Temp. edtAvg.Text:=IntToStr(Max); //Display Maximum Temp. CountAverage。最后,您只需要一个0循环。

答案 1 :(得分:2)

(至少在Delphi 2010中 - 单位数学)有一个函数可以计算一步中的平均值和标准差,以及返回数组中最小值和最大值的函数。 BTW,Mean是所有值的算术平均值,是正确的术语。 (我复制了一个我正在研究并修改为你的例子的例子 - 至少编译它):

type
  a = array of double;
var
  Temperatures : a;
  Average,stddev3, Max, Min : extended; 
  // Compiler insists on extended for these properties
begin
   Max := Math.MaxValue(Temperatures);
   Min := Math.MinValue(Temperatures);
   Math.MeanAndStdDev(Temperatures ,Average,stddev3);
end;

对于数组使用中的最大值(它采用double数组并返回double):

function MaxValue(const Data: array of Double): Double;

对于最小值,请使用相应的:

function MinValue(const Data: array of Double): Double;

我同意平均值不能是整数,但整数数组有两个相似的函数:

function MinIntValue(const Data: array of Integer): Integer; and
function MaxIntValue(const Data: array of Integer): Integer;

答案 2 :(得分:1)

首先,考虑使用StrToIntDef(String To Integer with Default value)而不是StrToInt(String to Integer),这将产生以下内容......

value := StrToIntDef('Abcdef', 0); // value will be zero

VS

value := StrToInt('Abcdef'); // exception

但问题是你想要温度的整数或浮点值吗? (例如1或1.6?)如果你想要浮点值,可以使用StrToFloatDef ...

其次,我已经看到很多使用Delphi的毕业生犯了这个错误,尝试总是使用开始和结束,它会有所帮助...因为它使得你在if / for /中做的很清楚虽然你打算在外面做什么..

for i := 0 to lstTemp.Items.Count - 1 do
begin
  // Sum all the items in the list
  Sum := Sum + StrToIntDef(lstTemp.Items[i], 0);
end;

接下来你的数组有点无意义,SetLength和添加项目位是好的,但它不是很实用,当你可以只使用列表中的项目。您需要做的就是挂上最大值和最小值。

然后你的最后一个问题是,平均值不会是一个整数,它会有一个小数部分。例如。 5除以2是2.5,而不是2而不是3.您可以使用trunc返回整数部分,或者更改Average以使其成为浮点数...

for K:=0 to lstTemp.Items.Count-1 do
begin
  if (StrToIntDef(lstTemp.Items[K], 0) > Max) then
  begin
    Max := StrToIntDef(lstTemp.Items[K], 0);
  end;
  if (StrToIntDef(lstTemp.Items[K], 1000) < Min) then // note, really high number
  begin
    Min := StrToIntDef(lstTemp.Items[K], 1000);
  end;
end;

{Calculate Average}
Average := Trunc(Sum / Count); // do you really want to trunc this? I suspect not.

if Min = 1000 then // just incase
begin
  Min := 0;
end;

您将面临的最后一个问题是您始终设置相同文本框的文本...

edtAvg.Text:=IntToStr(Average); //Display Average
edtMin.Text:=IntToStr(Min); //Display Minimum Temp. (I assume this is supposed to be edtMin)
edtMax.Text:=IntToStr(Max); //Display Maximum Temp. (I assume this is supposed to be edtMax)

我想我最后的改进是注意到你只需要一个for循环......

for K:=0 to lstTemp.Items.Count-1 do
begin
  // Sum all the items in the list
  Sum := Sum + StrToIntDef(lstTemp.Items[K], 0);

  if (StrToIntDef(lstTemp.Items[K], Low(Integer)) > Max) then // A really low value
  begin
    Max := StrToIntDef(lstTemp.Items[K], Low(Integer));
  end;
  if (StrToIntDef(lstTemp.Items[K], High(Integer)) < Min) then // A really high value
  begin
    Min := StrToIntDef(lstTemp.Items[K], High(Integer));
  end;
end;

答案 3 :(得分:1)

0909EM的回复做得很好,但我有一些分歧。首先,我不相信有必要设置任何哨兵价值;只需使用第一个温度值。其次,如果我们在每一行中放置一个开头和结尾如果声明我们接近类似COBOL的英语冗长级别。事实上,这个简单的问题需要很多代码,这是一个令人遗憾的耻辱。第三,我会使用StrToIntDef。请记住这些来自禅宗的Python(如果你不了解Python,我就不在乎;每个人都应该记住它,至少在我们得到一个Intersing的I Ching之前):

  

错误绝不应该以无声方式传递。

     

除非明确沉默。

如果用户将不正确的数据传递到温度统计程序,StrToIntDef将静默地将这些值转换为零,这是一种意外和不希望的行为。调用者将获得他们认为可以接受的答案(因为没有错误),但是会有不正确的值(尤其是平均值)。让程序爆炸是一件好得多的事情,因此测试会显示错误的输入。

我也用For ... in替换For循环。我把它撞在了一起:

program temps;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, System.Classes, Generics.Collections, Math;

Var
  someTemps : TStringList;

Procedure TempStats(temperatures : TStringList);
  Var
    temps                      : TList<Real>;
    minTemp, maxTemp, sumTemps : Real;
    numTemps                   : Integer;
    tempStr                    : String;
    temp                       : Real;
    avgTemp                    : Real;

Begin
  numTemps := temperatures.Count;

  If numTemps > 0 then
    Begin
      temps := TList<Real>.Create;

      For tempStr in temperatures Do
        temps.Add(StrToFloat(tempStr));

      minTemp := temps[0];
      maxTemp := temps[0];
      sumTemps := 0;

      For temp in temps Do
        Begin
          minTemp := Min(minTemp, temp);
          maxTemp := Max(maxTemp, temp);
          sumTemps := sumTemps + temp;
        End;

      avgTemp := sumTemps / numTemps;

      WriteLn(avgTemp:0:2);
      WriteLn(minTemp:0:2);
      WriteLn(maxTemp:0:2);
      temps.Free;
    End
  Else
    WriteLn('No temperatures passed.');
End;


Begin
  someTemps := TStringList.Create;
  someTemps.AddStrings(TArray<String>.Create('72', '93', '84', '76', '82'));
  TempStats(someTemps);
  ReadLn;
  someTemps.Clear;
  TempStats(someTemps);
  someTemps.Free;
  ReadLn;
end.

答案 4 :(得分:1)

关于如何解决此问题的最重要的想法是阅读您的错误消息正确。在上一个问题上你评论过:&#34;错误是说它是一个重载函数或某些东西&#34;。这种态度不会帮助你理解这个问题。您需要正确读取错误消息。

在这个问题中,您将对错误进行以下描述:

  

所以2个错误是错误:不兼容的类型:得到&#34; AnsiString&#34;预期&#34; LongInt&#34;这是针对平均值:=总和/计数;错误:不兼容的类型:得到&#34; Set By Byte&#34;预期&#34; Double&#34;此错误适用于Temperatures [K]:= lstTemp.Items [K];

但是,根据提供的代码,说明与您应该看到的错误不对应。

看起来你没有你的错误,只是盲目地开始做出改变,希望你不小心做正确的事情。因为您没有读取错误,所以您没有注意到它们发生了变化。因此,当您向我们寻求帮助时,您使用新代码提供了旧错误,反之亦然。

如果您确实正确阅读错误消息,您可能已经能够自己解决问题。至少,您可以使用与代码实际匹配的描述来提出更好的问题。

平均值:=总和/计数;

AverageSumCount都声明为Integer。您应该获取的错误消息是:&#34;不兼容的类型:整数扩展&#34;。

如果您阅读错误消息,它应该为您提供<{1}}和Integer读取的线索。

这里的问题是,在数学中,除法产生一个有理数。并且相应地,程序中的除法运算的结果不是整数。因此,您需要将Extended声明为AverageDouble

Temperatures [K]:= lstTemp.Items [K];

Extended被声明为Temperatures的数组。您还没有显示Integer的声明,但根据其他代码,它是lstTemp声明为Items的标准Delphi控件之一。因此,您应该获取的错误消息是:&#34;不兼容的类型:整数字符串&#34;。

如果您阅读错误消息,它应该会为您提供一条线索,让您做出与之前5行相同的事情。

这个错误的原因是Delphi是一个强类型&#34;语言。编译器试图阻止你犯某些错误,因为它更好地抓住它们。想象一下,如果TStrings中的一个值为lstTemp,可能会发生什么。那不能转换为整数;并会导致&#34;运行时间&#34;程序错误。

要解决此问题,您需要告诉编译器:&#34;我知道值是一个字符串,可以是任何字符串,但我希望您将其转换为整数&#34;。您可以通过调用'Hello'函数来完成此操作。注意:如果将无效字符串传递给函数,您仍会遇到运行时错误,但是通过强制显式执行转换,您可以考虑是否要对输入数据进行一些预验证。


您询问了编译器报告的错误。这只是您在编程时遇到的一种错误 - 通常最容易解决。您还会遇到逻辑错误:您的程序成功编译,但行为不正确。 Andreas's excellent answer已经涵盖了那些,所以我不会重复它们 但是,我会给你一些宝贵的建议。一旦你克服了解决编译器错误的障碍,并且能够轻松完成 - 你需要尽快:

  • 养成彻底测试代码的习惯。
  • 了解如何使用集成调试器。
  • 了解其局限性。
  • 了解其他调试技巧:记录,分析,条件前和条件后检查。

最后,作为对alcalde's rant的回应,关于没有任何简单的函数来获得Min,Max,Sum或Avg:我提供了另一种可能的实现。

基本上,咆哮是因为他远远地写了一些东西:

StrToInt

显然上面的代码不会编译,但通过一些工作我们可以实现类似的东西。

他完全正确的两个方面:(1)这个实现更清晰,更容易维护,错误的可能性更小。 (2)德尔福没有提供简单方法。

事实上,如果您遵循自上而下的设计方法,这可能是您的初始伪代码。如果不要求退款,你应该接受自上而下的设计教育。 :)
自上而下设计方法背后的重点是,您正在寻找 理想 实施。你不必担心那里有什么。如果当前的库和工具没有提供begin if (lstTemp.Count > 0) then begin edtMin.Text := lstTemp.Min; edtMax.Text := lstTemp.Max; edtAvg.Text := lstTemp.Average; end else begin ShowMessage('List is empty'); end; end; 功能,您可以编写自己的

你是程序员,你有权力

我有时喜欢称之为&#34;一厢情愿的编程&#34;。您希望如果有其他的东西到位,我可以更容易地实现这些功能,例如&#34;这个&#34;。然后你就可以实现你的愿望了。

不用多说,这里是实施。您需要使用数学单元。

Min

答案 5 :(得分:0)

lstTemp.Items [i]中有什么值? 我想这些值是整数(没有浮点数),因为你使用的是IntToStr。

平均值不能是整数。整数是一个没有浮点的数字(4个字节)。一个简单的数字,如2,3,50,1500,-100

假设Sum = 100,Count = 3。 平均值是多少?

因此,您必须使用float变量类型,例如Double。

我希望它有所帮助...