Delphi:Sierpinski三角形:程序没有完全运行

Emi*_*l B 6 delphi

我不明白为什么这段代码会这样:

在此输入图像描述 只有左下角的三角形似乎在绘制,但内三角形只出现在顶部和右下三角形的第一个深度.我希望程序是递归的,但它是某种原因导致我的糟糕的编程技巧不是递归的.我真的想要了解我做错了什么.

implementation

{$R *.dfm}

var
  count : integer = 0;

procedure DrawTriangle(aCanvas: TCanvas;x,y,size : extended;n : integer);
var
  h : extended;
  w : extended;
  i : integer;
  x1,x2,x3,y1,y2,y3 : extended;
begin
    w := size;
    h := size;
    x1 := x;
    y1 := y;
    //ShowMessage(FloatToStr(w)+' '+FloatToStr(h));
  if aCanvas<>nil then
  try
    //1st - left
    aCanvas.MoveTo(Round(x1),Round(y1));
    aCanvas.LineTo(Round(x1+w*2),Round(y1));
    aCanvas.LineTo(Round(x1+w),Round(y1-h));
    aCanvas.LineTo(Round(x1),Round(y1));
    //2nd - right
    x2 := x1+w*2;
    y2 := y1;
    aCanvas.MoveTo(Round(x2),Round(y2));
    aCanvas.LineTo(Round(x2+w*2),Round(y2));
    aCanvas.LineTo(Round(x2+w),Round(y2-h));
    aCanvas.LineTo(Round(x2),Round(y2));
    //3rd - top
    x3 := x2-w;
    y3 := y2-h;
    aCanvas.MoveTo(Round(x3),Round(y3));
    aCanvas.LineTo(Round(x3+w*2),Round(y3));
    aCanvas.LineTo(Round(x3+w),Round(y3-h));
    aCanvas.LineTo(Round(x3),Round(y3));

    //Run itself
    inc(count);
    if count < n then
    begin
      DrawTriangle(aCanvas,x1,y1,size/2,n);
      DrawTriangle(aCanvas,x2,y2,size/2,n);
      DrawTriangle(aCanvas,x3,y3,size/2,n);
    end;
  except
    on e: exception do raise e;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  size : extended;
  i : integer;
  x,y : extended;
begin
  size := 100;
  x := 100;
  y := 400;
  DrawTriangle(Image1.Canvas,x,y,size,10);
end;
end.
Run Code Online (Sandbox Code Playgroud)

And*_*and 8

问题是你绘制第一个三角形,然后你调用DrawTriangle绘制左下角的部分,这样做,然后调用DrawTriangle绘制新的左下角部分,依此类推,直到count达到n.然后我们一次将一个过程返回到原始过程,并且在每个步骤中我们仅绘制剩余的两个三角形一次.

以下逻辑按预期工作.(删除全局count变量.)

procedure DrawTriangle(aCanvas: TCanvas; x, y, size: extended; n: integer);
var
  h: extended;
  w: extended;
  x1, x2, x3, y1, y2, y3: extended;
begin
  w := size;
  h := size;
  x1 := x;
  y1 := y;

  //1st - left
  aCanvas.MoveTo(Round(x1), Round(y1));
  aCanvas.LineTo(Round(x1+w*2), Round(y1));
  aCanvas.LineTo(Round(x1+w), Round(y1-h));
  aCanvas.LineTo(Round(x1), Round(y1));
  //2nd - right
  x2 := x1+w*2;
  y2 := y1;
  aCanvas.MoveTo(Round(x2), Round(y2));
  aCanvas.LineTo(Round(x2+w*2), Round(y2));
  aCanvas.LineTo(Round(x2+w), Round(y2-h));
  aCanvas.LineTo(Round(x2), Round(y2));
  //3rd - top
  x3 := x2-w;
  y3 := y2-h;
  aCanvas.MoveTo(Round(x3), Round(y3));
  aCanvas.LineTo(Round(x3+w*2), Round(y3));
  aCanvas.LineTo(Round(x3+w), Round(y3-h));
  aCanvas.LineTo(Round(x3), Round(y3));

  //Run itself
  if n > 0 then
  begin
    DrawTriangle(aCanvas, x1, y1, size/2, n-1);
    DrawTriangle(aCanvas, x2, y2, size/2, n-1);
    DrawTriangle(aCanvas, x3, y3, size/2, n-1);
  end;
