通过Lua迭代器自定义实现对c#集合的遍历

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了通过Lua迭代器自定义实现对c#集合的遍历相关的知识,希望对你有一定的参考价值。

参考技术A 在c#中定义的集合是没法在lua中用for ..in ipairs()这种方法来遍历的,下面介绍一个自定义的实现来实现

Lua 迭代器

迭代器(iterator)是一种对象,它能够用来遍历标准模板库容器中的部分或全部元素,每个迭代器对象代表容器中的确定的地址。

在 Lua 中迭代器是一种支持指针类型的结构,它可以遍历集合的每一个元素

无状态的迭代器是指不保留任何状态的迭代器,因此在循环中我们可以利用无状态迭代器避免创建闭包花费额外的代价。

每一次迭代,迭代函数都是用两个变量(状态常量和控制变量)的值作为参数被调用,一个无状态的迭代器只利用这两个值可以获取下一个元素。

这种无状态迭代器的典型的简单的例子是 ipairs,它遍历数组的每一个元素。

以下实例我们使用了一个简单的函数来实现迭代器,实现 数字 n 的平方:

function square(iteratorMaxCount,currentNumber)

   if currentNumber<iteratorMaxCount

   then

currentNumber = currentNumber+1

   return currentNumber, currentNumber*currentNumber

   end

end

for i,n in square,3,0

do

   print(i,n)

end

很多情况下,迭代器需要保存多个状态信息而不是简单的状态常量和控制变量,最简单的方法是使用闭包,还有一种方法就是将所有的状态信息封装到 table 内,将 table 作为迭代器的状态常量,因为这种情况下可以将所有的信息存放在 table 内,所以迭代函数通常不需要第二个参数

如下,自定义实现一个ipairs一样功能的迭代器

function kpairs(t)

    local index = 0

    local count = #t

    return function ()

        index = index + 1

        if index <= count then

            local v = t[index]

            if v then

                return index, v

            else

                return nil

            end

        end

  end

end

for i, v in kpairs(a) do

    print(string.format("%s=%s", i, tostring(v)))

end

如果不判断空的话,上例将输出所有元素,包括nil,而我们知道ipairs是遇到nil就认为遍历结束了

有了这些基础之后,再来看怎么自定义一个遍历c#集合的迭代器,假设list在c#中是这样申明的

class MyItem

    public string name;

    public int value;



var list = new List<MyItem>();

function xpairs(list)

    if list then

        local iter = list:GetEnumerator()

        return function()

            if iter:MoveNext() then

                return iter.Current

            else

                iter:Dispose()

                return nil

            end

        end

    end

    return nil

end

则用法如下

for item in xpairs(list) do

    print(string.format("name=%s, value=%s", item.name, item.value))

end

C#设计模式-迭代器模式

一、 迭代器(Iterator)模式

迭代器是针对集合对象而生的,对于集合对象而言,必然涉及到集合元素的添加删除操作,同时也肯定支持遍历集合元素的操作,我们此时可以把遍历操作也放在集合对象中,但这样的话,集合对象就承担太多的责任了,面向对象设计原则中有一条是单一职责原则,所以我们要尽可能地分离这些职责,用不同的类去承担不同的职责。迭代器模式就是用迭代器类来承担遍历集合元素的职责。

迭代器模式提供了一种方法顺序访问一个聚合对象(理解为集合对象)中各个元素,而又无需暴露该对象的内部表示,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。

二、 迭代器模式的结构

既然,迭代器模式承担了遍历集合对象的职责,则该模式自然存在2个类,一个是聚合类,一个是迭代器类。在面向对象涉及原则中还有一条是针对接口编程,所以,在迭代器模式中,抽象了2个接口,一个是聚合接口,另一个是迭代器接口,这样迭代器模式中就四个角色了,具体的类图如下所示:

从上图可以看出,迭代器模式由以下角色组成:

  • 迭代器角色(Iterator):迭代器角色负责定义访问和遍历元素的接口
  • 具体迭代器角色(Concrete Iteraror):具体迭代器角色实现了迭代器接口,并需要记录遍历中的当前位置。
  • 聚合角色(Aggregate):聚合角色负责定义获得迭代器角色的接口
  • 具体聚合角色(Concrete Aggregate):具体聚合角色实现聚合角色接口。

三、 迭代器模式的实现

// 抽象聚合类

using System;

public interface IListCollection
{
    Iterator GetIterator();
}

// 迭代器抽象类
public interface Iterator
{
    bool MoveNext();
    Object GetCurrent();
    void Next();
    void Reset();
}

