中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

IEnumerable 的小例子有哪些

發布時間:2021-09-10 18:52:57 來源:億速云 閱讀:135 作者:柒染 欄目:大數據

今天就跟大家聊聊有關 IEnumerable 的小例子有哪些,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。

全是源碼

每個以 TXX 開頭命名的均是一個示例。建議從上往下閱讀。

using System;
using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using Xunit;
using Xunit.Abstractions;

namespace Try_More_On_IEnumerable
{
    public class EnumerableTests2
    {
        private readonly ITestOutputHelper _testOutputHelper;

        public EnumerableTests2(
            ITestOutputHelper testOutputHelper)
        {
            _testOutputHelper = testOutputHelper;
        }

        [Fact]
        public void T11分組合并()
        {
            var array1 = new[] {0, 1, 2, 3, 4};
            var array2 = new[] {5, 6, 7, 8, 9};

            // 通過本地方法合并兩個數組為一個數據
            var result1 = ConcatArray(array1, array2).ToArray();

            // 使用 Linq 中的 Concat 來合并兩個 IEnumerable 對象
            var result2 = array1.Concat(array2).ToArray();

            // 使用 Linq 中的 SelectMany 將 “二維數據” 拉平合并為一個數組
            var result3 = new[] {array1, array2}.SelectMany(x => x).ToArray();

            /**
             *  使用 Enumerable.Range 生成一個數組,這個數據的結果為
             *  0,1,2,3,4,5,6,7,8,9
             */
            var result = Enumerable.Range(0, 10).ToArray();

            // 通過以上三種方式合并的結果時相同的
            result1.Should().Equal(result);
            result2.Should().Equal(result);
            result3.Should().Equal(result);

            IEnumerable<T> ConcatArray<T>(IEnumerable<T> source1, IEnumerable<T> source2)
            {
                foreach (var item in source1)
                {
                    yield return item;
                }

                foreach (var item in source2)
                {
                    yield return item;
                }
            }
        }

        [Fact]
        public void T12拉平三重循環()
        {
            /**
             * 通過本地函數獲取 0-999 共 1000 個數字。
             * 在 GetSomeData 通過三重循環構造這些數據
             * 值得注意的是 GetSomeData 隱藏了三重循環的細節
             */
            var result1 = GetSomeData(10, 10, 10)
                .ToArray();

            /**
             * 與 GetSomeData 方法對比,將“遍歷”和“處理”兩個邏輯進行了分離。
             * “遍歷”指的是三重循環本身。
             * “處理”指的是三重循環最內部的加法過程。
             * 這里通過 Select 方法,將“處理”過程抽離了出來。
             * 這其實和 “T03分離條件”中使用 Where 使用的是相同的思想。
             */
            var result2 = GetSomeData2(10, 10, 10)
                .Select(tuple => tuple.i * 100 + tuple.j * 10 + tuple.k)
                .ToArray();

            // 生成一個 0-999 的數組。
            var result = Enumerable.Range(0, 1000).ToArray();

            result1.Should().Equal(result);
            result2.Should().Equal(result);

            IEnumerable<int> GetSomeData(int maxI, int maxJ, int maxK)
            {
                for (var i = 0; i < maxI; i++)
                {
                    for (var j = 0; j < maxJ; j++)
                    {
                        for (var k = 0; k < maxK; k++)
                        {
                            yield return i * 100 + j * 10 + k;
                        }
                    }
                }
            }

            IEnumerable<(int i, int j, int k)> GetSomeData2(int maxI, int maxJ, int maxK)
            {
                for (var i = 0; i < maxI; i++)
                {
                    for (var j = 0; j < maxJ; j++)
                    {
                        for (var k = 0; k < maxK; k++)
                        {
                            yield return (i, j, k);
                        }
                    }
                }
            }
        }

        private class TreeNode
        {
            public TreeNode()
            {
                Children = Enumerable.Empty<TreeNode>();
            }

            /// <summary>
            /// 當前節點的值
            /// </summary>
            public int Value { get; set; }
            
            /// <summary>
            /// 當前節點的子節點列表
            /// </summary>
            public IEnumerable<TreeNode> Children { get; set; }
        }

