C# Linq源码分析之Take (三)

news2025/1/23 17:31:09

概要

本文在前两篇Take源码分析的基础上,着重分析Range参数中有倒数的情况,即分析TakeRangeFromEndIterator的源码实现。

源码及分析

TakeRangeFromEndIterator方法用于处理Range中的开始和结束索引存在倒数的情况。该方法位于Take.cs文件中。通过yield return/break的方式管理迭代过程。

TakeRangeFromEndIterator方法从整体上分为两部分,一部分是TryGetNonEnumeratedCount为真的情况,即souce实现了ICollection接口的情况;另一部分是souce没有实现ICollection接口,TryGetNonEnumeratedCount返回为False的情况。

 private static IEnumerable<TSource> TakeRangeFromEndIterator<TSource>(IEnumerable<TSource> source, bool isStartIndexFromEnd, int startIndex, bool isEndIndexFromEnd, int endIndex)
{
    if (source.TryGetNonEnumeratedCount(out int count))
    {
        startIndex = CalculateStartIndex(isStartIndexFromEnd, startIndex, count);
        endIndex = CalculateEndIndex(isEndIndexFromEnd, endIndex, count);

        if (startIndex < endIndex)
        {
            foreach (TSource element in 
            TakeRangeIterator(source, startIndex, endIndex))
            {
                yield return element;
            }
        }
        yield break;
    }

    static int CalculateStartIndex(bool isStartIndexFromEnd, int startIndex, int count) =>
        Math.Max(0, isStartIndexFromEnd ? count - startIndex : startIndex);

    static int CalculateEndIndex(bool isEndIndexFromEnd, int endIndex, int count) =>
        Math.Min(count, isEndIndexFromEnd ? count - endIndex : endIndex);
}

  1. 该函数有4个参数,开始索引是否倒数,开始索引值,结束索引是否为倒数,结束索引值;
  2. 如果source实现了ICollection接口,可以在不遍历souce序列的情况下,直接获取序列长度,则TryGetNonEnumeratedCount返回为True;
  3. 调用CalculateStartIndex和CalculateEndIndex内联方法,获取正数的索引值,假设Range是 ^3… ^1,元素共10个,则转换完成后是7…9,即从第7个取到第9个;
  4. 在开始索引值小于结束索引值的前提下,调TakeRangeIterator方法,按照普通正数Range的方法进行处理,并将结果以状态机的形式按照yield return/break方式返回。具体详见 C# Linq源码分析之Take (二)

注意在TryGetNonEnumeratedCount返回为True的情况下,因为可以直接取到序列的元素个数,不需要进行逐个迭代和累加。因此才可以将倒数的Range转换成正数的Range。对于无法获取序列元素的情况,我们看下面的代码分析。

在开始索引是倒数的情况下,进行如下处理,此时假设我们有如下序列,我们的Range是 ^3 … ^1,但是不知道序列内元素个数。
在这里插入图片描述

 Queue<TSource> queue;

    if (isStartIndexFromEnd)
    {
        // TakeLast compat: enumerator should be disposed before yielding the first element.
        using (IEnumerator<TSource> e = source.GetEnumerator())
        {
            if (!e.MoveNext())
            {
                yield break;
            }

            queue = new Queue<TSource>();
            queue.Enqueue(e.Current);
            count = 1;

            while (e.MoveNext())
            {
                if (count < startIndex)
                {
                    queue.Enqueue(e.Current);
                    ++count;
                }
                else
                {
                    do
                    {
                        queue.Dequeue();
                        queue.Enqueue(e.Current);
                        checked { ++count; }
                    } while (e.MoveNext());
                    break;
                }
            }

            Debug.Assert(queue.Count == Math.Min(count, startIndex));
        }

        startIndex = CalculateStartIndex(isStartIndexFromEnd: true, startIndex, count);
        endIndex = CalculateEndIndex(isEndIndexFromEnd, endIndex, count);
        Debug.Assert(endIndex - startIndex <= queue.Count);

        for (int rangeIndex = startIndex; rangeIndex < endIndex; rangeIndex++)
        {
            yield return queue.Dequeue();
        }
    }
  1. 获取souce的迭代器e;
  2. 如果取一个元素失败,证明Range中指定的范围在实际序列中根本取不到,则通过yield break关闭状态机;
  3. 定义缓冲队列queue,将“A”放入队列,元素个数初始值设置为1;
  4. 迭代开始,count < startIndex在元素个数累加器小于启始索引的情况下,每次队列增加一个元素,直到count等于3,此时队列元素是"A", “B”, “C”;
  5. 然后每次删除队首元素,再添加新的元素到队尾,当遍历完整个source序列后,队列元素是“G”, “H”, “I”,此种方法遍历,算法只需要启始索引值,结束索引值完全忽略;
  6. 根据迭代中获取的元素个数count,计算出正数的开始和结束索引,本例应该是6…8;
  7. 将缓冲队列queue按照状态机的形式,通过yield return方式返回;因为rangeIndex < endIndex;,所以最后的返回值是“G”, “H”没有“I”,只会取到结束索引对应元素的前一个元素。