// 具体聚合类
public class ConcreteList : IListCollection
{
    readonly int[] _collection;
    public ConcreteList()
    {
        _collection = new int[] { 2, 4, 6, 8 };
    }

    public Iterator GetIterator()
    {
        return new ConcreteIterator(this);
    }

    public int Length
    {
        get { return _collection.Length; }
    }

    public int GetElement(int index)
    {
        return _collection[index];
    }
}

// 具体迭代器类
public class ConcreteIterator : Iterator
{
    // 迭代器要集合对象进行遍历操作,自然就需要引用集合对象
    private readonly ConcreteList _list;
    private int _index;

    public ConcreteIterator(ConcreteList list)
    {
        _list = list;
        _index = 0;
    }


    public bool MoveNext()
    {
        if (_index < _list.Length)
        {
            return true;
        }
        return false;
    }

    public Object GetCurrent()
    {
        return _list.GetElement(_index);
    }

    public void Reset()
    {
        _index = 0;
    }

    public void Next()
    {
        if (_index < _list.Length)
        {
            _index++;
        }

    }
}

// 客户端
class Program
{
    static void Main(string[] args)
    {
        IListCollection list = new ConcreteList();
        Iterator iterator = list.GetIterator();

        while (iterator.MoveNext())
        {
            var i = (int)iterator.GetCurrent();
            Console.WriteLine(i.ToString());
            iterator.Next();
        }

        Console.Read();
    }
}

自然,上面代码的运行结果也是对集合每个元素的输出,具体运行结果如下所示:

2
4
6
8

四、 .NET中迭代器模式的应用

在.NET下,迭代器模式中的聚集接口和迭代器接口都已经存在了,其中IEnumerator接口扮演的就是迭代器角色,IEnumberable接口则扮演的就是抽象聚集的角色,只有一个GetEnumerator()方法,关于这两个接口的定义可以自行参考MSDN。在.NET 1.0中,.NET 类库中很多集合都已经实现了迭代器模式,大家可以用反编译工具Reflector来查看下mscorlib程序集下的System.Collections命名空间下的类,这里给出ArrayList的定义代码,具体实现代码可以自行用反编译工具查看,具体代码如下所示:

using System;
using System.Collections;
using System.Collections.Generic;

public class ArrayList : IList, ICollection, IEnumerable, ICloneable
{
    // Fields
    private const int DefaultCapacity = 4;
    private object[] _items;
    private int _size;
    [NonSerialized]
    private object _syncRoot;
    private int _version;

    public ArrayList(object[] items, int size, object syncRoot, int version)
    {
        _items = items;
        _size = size;
        _syncRoot = syncRoot;
        _version = version;
    }

    public virtual IEnumerator GetEnumerator();
    public virtual IEnumerator<> GetEnumerator(int index, int count);

    // Properties
    public virtual int Capacity { get; set; }
    public virtual int Count { get; }
    ..............// 更多代码请自行用反编译工具Reflector查看
}

通过查看源码你可以发现,ArrayList中迭代器的实现与我们前面给出的示例代码非常相似。然而,在.NET 2.0中,由于有了yield return关键字,实现迭代器模式就更简单了。

五、 迭代器模式的适用场景

在下面的情况下可以考虑使用迭代器模式:

  1. 系统需要访问一个聚合对象的内容而无需暴露它的内部表示。
  2. 系统需要支持对聚合对象的多种遍历。
  3. 系统需要为不同的聚合结构提供一个统一的接口。

六、 迭代器模式的优缺点

优点:

  1. 迭代器模式使得访问一个聚合对象的内容而无需暴露它的内部表示,即迭代抽象。
  2. 迭代器模式为遍历不同的集合结构提供了一个统一的接口,从而支持同样的算法在不同的集合结构上进行操作

缺点:

  1. 迭代器模式在遍历的同时更改迭代器所在的集合结构会导致出现异常。所以使用foreach语句只能在对集合进行遍历,不能在遍历的同时更改集合中的元素。

七、 总结

到这里,本博文的内容就介绍结束了,迭代器模式就是抽象一个迭代器类来分离了集合对象的遍历行为,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。

以上是关于通过Lua迭代器自定义实现对c#集合的遍历的主要内容,如果未能解决你的问题,请参考以下文章

lua 迭代器 iterator

雷林鹏分享:Lua 迭代器

Java中定义一个迭代器(Iterator)遍历完一个集合后,如果还要再遍历一次这个集合?

Lua 迭代器

详解C#迭代器

lua 10 迭代器