使用Next()函数迭代嵌套列表,不使用生成器

时间:2009-10-08 14:31:36

标签: delphi iteration yield

虽然我很想在python中解决这个问题,但我仍然坚持使用Delphi。我有嵌套列表(实际上是嵌套列表作为属性的对象,但是从来没有),我希望以生成器方式迭代它们。也就是说,我想编写一个Next函数,它给出了嵌套列表描述的树叶中的下一个项目。

例如,假设我有

 [[1,2,3],[4,5],[],[6],[7,8]]

我希望连续8次调用Next()返回1..8。

如何在没有产量和生成器的语言中执行此操作?

请注意,嵌套的深度是固定的(在此示例中为2,在现实生活中为4),但欢迎解决更深入变化的更一般情况的答案。

编辑:对不起,我应该提到,这是Delphi 2007。

3 个答案:

答案 0 :(得分:2)

如果您正在使用任何具有内置枚举器的Delphi列表,则可以通过一点递归轻松完成。您的基本列表是一个数字列表,如TList<integer>。然后,您将嵌套列表实现为TList<TList<integer>>。 (我假设您有Delphi 2009或2010.如果没有,它会变得有点棘手。)

您需要的是使您自己的专用列表类来自TList<T>,并为其添加虚拟Next()函数,并为列表的枚举器添加字段。设置for..in循环时,编译器会在内部使用枚举器,但您可以手动运行它们。 Next()函数创建枚举器(如果尚未分配)并将其放入字段中,然后在其上调用MoveNext()。如果成功,请拨打GetCurrent并获取您的号码。否则,你已经完成了。 FreeAndNil枚举器,并向调用函数发出信号,告知你没有任何东西可以返回。可能最简单的方法是在Next()中放置一个 var 布尔参数,返回调用MoveNext的结果,并让调用函数检查其值。

对于较高的列表,它有点复杂,但并不多。从您刚刚设置的泛型类下降,并覆盖Next()。这个将获得一个枚举器,它枚举它所包含的列表,并返回FEnumerator.GetCurrent.Next()的值,直到该子列表用尽,然后在其枚举器上调用MoveNext。

这适用于任何深度的嵌套列表。只是不要尝试制作包含数字和列表的列表。

答案 1 :(得分:0)

方法1:“分层列表”是树,使用(或写入)树/ treenode类,并对其值进行简单的深度优先搜索。

方法2:显式地为您的分层列表类型编写深度优先搜索:

TEnumerator = RECORD
  private
    FStack : Stack of TNestedList;  //a stack, holding which level of the tree you are exploring
    FIndexStack : Stack of Integer; //a twin stack, holding the index of the child you are exploring at each layer

  public
    Init( AList : TNestedList );
    Next( var AVal : Integer ) : boolean;
end;


procedure TEnumerator.Init( AList );
begin
  SetLength(FStack, 1);
  FStack[0] := AList;
  SetLength( FIndexStack, 1 );
  FIndexStack[0] := 0;

  _GoToNextValue();
end;

procedure TEnumerator._GoToNextValue();
begin
  while FStack.notEmpty()
    and FIndexStack.last > FStack.last.length do
     begin
     //we have finished exploring FStack.last, we need to explore its next sibling :
     //pop FStack.last :
       FIndexStack.pop;
       FStack.pop;
     //go to next sibling :
       FIndexStack.last := FIndexStack.last + 1;
     end;

  //nothing left to explore :
  if FStack.empty then Exit;

  //else :
  //  dig through the layers of list until you reach a value
  while FStack.last.isAList() do
  begin
    FStack.push( FStack.last[ FIndexStack.last ] );
    FIndexStack.push( 0 );    
  end;
end;

function Next( var AVal : Integer ) : boolean;
begin
  _GoToNextValue();

  Result := FStack.notEmpty();

  if Result then
  begin
    AVal := FStack.last[ FIndexStack.last ];
    FIndexSatck.last := FIndexSatck.last + 1;
  end;
end;

你基本上明确地做了“yield”会做什么(即:保存调用堆栈的“冻结副本”,返回当前值)

用法:

LEnum : TEnumerator;

LEnum.Init( myList );
while LEnum.Next( LVal ) do
begin
  <do something>
end;

答案 2 :(得分:0)

为了解决这个问题,我最终制作了一个平面的索引列表并记住了我的位置:(在pythonesque中,因为它更短)

class Nested:
    nestedList = [[1,2,3],[4,5],[],[6],[7,8]]
    positions = []
    curr = 0

    def Setup:
        for a in nestedList:
            for b in a:
               positions.add((a,b))

    def Next:
        p = positions[curr]
        curr += 1
        return nestedList[p[0]][p[1]]

显然我的列表在迭代期间不会改变,或者这可能不起作用......