-1

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

它实际上是温度。所以我认为我几乎是正确的,但有 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”这是平均:=总和/计数;错误:不兼容的类型:得到“字节集”预期“双”此错误是针对温度 [K] := lstTemp.Items[K];

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

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

谢谢

4

6 回答 6

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;

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

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

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

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

第四,您声明Averageinteger,但它需要是浮点数(显然),例如realor double

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

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

第六,虽然没有报错,但也不需要你初始化CountAverage0。最后,您只需要一个for循环。

于 2013-09-07T16:17:47.127 回答
2

(至少在 Delphi 2010 - 单元数学中)有一个函数可以一步计算平均值和标准差,还有一个函数可以返回数组中的最小值和最大值。顺便说一句,平均值是所有值的算术平均值,是正确的术语。(我复制了一个我正在处理的示例并修改为您的示例 - 它至少可以编译):

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;

对于数组中的最大值使用(它需要一个双精度数组并返回双精度):

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

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

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

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

function MinIntValue(const Data: array of Integer): Integer; and
function MaxIntValue(const Data: array of Integer): Integer;
于 2013-09-08T00:11:19.360 回答
1

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

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

对比

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

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

其次,我看到很多使用 Delphi 的毕业生犯了这个错误,尝试始终使用 begin 和 end,它会有所帮助......因为它让你在 if/for/while 中做什么以及做什么变得非常清楚你打算在外面做..

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 和添加项目位是可以的,但它不是很有用,当你可以使用列表中的项目时。您需要做的就是抓住最大值和最小值。

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

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;
于 2013-09-07T16:44:29.267 回答
1

如何解决这个问题的最重要的想法是正确阅读您的错误消息。在上一个问题上您评论说:“错误是说它是一个重载的函数或其他东西”。这种态度不会帮助你理解问题。您需要正确阅读错误消息。

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

所以2个错误是错误:不兼容的类型:得到“AnsiString”预期“LongInt”这是平均:=总和/计数;错误:不兼容的类型:得到“字节集”预期“双”此错误是针对温度 [K] := lstTemp.Items[K];

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

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

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

平均值:=总和/计数;

Average,Sum并且Count都被声明为Integer. 您应该得到的错误消息是:“不兼容的类型:整数扩展”。

如果您阅读了错误消息,它应该为您提供阅读的线索IntegerExtended

这里的问题是,在数学中,除法产生一个有理数。相应地,程序中除法运算的结果不是整数。所以你需要声明AverageDoubleor Extended

温度[K] := lstTemp.Items[K];

Temperatures被声明为一个数组Integer。您没有显示 的​​声明lstTemp,但根据其他代码,它是已Items声明为的标准 Delphi 控件之一TStrings所以你应该得到的错误信息是:“不兼容的类型:整数字符串”。

如果您阅读了错误消息,它应该会为您提供与之前 5 行相同的操作的线索。

出现此错误的原因是 Delphi 是一种“强类型”语言。编译器试图防止您犯某些类型的错误,因为最好尽早发现它们。想象一下如果其中的值之一是会发生lstTemp什么'Hello'。那不能转换为整数;并且会在您的程序中导致“运行时”错误。

要解决此问题,您需要告诉编译器:“我知道该值是一个字符串,可以是任何字符串,但我希望您将其转换为整数”。你可以通过调用StrToInt函数来做到这一点。注意:如果将无效字符串传递给函数,您仍然会收到运行时错误,但是通过强制显式进行转换,您可以考虑是否要对输入数据进行一些预验证。


您询问了编译器报告的错误。这只是你在编程时会遇到的一种错误——通常是最容易解决的。您还会遇到逻辑错误:您的程序编译成功,但行为不正确。安德烈亚斯的优秀答案已经涵盖了这些,所以我不会重复它们。
不过,我会给你一些宝贵的建议。一旦您克服了解决编译器错误的障碍,并且能够轻松地做到这一点 - 您需要尽快:

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

最后,作为对alcalde 抱怨没有任何简单的函数来获取 Min、Max、Sum 或 Avg 的回应:我提供了另一种可能的实现。

基本上,咆哮是关于他宁愿写一些类似以下内容的事实:

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;

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

他在两个方面是完全正确的:(1)这个实现会更干净,更容易维护并且出错的机会更少。(2) Delphi 没有提供一种方法来简单地做到这一点。

事实上,如果您遵循自上而下的设计方法,这可能是您最初的伪代码。应该教你自上而下的设计,如果不是要求你退款。:)
自顶向下设计方法背后的重点是您正在寻找一个理想的实现。您不必担心存在/不存在什么。如果当前的库和工具不提供Min功能,您可以编写自己的.

你是程序员,你有力量

我有时喜欢称其为“一厢情愿的编程”。您希望如果有其他东西,我可以像“this”那样更轻松地实现该功能。然后你去实现你的愿望。

事不宜迟,这里是实现。您将需要使用数学单元。

type
  { We will call existing functions that take TDoubleArray as input }
  TDoubleArray = array of Double;

  TStringsHelper = class(TStrings)
  { A useful class to help us convert TStrings into TDoubleArray }
  public
    class function Using(AStrings: TStrings): TStringsHelper;
    function AsDoubleArray: TDoubleArray;
  end;

{ TStringsHelper }

function TStringsHelper.AsDoubleArray: TDoubleArray;
var
  LoopI: Integer;
begin
  SetLength(Result, Count);
  for LoopI := 0 to Count - 1 do
  begin
    Result[LoopI] := StrToFloat(Strings[LoopI]);
  end;
end;

class function TStringsHelper.Using(AStrings: TStrings): TStringsHelper;
begin
  Result := TStringsHelper(AStrings);
end;


var
  LTemperatures: TDoubleArray;
begin
  { This code is almost the same as our "ideal" implementation }
  if (lstTemp.Items.Count > 0) then
  begin
    LTemperatures := TStringsHelper.Using(lstTemp.Items).AsDoubleArray;
    edtMin.Text := FloatToStr(MinValue(LTemperatures));
    edtMin.Text := FloatToStr(MaxValue(LTemperatures));
    edtMin.Text := FloatToStr(Mean(LTemperatures));
  end
  else
  begin
    ShowMessage('List is empty');
  end;
end;
于 2013-09-08T17:54:42.933 回答
1

0909EM的回复做得很好,但我有一些不同意见。首先,我认为根本不需要设置任何哨兵值;只需使用第一个温度值。其次,如果我们在每行 If 语句周围放置一个 Begin 和 End,我们将接近 COBOL 级别的英语冗长程度。事实上,这个简单的问题需要这么多代码,真是太可惜了。第三,我不会使用 StrToIntDef。记住 Python Zen Of Python 中的这些台词(我不在乎你是否不懂 Python;每个人都应该记住它,至少在我们得到 Intersimone 的易经之前):

错误永远不应该悄无声息地过去。

除非明确沉默。

如果用户将不正确的数据传递到温度统计过程中,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.
于 2013-09-07T20:44:26.910 回答
0

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

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

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

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

我希望它有帮助...

于 2013-09-07T16:19:14.730 回答