        [Fact]
        public void T13遍歷樹()
        {
            /**
             * 樹結構如下:
             * └─0
             *   ├─1
             *   │ └─3
             *   └─2
             */
            var tree = new TreeNode
            {
                Value = 0,
                Children = new[]
                {
                    new TreeNode
                    {
                        Value = 1,
                        Children = new[]
                        {
                            new TreeNode
                            {
                                Value = 3
                            },
                        }
                    },
                    new TreeNode
                    {
                        Value = 2
                    },
                }
            };

            // 深度優先遍歷的結果
            var dftResult = new[] {0, 1, 3, 2};

            // 通過迭代器實現深度優先遍歷
            var dft = DFTByEnumerable(tree).ToArray();
            dft.Should().Equal(dftResult);

            // 使用堆棧配合循環算法實現深度優先遍歷
            var dftList = DFTByStack(tree).ToArray();
            dftList.Should().Equal(dftResult);

            // 遞歸算法實現深度優先遍歷
            var dftByRecursion = DFTByRecursion(tree).ToArray();
            dftByRecursion.Should().Equal(dftResult);

            // 廣度優先遍歷的結果
            var bdfResult = new[] {0, 1, 2, 3};

            /**
             * 通過迭代器實現廣度優先遍歷
             * 此處未提供“通過隊列配合循環算法”和“遞歸算法”實現廣度優先遍歷的兩種算法進行對比。讀者可以自行嘗試。
             */
            var bft = BFT(tree).ToArray();
            bft.Should().Equal(bdfResult);

            /**
             * 迭代器深度優先遍歷
             * depth-first traversal
             */
            IEnumerable<int> DFTByEnumerable(TreeNode root)
            {
                yield return root.Value;
                foreach (var child in root.Children)
                {
                    foreach (var item in DFTByEnumerable(child))
                    {
                        yield return item;
                    }
                }
            }

            // 使用堆棧配合循環算法實現深度優先遍歷
            IEnumerable<int> DFTByStack(TreeNode root)
            {
                var result = new List<int>();
                var stack = new Stack<TreeNode>();
                stack.Push(root);
                while (stack.TryPop(out var node))
                {
                    result.Add(node.Value);
                    foreach (var nodeChild in node.Children.Reverse())
                    {
                        stack.Push(nodeChild);
                    }
                }

                return result;
            }

            // 遞歸算法實現深度優先遍歷
            IEnumerable<int> DFTByRecursion(TreeNode root)
            {
                var list = new List<int> {root.Value};
                foreach (var rootChild in root.Children)
                {
                    list.AddRange(DFTByRecursion(rootChild));
                }

                return list;
            }

            // 通過迭代器實現廣度優先遍歷
            IEnumerable<int> BFT(TreeNode root)
            {
                yield return root.Value;

                foreach (var bftChild in BFTChildren(root.Children))
                {
                    yield return bftChild;
                }

                IEnumerable<int> BFTChildren(IEnumerable<TreeNode> children)
                {
                    var tempList = new List<TreeNode>();
                    foreach (var treeNode in children)
                    {
                        tempList.Add(treeNode);
                        yield return treeNode.Value;
                    }

                    foreach (var bftChild in tempList.SelectMany(treeNode => BFTChildren(treeNode.Children)))
                    {
                        yield return bftChild;
                    }
                }
            }
        }