在开始索引不是倒数的情况下, 进行如下处理,此时假设我们有如下序列,我们的Range是 3 … ^2,此时我们并不清楚集合内元素的个数。

请注意在现有的情况下,如果开始索引是正数,结尾索引一定是倒数的。如果结尾索引是正数,更加之前的代码分析,只会进入C# Linq源码分析之Take (二)所介绍的TakeRangeIterator方法。

假设我们使用的数据如下:

在这里插入图片描述

 else
    {
        Debug.Assert(!isStartIndexFromEnd && isEndIndexFromEnd);

        // SkipLast compat: the enumerator should be disposed at the end of the enumeration.
        using IEnumerator<TSource> e = source.GetEnumerator();

        count = 0;
        while (count < startIndex && e.MoveNext())
        {
            ++count;
        }

        if (count == startIndex)
        {
            queue = new Queue<TSource>();
            while (e.MoveNext())
            {
                if (queue.Count == endIndex)
                {
                    do
                    {
                        queue.Enqueue(e.Current);
                        yield return queue.Dequeue();
                    } while (e.MoveNext());

                    break;
                }
                else
                {
                    queue.Enqueue(e.Current);
                }
            }
        }
    }
  1. 定义迭代器e 和 集合内初始元素个数计数器设置为0;
  2. 在初始化操作后,count是3,迭代器指向元素C;
  3. 如果count等于 startIndex,证明Range中的取值在下面的序列中可以取到。如果不等,则返回空;
    在这里插入图片描述
  4. 因为是倒数第2个,所以endIndex的数值是2,
  5. D和E进入缓冲队列;
  6. 因为缓冲队列中的长度必须和endIndex相等,队列每进入一个元素到队尾,然后删除队首元素并按照yield return的状态机方式返回。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    最后将所有需要的元素返回。上面算法并不需要直到序列内元素的个数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/894929.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Android4:约束布局

创建项目My Constraint Layout 一般创建项目之后activity_main.xml文件默认就是采用约束布局&#xff0c;如&#xff1a; <?xml version"1.0" encoding"utf-8"?> <androidx.constraintlayout.widget.ConstraintLayoutxmlns:android"http:…

FastDFS文件管理知识点+案例分析

一 介绍FastDFS 原理 FastDFS开源分布式文件系统由C语言编写实现, 可以通过专有API访问&#xff0c;目前提供了C、Java和PHP API。 FastDFS是一个开源的轻量级分布式文件系统&#xff0c;它对文件进行管理&#xff0c;功能包括&#xff1a;文件存储、文件同步、文件访问&#x…

scala中json4s 使用详解

预备知识 json4s的数据结构AST (Abstract Syntax Tree)。 sealed abstract class JValue case object JNothing extends JValue // zero for JValue case object JNull extends JValue case class JString(s: String) extends JValue case class JDouble(num: Double) extend…

企业需求如何精准对接科研院校科技成果?

一系列科技成果转化相关政策、法律法规的出台&#xff0c;正在破除高校科技成果转化机制上的桎梏。企业科技创新的需求也越来越大&#xff0c;也充分意识到与高校合作对于推动企业发展的重要性&#xff0c;对接科技成果的热情高涨&#xff0c;合作越来越频繁。但是也存在不成功…

SpringBoot 的 RedisTemplate、Redisson

一、Jedis、Lettuce、Redisson的简介 优先使用Lettuce&#xff0c; 需要分布式锁&#xff0c;分布式集合等分布式的高级特性&#xff0c;添加Redisson结合使用。 对于高并发&#xff0c;1000/s的并发&#xff0c;数据库可能由行锁变成表锁&#xff0c;性能下降会厉害。 1.1、…

linux中ubuntu安装hashcat方法以及使用GPU破解

一、linux安装hashcat git clone https://github.com/hashcat/hashcat.git make make install二、安装驱动 驱动版本安装大全&#xff1a;https://developer.nvidia.com/cuda-toolkit-archive 查看自己显卡选择对应的版本&#xff0c;根据下面命令无脑安装就行了 wget https:/…

誉天华为数通Datacom HCIE认证课程

作为当前网络领域最具含金量的认证之一&#xff0c;华为Datacom HCIE认证不仅是网络工程师们技术能力的证明&#xff0c;也是未来行业趋势的风向标。 尽管经历了几度版本更迭、考题变化&#xff0c;但是誉天一直紧跟技术发展趋势&#xff0c;在专业领域和教学内容上始终与当前的…

生成国密密钥对

在线生成国密密钥对 生成的密钥对要妥善保管&#xff0c;丢失是无法找回的。

奇迹MU服务端IGC架设流程