end;
Run Code Online (Sandbox Code Playgroud)

我把它作为练习来弄清楚它为何起作用.

还要注意我删除了完全不必要的try..except块.

最后,您可以更有效地编写代码,正如Rufo爵士所证明的那样.


Del*_*ics 6

您尝试限制递归深度包含错误.

您绘制3个内部三角形中的每一个并增加计数,然后再次调用DrawTriangle以在这些三角形内绘制三角形.每个调用增量再次计数 ,这意味着计数不反映递归的深度,而只是反映了调用DrawTriangle的次数.指定限制为10后,您会发现在结果中已绘制了10组三角形.

不是递增计数以跟踪递归深度,而是应该为每次调用递减 n,直到n = 0.

为了使这个意图更清楚,你可以使用一个嵌套过程,其中外部调用接受内部嵌套过程指定的最大递归级别数,执行实际递归,指示剩余的级别数,并递减递归调用本身的数量:

procedure DrawTriangle(aCanvas: TCanvas;x,y,size : extended; maxLevels: integer);

  procedure Draw(x,y,size: extended; levelsLeft: integer);
  var
    h : extended;
    w : extended;
    i : integer;
    x1,x2,x3,y1,y2,y3 : extended;
  begin
    w := size;
    h := size;
    x1 := x;
    y1 := y;

    //1st - left
    aCanvas.MoveTo(Round(x1),Round(y1));
    aCanvas.LineTo(Round(x1+w*2),Round(y1));
    aCanvas.LineTo(Round(x1+w),Round(y1-h));
    aCanvas.LineTo(Round(x1),Round(y1));

    //2nd - right
    x2 := x1+w*2;
    y2 := y1;
    aCanvas.MoveTo(Round(x2),Round(y2));
    aCanvas.LineTo(Round(x2+w*2),Round(y2));
    aCanvas.LineTo(Round(x2+w),Round(y2-h));
    aCanvas.LineTo(Round(x2),Round(y2));

    //3rd - top
    x3 := x2-w;
    y3 := y2-h;
    aCanvas.MoveTo(Round(x3),Round(y3));
    aCanvas.LineTo(Round(x3+w*2),Round(y3));
    aCanvas.LineTo(Round(x3+w),Round(y3-h));
    aCanvas.LineTo(Round(x3),Round(y3));

    //Run itself
    if (levelsLeft > 0) then
    begin
      Draw(x1, y1, size/2, levelsLeft - 1);
      Draw(x2, y2, size/2, levelsLeft - 1);
      Draw(x3, y3, size/2, levelsLeft - 1);
    end;
  end;

begin
  if Assigned(aCanvas) then
    Draw(x, y, size, maxLevels);
end;
Run Code Online (Sandbox Code Playgroud)

这还允许以更清晰的方式测试前置条件,在这种情况下当前限制为确保已经指定了画布,但是还可以包括在启动递归调用之前规范化或验证可能需要的其他参数.

由于原始参数保留在嵌套过程的作用域中,因此也意味着您可以将对递归过程的调用中的参数限制为仅在每次调用时实际更改的参数.(我已经在我的答案中更新了代码以加入此代码).

顺便提一下,try..except简单地引发任何捕获的异常完全等同于完全没有try..except阻塞,所以我从这个实现中删除了它.

此外,如果size的值达到某个最小值(内部三角形不明显,例如size <2),您可能还需要考虑添加一个附加条件来暂停递归.