        [Fact]
        public void T14搜索樹()
        {
            /**
             * 此處所指的搜索樹是指在遍歷樹的基礎上增加終結遍歷的條件。
             * 因為一般構建搜索樹是為了找到第一個滿足條件的數據,因此與單純的遍歷存在不同。
             * 樹結構如下:
             * └─0
             *   ├─1
             *   │ └─3
             *   └─5
             *     └─2
             */

            var tree = new TreeNode
            {
                Value = 0,
                Children = new[]
                {
                    new TreeNode
                    {
                        Value = 1,
                        Children = new[]
                        {
                            new TreeNode
                            {
                                Value = 3
                            },
                        }
                    },
                    new TreeNode
                    {
                        Value = 5,
                        Children = new[]
                        {
                            new TreeNode
                            {
                                Value = 2
                            },
                        }
                    },
                }
            };

            /**
             * 有了深度優先遍歷算法的情況下,再增加一個條件判斷,便可以實現深度優先的搜索
             * 搜索樹中第一個大于等于 3 并且是奇數的數字
             */
            var result = DFS(tree, x => x >= 3 && x % 2 == 1);

            /**
             * 搜索到的結果是3。
             * 特別提出,如果使用廣度優先搜索,結果應該是5。
             * 讀者可以通過 T13遍歷樹 中的廣度優先遍歷算法配合 FirstOrDefault 中相同的條件實現。
             * 建議讀者嘗試以上代碼嘗試一下。
             */
            result.Should().Be(3);

            int DFS(TreeNode root, Func<int, bool> predicate)
            {
                var re = DFTByEnumerable(root)
                    .FirstOrDefault(predicate);
                return re;
            }

            // 迭代器深度優先遍歷
            IEnumerable<int> DFTByEnumerable(TreeNode root)
            {
                yield return root.Value;
                foreach (var child in root.Children)
                {
                    foreach (var item in DFTByEnumerable(child))
                    {
                        yield return item;
                    }
                }
            }
        }

        [Fact]
        public void T15分頁()
        {
            var arraySource = new[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

            // 使用迭代器進行分頁,每 3 個一頁
            var enumerablePagedResult = PageByEnumerable(arraySource, 3).ToArray();

            // 結果一共 4 頁
            enumerablePagedResult.Should().HaveCount(4);
            // 最后一頁只有一個數字,為 9 
            enumerablePagedResult.Last().Should().Equal(9);


            // 通過常規的 Skip 和 Take 來分頁是最為常見的辦法。結果應該與上面的分頁結果一樣
            var result3 = NormalPage(arraySource, 3).ToArray();

            result3.Should().HaveCount(4);
            result3.Last().Should().Equal(9);

            IEnumerable<IEnumerable<int>> PageByEnumerable(IEnumerable<int> source, int pageSize)
            {
                var onePage = new LinkedList<int>();
                foreach (var i in source)
                {
                    onePage.AddLast(i);
                    if (onePage.Count != pageSize)
                    {
                        continue;
                    }

                    yield return onePage;
                    onePage = new LinkedList<int>();
                }

                // 最后一頁如果數據不足一頁,也應該返回該頁
                if (onePage.Count > 0)
                {
                    yield return onePage;
                }
            }

            IEnumerable<IEnumerable<int>> NormalPage(IReadOnlyCollection<int> source, int pageSize)
            {
                var pageCount = Math.Ceiling(1.0 * source.Count / pageSize);
                for (var i = 0; i < pageCount; i++)
                {
                    var offset = i * pageSize;
                    var onePage = source
                        .Skip(offset)
                        .Take(pageSize);
                    yield return onePage;
                }
            }

            /**
             * 從寫法邏輯上來看,顯然 NormalPage 的寫法更容易讓大眾接受
             * PageByEnumerable 寫法在僅僅只有在一些特殊的情況下才能體現性能上的優勢,可讀性上卻不如 NormalPage
             */
        }

        [Fact]
        public void T16分頁與多級緩存()
        {
            /**
             * 獲取 5 頁數據,每頁 2 個。
             * 依次從 內存、Redis、ElasticSearch和數據庫中獲取數據。
             * 先從內存中獲取數據,如果內存中數據不足頁,則從 Redis 中獲取。
             * 若 Redis 獲取后還是不足頁,進而從 ElasticSearch 中獲取。依次類推,直到足頁或者再無數據
             */
            const int pageSize = 2;
            const int pageCount = 5;
            var emptyData = Enumerable.Empty<int>().ToArray();

            /**
             * 初始化各數據源的數據,除了內存有數據外,其他數據源均沒有數據
             */
            var memoryData = new[] {0, 1, 2};
            var redisData = emptyData;
            var elasticSearchData = emptyData;
            var databaseData = emptyData;

            var result = GetSourceData()
                // ToPagination 是一個擴展方法。此處是為了體現鏈式調用的可讀性,轉而使用擴展方法,沒有使用本地函數
                .ToPagination(pageCount, pageSize)
                .ToArray();

            result.Should().HaveCount(2);
            result[0].Should().Equal(0, 1);
            result[1].Should().Equal(2);

            /**
             * 初始化各數據源數據,各個數據源均有一些數據
             */
            memoryData = new[] {0, 1, 2};
            redisData = new[] {3, 4, 5};
            elasticSearchData = new[] {6, 7, 8};
            databaseData = Enumerable.Range(9, 100).ToArray();

            var result2 = GetSourceData()
                .ToPagination(pageCount, pageSize)
                .ToArray();

            result2.Should().HaveCount(5);
            result2[0].Should().Equal(0, 1);
            result2[1].Should().Equal(2, 3);
            result2[2].Should().Equal(4, 5);
            result2[3].Should().Equal(6, 7);
            result2[4].Should().Equal(8, 9);

            IEnumerable<int> GetSourceData()
            {
                // 將多數據源的數據連接在一起
                var data = GetDataSource()
                    .SelectMany(x => x);
                return data;

                // 獲取數據源
                IEnumerable<IEnumerable<int>> GetDataSource()
                {
                    // 將數據源依次返回
                    yield return GetFromMemory();
                    yield return GetFromRedis();
                    yield return GetFromElasticSearch();
                    yield return GetFromDatabase();
                }

                IEnumerable<int> GetFromMemory()
                {
                    _testOutputHelper.WriteLine("正在從內存中獲取數據");
                    return memoryData;
                }

                IEnumerable<int> GetFromRedis()
                {
                    _testOutputHelper.WriteLine("正在從Redis中獲取數據");
                    return redisData;
                }

                IEnumerable<int> GetFromElasticSearch()
                {
                    _testOutputHelper.WriteLine("正在從ElasticSearch中獲取數據");
                    return elasticSearchData;
                }

                IEnumerable<int> GetFromDatabase()
                {
                    _testOutputHelper.WriteLine("正在從數據庫中獲取數據");
                    return databaseData;
                }
            }

            /**
             * 值得注意的是:
             * 由于 Enumerable 按需迭代的特性,如果將 result2 的所屬頁數改為只獲取 1 頁。
             * 則在執行數據獲取時,將不會再控制臺中輸出從 Redis、ElasticSearch和數據庫中獲取數據。
             * 也就是說,并沒有執行這些操作。讀者可以自行修改以上代碼,加深印象。
             */
        }
    }