奇迹MU服务端IGC架设流程 大家好我是艾西&#xff0c;今天跟大家简单聊下奇迹IGC服务端。IGC端是国外的东西全英文大家感兴趣的话也可以自己研究研究&#xff0c;整体的东西还是非常完整。扩展以及端的稳定性还是非常不错的就是有点费脑子........&#xff08;此教程为个人娱乐…

WebStorm运行vue项目

WebStorm运行vue项目&#xff08;vue2&#xff09; 1.安装webstorm 2. 需要安装node.js 环境&#xff0c;可以去官网下载 https://nodejs.org/en 3. 安装完需要查看 按winr 输入cmd进入 输入命令node -v 和npm -v 查看&#xff0c;会出现相应的版本号,代表安装成功 vue官网安…

IC设计中主要的EDA工具有哪些? (内附EDA虚拟机安装资源)

EDA工具的使用涵盖了芯片的功能设计、综合、验证、物理设计等环节&#xff0c;更是被称作“芯片设计的工作母机”。下面就来为大家具体介绍一下常见的EDA工具。&#xff08;需要EDA虚拟机安装资源文末可领取~&#xff09; 什么是EDA&#xff1f; EDA是电子设计自动化&#xf…

对象存储服务-MinIO基本集成

是什么 MinIO 是一个高性能的分布式对象存储服务&#xff0c;适合存储非结构化数据&#xff0c;如图片&#xff0c;音频&#xff0c;视频&#xff0c;日志等。对象文件最大可以达到5TB。 安装启动 mkdir -p /usr/local/minio cd /usr/local/minio# 下载安装包 wget https:/…

NextJs - Error Handling (错误处理)

Error.js 的作用 使用 error.js 文件规则&#xff0c;可以优雅地处理嵌套路由中的意外运行时错误。 在 React Error Boundary 中自动封装路由段及其嵌套子段。 使用文件系统层次结构来调整粒度&#xff0c;为特定网段量身定制错误 UI。 将错误隔离到受影响的网段&#xff0…

红帆OA SQL注入漏洞复现

0x01 产品简介 红帆iOffice.net从最早满足医院行政办公需求&#xff08;传统OA&#xff09;&#xff0c;到目前融合了卫生主管部门的管理规范和众多行业特色应用&#xff0c;是目前唯一定位于解决医院综合业务管理的软件&#xff0c;是最符合医院行业特点的医院综合业务管理平…

怎么修改图片的分辨率?

怎么修改图片的分辨率&#xff1f;很多人还不知道分辨率是什么意思&#xff0c;以为代表了图片的清晰度&#xff0c;然而并不是这样的&#xff0c;其实图片的分辨率就是图片尺寸大小的意思。修改图片的分辨率即改变图片的尺寸&#xff0c;通常以像素为单位表示。分辨率决定了图…

【腾讯云Cloud Studio实战训练营】使用Cloud Studio社区版快速构建React完成点餐H5页面还原

陈老老老板&#x1f9b8; &#x1f468;‍&#x1f4bb;本文专栏&#xff1a;生活&#xff08;主要讲一下自己生活相关的内容&#xff09; &#x1f468;‍&#x1f4bb;本文简述&#xff1a;生活就像海洋,只有意志坚强的人,才能到达彼岸。 &#x1f468;‍&#x1f4bb;上一篇…

如何优雅做好项目管理?

导言 项目本身无好坏之分&#xff0c;项目管理有做好与做坏之别。在互联网大厂的体制下&#xff0c;想要做坏一个项目很难&#xff08;可以通过换人、追加资源等方式消除风险&#xff09;&#xff0c;想要做好一个项目不容易&#xff0c;需要团队及PM付出大量心血和精力。在这…

实时业务需求和数据处理要求的满足:流处理的能力

随着数据量和数据种类的不断增加&#xff0c;企业和组织对于实时数据处理和业务响应的需求也越来越迫切。传统的批处理方式无法满足业务对于实时性和即时响应的要求&#xff0c;因此&#xff0c;流处理技术应运而生。通过流处理能力&#xff0c;可以有效地处理实时数据&#xf…

Python爬虫常用:谷歌浏览器驱动——Chromedriver 插件安装教程

前言 大家早好、午好、晚好吖 ❤ ~欢迎光临本文章 我们在做爬虫的时候经常要使用谷歌浏览器驱动&#xff0c;今天分享下这个Chromedriver 插件的安装方法。 话不多说&#xff0c;直接开搞&#xff0c;如果有什么疑惑/资料需要的可以点击文章末尾名片领取源码 第一步、打开谷…

VR仿真实训系统编辑平台赋予老师更多自由和灵活性

为了降低院校教师在VR虚拟现实方面应用的门槛&#xff0c;VR公司深圳华锐视点融合多年的VR虚拟仿真实训系统制作经验&#xff0c;制作了VR动物课件编辑器&#xff0c;正在逐渐受到师生们的关注和应用。 简单来说&#xff0c;VR畜牧专业课件编辑器是一种可以制作虚拟现实动物教学…