自己动手,实现一种类似List<T>的数据结构(二)之Underscore.js的前缘

前言:

首先,小匹夫要祝各位看官圣诞快乐,新年愉快~。上一篇文章《自己动手,实现一种类似List<T>的数据结构(一)》 介绍了一下不依靠List<T>实现的各种接口,仿造一个轻量级数据结构的过程。可能有的看官会有一些疑问,例如一些功能可以通过Linq提供的拓展来实现呀。此言不虚但也不全对,为了我们在工作中能方便的操作集合而提供的这些拓展方法(包括我们自己也可以构建的拓展方法),例如 Where,Any,Max,All...balalbala等等这些方法都是针对IEnumerable的对象进行扩展的,也就是说需要实现 IEnumerable接口。但是前面已经说了,小匹夫的用意是不实现各种List<T>继承的接口。另外小匹夫的初衷是仿造和拓展 List<T>,将工作中需要使用到的各种功能集成到一个类中,所以有些现成的拓展方法不需要,有一些没有的方法小匹夫也会自己实现一下(当然不是通过给现成的类添加拓展方法这种方式)。当然这篇文章介绍的东西还不成熟,需要慢慢完善,小匹夫也是把这个当做一个学习和实践的机会。好啦,解释完毕,那就介绍下今天的内容吧:

  1. 实现的方法的名称和说明列表
  2. 增加了3个委托来抽象3种情况。
  3. Map:通过委托把EggArray<T>中的每个值映射到一个新的EggArray<T>中。
  4. Difference:返回的值来自EggArray<T>中,但同时不是传入的Other里面的值
  5. Invoke:在EggArray<T>的每个元素上执行methodName方法。
  6. Pluck:萃取EggArray<T>中某字段值,返回一个数组,由于字段类型不确定,所以需要装箱。
  7. Shuffle:返回一个随机乱序的T[]副本。

那么下面我们就书接上文,继续我们仿照和拓展List<T>的步伐。

Underscore.js的前缘

咦,这不是一篇关于Csharp的文章吗?怎么把JS给干出来?哈哈,当然技术上并没有什么必然的关系,只不过是小匹夫之前使用过cocos2d这套游戏引擎开发过游戏,有一段时间也很痴迷于cocos2d-js这种使用JS就能开发原生游戏的能力。所以也接触了一些js库,对Underscore.js更是情有独钟。所以一提到要模仿List<T>这种内部其实是Array的数据结构,一个灵感就是为何不尝试实现一些Underscore.js数组部分的若干功能呢?所以下表EggArray<T>的新增方法中有部分借鉴于Underscore.js。

新增方法表

新增方法 说明
First  返回EggArray<T> 的第一个元素。传递 n参数将返回数组中从第一个元素开始的n个元素
Last  返回EggArray<T> 的最后一个元素。传递 n参数将返回数组中从最后一个元素开始的n个元素
Slice  切割
Get  预留
Set  预留
AddFirst  将对象添加到 EggArray<T> 的起始处。
RemoveLast  从 EggArray<T> 中移除特定对象的最后一个匹配项。
ContainsStrict  确定某元素是否在 EggArray<T> 中。(严格判断是否是同一个对象)
IndexOfStrict  搜索指定的对象,并返回整个 EggArray<T> 中第一个匹配项的从零开始的索引。(同上)
TryGet  获取指定类型对象
LastIndexOf  搜索指定的对象,并返回整个 EggArray<T> 中第一个匹配项的从结尾开始的索引。
Map  通过委托把EggArray<T>中的每个值映射到一个新的EggArray<T>中
Filter  遍历EggArray<T>中的每个值,返回包含所有通过predicate真值检测的元素值。
Without  返回一个删除所有values值后的 EggArray<T>副本。
Find 在EggArray<T>中逐项查找,返回第一个通过predicate迭代函数真值检测的元素值
Every 如果EggArray<T>中的所有元素都通过predicate的真值检测就返回true
Some 如果EggArray<T>中有任何一个元素通过 predicate 的真值检测就返回true
Partition 拆分一个EggArray<T>为两个数组:  第一个数组其元素都满足predicate迭代函数, 而第二个的所有元素均不能满足predicate迭代函数
Difference 返回的值来自EggArray<T>中,但同时不是传入的Other里面的值
Uniq 返回 EggArray<T>去重后的副本
Invoke 在EggArray<T>的每个元素上执行methodName方法。
Pluck 萃取EggArray<T>中元素某属性值,返回一个数组。
Shuffle 返回一个随机乱序的T[]副本
Sample 从 EggArray<T>中产生一个随机样本。传递一个数字表示从EggArray<T>中返回n个随机元素。否则将返回一个单一的随机项。

