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

概要

本文在前两篇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的状态机方式返回。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    最后将所有需要的元素返回。上面算法并不需要直到序列内元素的个数。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Windows系统编程】06.HotFixHook与进程通信(详解HotFixHook)

上一讲讲到的InlineHook&#xff0c;每次Hook的时候&#xff0c;都要读写两次内存&#xff08;先Hook&#xff0c;再还原&#xff09;这种Hook方式&#xff0c;性能比较低&#xff0c;今天我们讲的这种Hook方式&#xff0c;可以说是InlineHook的升级版本 HotFix&#xff08;热…

[JavaWeb]【七】web后端开发-MYSQL

前言&#xff1a;MySQL是一种流行的关系型数据库管理系统,它的作用是存储和管理数据。在Web开发中,MySQL是必备的数据库技能之一,因为它可以帮助Web开发人员处理大量的数据,并且提供了强大的数据查询和管理功能。 一 数据库介绍 1.1 什么是数据库 1.2 数据库产品 二 MySQL概述…

NLPR、SenseTime 和 NTU 加速自动视频纵向编辑

视频人像编辑技术已经在电视、视频和电影制作中得到了应用&#xff0c;并有望在不断发展的网真场景中发挥关键作用。最先进的方法已经可以逼真地将同源音频合成为视频。现在&#xff0c;来自北京模式识别国家实验室&#xff08;NLPR&#xff09;、商汤科技研究和南洋理工大学的…

深度分析纳斯达克上市公司慧择的竞争优势和投资价值

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 一、保险行业的现状、竞争与机遇 在疫情期间&#xff0c;很多行业的经营理念与经营方式&#xff0c;甚至客户行为、客户需求都发生了变化&#xff0c;进而催生出新的机遇。保险行业亦是如此&#xff0c;受疫情影响&#xf…

借助 AI 工具,真的能成为 10x 工程师?

或许你听说过 10x 工程师吗&#xff1f; 如果你问猎头公司 10x 工程师是什么意思&#xff0c;他们可能会说 “生产力”&#xff01;10x 是指完成任务比别人快 10 倍的工程师。 2019 年&#xff0c;Twitter 上就曾经对 10 x 工程师这一议题有过一次空前热烈的讨论&#xff0c;引…

解决电脑声音正常但就是某些游戏没声音问题

电脑声音正常&#xff0c;玩普遍游戏也正常&#xff0c;就有游戏不出声音 详细介绍经过&#xff0c;不喜欢的请直接跳 第三部分。 一、先说下起因现象。 1 大富翁11 没声音。 前段时间无聊怀旧就买了个大富翁11玩玩&#xff0c;近二十年前的老台式机正常无问题。后来想在性能…

【网络编程(二)】NIO快速入门

NIO Java NIO 三大核心组件 Buffer&#xff08;缓冲区&#xff09;&#xff1a;每个客户端连接都会对应一个Buffer&#xff0c;读写数据通过缓冲区读写。Channel&#xff08;通道&#xff09;&#xff1a;每个channel用于连接Buffer和Selector&#xff0c;通道可以进行双向读…

Linux下Docker安装及卸载

文章目录 Linux下Docker安装及卸载1 Docker安装及卸载1.1 安装前准备1.2 安装docker软件2.4.3 启动docker2.2.4 测试2.2.5 卸载 Linux下Docker安装及卸载 1 Docker安装及卸载 官方网址&#xff1a;https://docs.docker.com/engine/install/centos/ 1.1 安装前准备 确定你是C…

zookeeper安装配置采坑流程

安装 wget https://dlcdn.apache.org/zookeeper/zookeeper-3.8.2/apache-zookeeper-3.8.2-bin.tar.gz解压&#xff1a; tar -zxvf apache-zookeeper-3.8.2-bin.tar.gz如下 bin目录下文件是可执行文件 conf目录文件是配置文件 修改zoo.cfg&#xff08;复制zoo_sample&#x…

【学习FreeRTOS】第12章——FreeRTOS时间管理

