如何将IEnumerable的IEnumerable折叠为单个IEnumerable?

时间:2016-08-03 11:31:54

标签: delphi ienumerable delphi-xe7 spring4d

function TSomething.Concat<T>(const E: IEnumerable<IEnumerable<T>>): IEnumerable<T>;
begin
  Result := TConcatIterator<T>.Create(E) as IEnumerable<T>;
end;

那不能编译,因为TConcatIterator<T>只能连接两个枚举,但我需要的是一个迭代可枚举枚举的迭代器。

Haskell具有执行此操作的concat函数:

concat [[1, 2, 3], [4,5]] => [1, 2, 3, 4, 5]

Delphi版本如下所示:

class function TForm1.Concat<T>(
  const AEnumerable: IEnumerable<IEnumerable<T>>): IEnumerable<T>;
begin
  // ???
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  InnerList1: IList<Integer>;
  InnerList2: IList<Integer>;
  OuterList: IList<IEnumerable<Integer>>;
  Concated: IEnumerable<Integer>;
begin
  InnerList1 := TCollections.CreateList<Integer>([1, 2, 3]);
  InnerList2 := TCollections.CreateList<Integer>([4, 5]);
  OuterList := TCollections.CreateList<IEnumerable<Integer>>([InnerList1, InnerList2]);
  Concated := Concat<Integer>(OuterList);
end;

如何在spring4d中实现这一点?

2 个答案:

答案 0 :(得分:3)

由于Spring4D集合以.Net中的那些集合为模型,因此您正在寻找的操作/方法称为SelectMany

在1.2中,我们在TEnumerable类型中有一个静态方法,所以代码看起来像这样:

Concated := TEnumerable.SelectMany<IEnumerable<Integer>, Integer>(OuterList,
  function(x: IEnumerable<Integer>): IEnumerable<Integer>
  begin
    Result := x;
  end);

然而,这有点冗长,因此您可以轻松编写一种方法来处理将IEnumerable<IEnumerable<T>>展平为IEnumerable<T>的特殊情况:

type
  TEnumerableHelper = class helper for TEnumerable
    class function SelectMany<T>(const source: IEnumerable<IEnumerable<T>>): IEnumerable<T>; overload; static;
  end;

class function TEnumerableHelper.SelectMany<T>(
  const source: IEnumerable<IEnumerable<T>>): IEnumerable<T>;
begin
  Result := TSelectManyIterator<IEnumerable<T>, T>.Create(source,
    function(x: IEnumerable<T>): IEnumerable<T>
    begin
      Result := x;
    end);
end;

并且很容易就像这样使用它:

Concated := TEnumerable.SelectMany<Integer>(OuterList);

SelectMany操作被延迟执行并延迟评估。

答案 1 :(得分:1)

严格(=非懒惰)版本可能如下所示:

class function TForm1.Concat<T>(
  const AEnumerable: IEnumerable<IEnumerable<T>>): IEnumerable<T>;
var
  L: IList<T>;
begin
  L := TCollections.CreateList<T>;
  AEnumerable.ForEach(
    procedure(const AInnerEnum: IEnumerable<T>)
    begin
      L.AddRange(AInnerEnum);
    end);
  Result := L as IEnumerable<T>;
end;