各位看官可以看到,增加了许多挺有趣的功能。为了能将表中的功能名字变成真正的功能,我们还需要对上一篇文章中的变量&属性部分做一些增改,如下我们增加了3个委托来抽象3种情况。

//定义三个委托来处理具体逻辑
public delegate void IterationHandler(T item);
public delegate bool IterationBoolHandler(T item);
public delegate T IterationVauleHandler(T item);

同时为了能测试我们的功能,我们还要定义一个用来被当做元素测试的类。

//被测试类
public class TargetClass
{
    public int id;
    public string name;

    public TargetClass(int id)
    {
        this.id = id;
        this.name = "NO. " + id;
    }

    public void Hi()
    {
        Debug.Log ("say hi");
    }
}

同时还要有一个测试的环境,因为小匹夫是用mac做unity3d的开发,所以就直接使用unity3d的环境了。

/// <summary>
/// Egg array test.Based on Unity3D,各个元素的id为0-9
/// </summary>
using UnityEngine;
using System.Collections;
using EggToolkit;
public class EggArrayTest : MonoBehaviour {
    EggArray<TargetClass> testArray = new EggArray<TargetClass>();
    // Use this for initialization
    void Start () {

        for(int i = 0; i < 10; i++)
        {
            TargetClass test = new TargetClass(i);
            testArray.Add(test);
        }
//        Test_Difference();
//        Test_Invoke();
//        Test_Pluck();
//        Test_Shuffle();
//        Test_Map();
    }

    void Update () {

    }
}

下面就让小匹夫带领大家分析几个具体的函数,并进行下测试吧。

Map:

使用了IterationVauleHandler这个委托,即需要返回一个T类型的值。

//通过委托把EggArray<T>中的每个值映射到一个新的EggArray<T>中
public EggArray<T> Map(EggArray<T>.IterationVauleHandler handler)
{
    EggArray<T> targetArray = new EggArray<T>(this.capacity);
    for(int i = 0; i < this.count; i++)
    {
        T t = handler(this.items[i]);
        targetArray.Add(t);
    }
    return targetArray;
}

在EggArrayTest中实现Test_Map这个方法:

void Test_Map()
{
    EggArray<TargetClass> newArray = testArray.Map(delegate(TargetClass item) {
        TargetClass newItem = new TargetClass(1);
        newItem.id = item.id * 10;
        return newItem;
    });
    newArray.Foreach(delegate(TargetClass item) {
        Debug.Log (item.id);
    });
}

//原元素的id为0-9,输出为0,10,20...90

Difference:

调用了Filter方法,其中Filter方法的参数是一个IterationBoolHandler委托,即一个返回bool值的委托。具体可以看Filter的实现。

/// <summary>
/// Difference the specified others.
///输出不包含others中元素的EggArray<T>
/// </summary>
/// <param name="others">Others.</param>
public EggArray<T> Difference(EggArray<T> others)
{
    EggArray<T> targetArray = new EggArray<T>();
    targetArray =
    this.Filter(delegate(T item) {
        bool b = !others.Contains(item);
        return b;
    });
    return targetArray;
}

在EggArrayTest中实现Test_Difference这个方法:

//作为参数传入的EggArray<T>由testArray的第5,第9这2个元素组成
void Test_Difference()
{
    EggArray<TargetClass> differentArray = new EggArray<TargetClass>();
    differentArray.Add(testArray.Get(5));
    differentArray.Add(testArray.Get(9));
    testArray.Difference(differentArray).Foreach(delegate(TargetClass item) {
        Debug.Log(item.name);
    });
}
//输出缺少no. 5,no. 9这两个name