    public static class EnumerableExtensions
    {
        /// <summary>
        /// 將原數據分頁
        /// </summary>
        /// <param name="source">數據源</param>
        /// <param name="pageCount">頁數</param>
        /// <param name="pageSize">頁大小</param>
        /// <returns></returns>
        public static IEnumerable<IEnumerable<int>> ToPagination(this IEnumerable<int> source,
            int pageCount,
            int pageSize)
        {
            var maxCount = pageCount * pageSize;
            var countNow = 0;
            var onePage = new LinkedList<int>();
            foreach (var i in source)
            {
                onePage.AddLast(i);
                countNow++;

                // 如果獲取的數量已經達到了分頁所需要的總數,則停止進一步迭代
                if (countNow == maxCount)
                {
                    break;
                }

                if (onePage.Count != pageSize)
                {
                    continue;
                }

                yield return onePage;
                onePage = new LinkedList<int>();
            }

            // 最后一頁如果數據不足一頁,也應該返回該頁
            if (onePage.Count > 0)
            {
                yield return onePage;
            }
        }
    }
}

看完上述內容,你們對 IEnumerable 的小例子有哪些有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

酉阳| 江西省| 铅山县| 滦平县| 新乐市| 张家界市| 长寿区| 安西县| 体育| 宜君县| 渭南市| 宾川县| 濮阳市| 东阳市| 新和县| 湟中县| 汝城县| 和硕县| 新兴县| 古浪县| 陆川县| 万源市| 凉城县| 靖西县| 新龙县| 黄大仙区| 满城县| 四子王旗| 东光县| 香港| 宜春市| 黔西县| 长春市| 辉南县| 海兴县| 察隅县| 林芝县| 利川市| 太谷县| 房产| 泌阳县|