1.FreeRTOS系统时钟节拍 FreeRTOS的系统时钟节拍计数器是全局变量xTickCount&#xff0c;一般来源于系统的SysTick。在STM32F1中&#xff0c;SysTick的时钟源是72MHz/89MHz&#xff0c;如下代码&#xff0c;RELOAD 9MHz/1000-1 8999&#xff0c;所以时钟节拍是1ms。 portNV…

jvm类文件结构

一 概述 在 Java 中&#xff0c;JVM 可以理解的代码就叫做字节码&#xff08;即扩展名为 .class 的文件&#xff09;&#xff0c;它不面向任何特定的处理器&#xff0c;只面向虚拟机。Java 语言通过字节码的方式&#xff0c;在一定程度上解决了传统解释型语言执行效率低的问题…

学渣的愤怒!自考本科能不能不考英语和数学?

英语和高数哪个更难&#xff1f; 这是自考生们最头大的两个科目。 自考高数有多难&#xff1f; 高数主要有微积分、线性代数和概率论三个部分。 其中微积分是基础、也是重要的一部分&#xff0c;不仅涉及到很多抽象概念和符号运算&#xff0c;还需要具备良好的计算能力和逻…

简单的洗牌算法

目录 前言 问题 代码展现及分析 poker类 game类 Text类 前言 洗牌算法为ArrayList具体使用的典例&#xff0c;可以很好的让我们快速熟系ArrayList的用法。如果你对ArrayList还不太了解除&#xff0c;推荐先看本博主的ArrayList的详解。 ArrayList的详解_WHabcwu的博客-CSD…

C++笔记之单例模式

C笔记之单例模式 参考笔记&#xff1a;C笔记之call_once和once_flag code review 文章目录 C笔记之单例模式1.返回实例引用2.返回实例指针3.单例和智能指针share_ptr结合4.单例和std::call_once结合5.单例和std::call_once、unique_ptr结合 1.返回实例引用 代码 #include <…

数据结构之队列详解(包含例题)

一、队列的概念 队列是一种特殊的线性表&#xff0c;特殊之处在于它只允许在表的前端&#xff08;front&#xff09;进行删除操作&#xff0c;而在表的后端&#xff08;rear&#xff09;进行插入操作&#xff0c;和栈一样&#xff0c;队列是一种操作受限制的线性表。进行插入操…

亿发创新中医药信息化解决方案,自动化煎煮+调剂,打造智能中药房

传统中医药行业逐步复兴&#xff0c;同时互联网科技和人工智能等信息科技助力中医药行业逐步实现数字化转型。利用互联网、物联网、大数据等科技&#xff0c;实现现代科学与传统中医药的结合&#xff0c;提供智能配方颗粒调配系统、中药自动化调剂系统、中药煎配智能管理系统、…

【Docker报错】docker拉取镜像时报错:no such host

报错信息 [rootSoft soft]# docker pull mysql Using default tag: latest Error response from daemon: Head "https://registry-1.docker.io/v2/library/mysql/manifests/latest": dial tcp: lookup registry-1.docker.io on 192.168.80.2:53: no such host解决方法…

Leetcode-每日一题【剑指 Offer 28. 对称的二叉树】

题目 请实现一个函数&#xff0c;用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样&#xff0c;那么它是对称的。 例如&#xff0c;二叉树 [1,2,2,3,4,4,3] 是对称的。 1 / \ 2 2 / \ / \ 3 4 4 3 但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称…

Android 网络编程-网络请求

Android 网络编程-网络请求 文章目录 Android 网络编程-网络请求一、主要内容二、开发网络请求前的基本准备1、查看需要请求的网址是否有效&#xff08;1&#xff09;通过网页在线验证&#xff08;2&#xff09;使用专用window网咯请求工具&#xff08;3&#xff09;编写app代码…

多维时序 | MATLAB实现WOA-CNN-BiGRU-Attention多变量时间序列预测

多维时序 | MATLAB实现WOA-CNN-BiGRU-Attention多变量时间序列预测 目录 多维时序 | MATLAB实现WOA-CNN-BiGRU-Attention多变量时间序列预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 多维时序 | MATLAB实现WOA-CNN-BiGRU-Attention多变量时间序列预测 1.程…