Invoke:

在EggArray<T>的每个元素上执行methodName方法。

/// <summary>
/// Invoke the specified methodName.
/// 每个元素上执行methodName方法,若方法不存在则抛出exception
/// </summary>
/// <param name="methodName">Method name.</param>
public void Invoke(string methodName)
{
    Type t = typeof(T);
    var method = t.GetMethod(methodName);
    if(method == null)
        throw new Exception("没有找到指定的方法哦~,可能不叫" + methodName);
    for(int i = 0; i < this.count; i++)
    {
        method.Invoke(this.items[i], null);
    }
}

在EggArrayTest中实现Test_Invoke这个方法:

//调用TargetClass的HI()方法
void Test_Invoke()
{
    testArray.Invoke("Hi");
}

//输出:say hi

Pluck:

萃取EggArray<T>中某字段值,返回一个数组,由于字段类型不确定,所以需要装箱。当传入的名称无法查找到该字段时,抛出exception。

/// <summary>
/// Pluck the specified fieldName.
/// 萃取某字段值,返回一个数组
/// 由于字段类型不确定,所以需要装箱
/// </summary>
/// <param name="fieldName">Field name.</param>
public object[] Pluck(string fieldName)
{
    Type t = typeof(T);
    object[] targetArray = new object[this.count];
    var field = t.GetField(fieldName);
    if(field == null)
        throw new Exception("没有找到指定的field哦~,可能不叫" + fieldName);
    for(int i = 0; i < this.count; i++)
    {
        object value = field.GetValue(this.items[i]);
        targetArray[i] = value;
    }
    return targetArray;
}

在EggArrayTest中实现Test_Pluck这个方法:

//获取各个元素 字段id的值
void Test_Pluck()
{
    object[] testObj = testArray.Pluck("id");
    string testString = string.Empty;
    for(int i = 0; i < testObj.Length; i++)
    {
        testString = testObj[i].ToString();
        Debug.Log ("field value is " + testString);
    }
}

//输出为0-9

Shuffle:

返回一个随机乱序的T[],下面看代码

/// <summary>
/// Shuffle this instance.
/// 返回一个随机乱序的副本
/// </summary>
public T[] Shuffle()
{
    T[] shuffled = new T[this.count];
    Random random = new Random();
    for (int index = 0, rand; index < this.count; index++) {
        rand = random.Next(index);
        if (rand != index)
            shuffled[index] = shuffled[rand];
        shuffled[rand] = this.items[index];
    }
    return shuffled;
}

在EggArrayTest中实现Test_Shuffle这个方法:

//
void Test_Shuffle()
{
    TargetClass[] test = testArray.Shuffle();
    for(int i = 0; i < test.Length; i++)
    {
        Debug.Log (test[i].name);
    }
}

//默认顺序为NO. 0 ~ NO. 9
//乱序后,见图

好了,这周就到这里~小匹夫最近也在赶项目的途中,所以测试和修改的精力也被消耗了很多。过完元旦之后,再继续~

末了还是要说一声:各位元旦快乐~

装模作样的声明一下:本博文章若非特殊注明皆为原创,若需转载请保留原文链接及作者信息慕容小匹夫

时间: 2024-07-29 09:02:17

自己动手,实现一种类似List<T>的数据结构(二)之Underscore.js的前缘的相关文章

自己动手,实现一种类似List&lt;T&gt;的数据结构(一)

前言 上一篇文章<Unity3D中常用的数据结构总结与分析>简单总结了一下小匹夫工作中经常遇到的一些数据结构.不过小匹夫一直有种观点,就是光说的热闹实际啥也不做真的没啥意思.光说不练假把式,那么这篇文章不如记录一下小匹夫自己动手实现一个有类似功能的数据结构的过程吧. 模仿List<T> 寻思半天,写代码是为了啥?不是为了写以致用嘛?那么小匹夫工作中用的最多的数据结构是啥?思来想去还就是List<T>了,而且平时使用的时候的确也觉得有自己定制的空间.作为一个类,重要的无非

十三种基于直方图的图像全局二值化算法原理、实现、代码及效果(转)

十三种基于直方图的图像全局二值化算法原理.实现.代码及效果(转) http://www.cnblogs.com/carekee/articles/3643394.html 图像二值化的目的是最大限度的将图象中感兴趣的部分保留下来,在很多情况下,也是进行图像分析.特征提取与模式识别之前的必要的图像预处理过程.这个看似简单的问题,在过去的四十年里受到国内外学者的广泛关注,产生了数以百计的阈值选取方法,但如同其他图像分割算法一样,没有一个现有方法对各种各样的图像都能得到令人满意的结果. 在这些庞大的分

Java经典23种设计模式之结构型模式(二)

接上篇,本文介绍结构型模式里的组合模式.装饰模式.外观模式. 一.组合模式(Composite) 组合模式:将对象组合成树形结构,表示"部分--整体"的层次结构.最终达到单个对象和组合对象的使用具有一致性.单看这句话貌似有点抽象,其实比较简单. 以李云龙的独立团为例,目的要统计赵嘉宇一战共歼灭敌人多少个.最高的级别是团,一个团有若干个营,一个营有若干个排,一个排有若干个战士.(为了简化问题,排下面就不设行政单位了).很自然的,李云龙给营长开会回去给老子统计.营长回去给各个排长开会,赶紧

Ionic 设置全局变量,三种方法设置图片一种是直接增加,一种是replace,第三种是管道和第二种类似

原文地址:https://www.cnblogs.com/sugartang/p/11432422.html

fiddler几种功能强大的用法(二)

参考网址:http://blog.rekfan.com/articles/228.html http://www.cnblogs.com/tugenhua0707/p/4637771.html http://blog.csdn.net/xia_xia0919/article/details/50518629 1.直接在fiddler上配置host,点击tools>>hosts,在里面填写自己想要设置的host即可 2.Fiddler显示请求服务器的ip及系统环境的配置方法: 1)打开Rules

Java经典23种设计模式之行为型模式(二)

本文接着介绍行为型模式里的解释器模式.迭代器模式.中介者模式. 一.解释器模式Interpret 给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言的中的句子. 1.AbstractExpression(抽象表达式) 声明一个抽象的解释操作,这个接口为抽象语法树中全部的节点所共享. public abstract class Expression {    abstract void interpret(Context ctx);} 2.Expression

FreeBSD上两种认证方式配置SVN笔记二

一.安装: 1.Apache安装 不用多说,进入到 /usr/ports/www/apache22目录下,运行make install即可安装默认方式搞定,如果你还有别的apache的应用和需求,自己研究. 2.Subversion安装 #cd /usr/ports/devel/subversion#make install WITHOUT_BDB=yes WITH_MOD_DAV_SVN=yes APXS=/usr/local/sbin/apxs 说明:WITHOUT_BDB=yes 这是我不

几种C#框架提供的数据结构对单值查找的效率比较

做分词组件时,有网友提出采用Hashtable 数据结构查找字符串效率较低,建议改为Dictionary,其理由是采用Hashtable 时Key值是object 会触发装箱和拆箱动作,一直对这种说法表示怀疑,因为我理解只有值类型和引用类型通过object 互转时才会发生装箱和查询,引用类型之间强制转换不应发生装箱和拆箱,而Dictionary 泛型实际上底层还是调用的Hashtable,所以效率怎么会比Hashtable 要高呢?今天决定对比较常用的4种数据结构做一个测试,以便以后做系统性能优

使用java语言实现一个队列(两种实现比较)(数据结构)

一.什么是队列,换句话说,队列主要特征是什么? 四个字:先进先出 六个字:屁股进,脑袋出 脑补个场景:日常排队买饭,新来的排在后面,前面打完饭的走人,这就是队列: OK,思考一个问题,我为什么写了两种实现,它们的区别是什么,哪个性能更好一些? 我觉得学习一定要带着问题来学习: 二.队列的两种实现 1.数组队列 数组队列比较简单,基于之前写的动态数组所实现的,基本方法都是根据队列的特性从而选择性的调用动态数组的方法来实现的. public class ArrayQueue<E> implemen