ความหมายของการประเมินค่าแบบขี้เกียจ
ทำความเข้าใจการทำงานของตัววนซ้ำที่เลื่อนออกไป
ความหมายของการประเมินค่าแบบขี้เกียจ เป็นบทเรียน C# Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน C# Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส C# Academy มีบทเรียนทั้งหมด 4 บทเรียน
การประมวลผลแบบเลื่อนเวลา
ตัววนซ้ำและคำค้นหา LINQ ใช้ การประมวลผลแบบเลื่อนเวลา โดยการกำหนดคำค้นหาแทบไม่ทำงานใด ๆ งานจะเริ่มขึ้นเมื่อคุณแจกแจงผลลัพธ์จริง ๆ ทีละรายการ
ยังไม่มีอะไรทำงานจนกว่าจะเริ่มแจกแจง
การเรียกเมธอดตัววนซ้ำไม่ได้ทำให้เนื้อหาภายในเมธอดทำงาน เนื้อหาจะเริ่มทำงานเมื่อ foreach (หรือผู้ใช้ผลลัพธ์รายอื่น) ดึงค่าแรก
using System;
using System.Collections.Generic;
public class Program
{
static IEnumerable<int> Numbers()
{
Console.WriteLine("-- iterator body started --");
yield return 1;
yield return 2;
}
public static void Main()
{
var seq = Numbers();
Console.WriteLine("query created, body not run yet");
foreach (var n in seq) Console.WriteLine(n);
}
}สร้างค่าทีละรายการ
การวนซ้ำแต่ละครั้งจะทำให้ตัววนซ้ำทำงานเพียงเท่าที่จำเป็นเพื่อสร้างค่าถัดไป รูปแบบการทำงานที่ให้ผู้ใช้ดึงค่าเช่นนี้ช่วยลดการใช้หน่วยความจำ แม้จะเป็นลำดับข้อมูลขนาดใหญ่มาก
using System;
using System.Collections.Generic;
public class Program
{
static IEnumerable<int> Trace()
{
for (int i = 1; i <= 3; i++)
{
Console.WriteLine("producing " + i);
yield return i;
}
}
public static void Main()
{
foreach (var n in Trace())
Console.WriteLine("consumed " + n);
}
}การแจกแจงซ้ำจะทำงานอีกครั้ง
ลำดับข้อมูลแบบเลื่อนเวลาไม่มีผลลัพธ์ที่แคชไว้ การแจกแจงสองครั้งจะทำให้ตัววนซ้ำทำงานสองครั้ง ซึ่งมีความสำคัญหาก source เปลี่ยนแปลงหรือการทำงานใช้เวลามาก
using System;
using System.Collections.Generic;
public class Program
{
static int _calls = 0;
static IEnumerable<int> Counter()
{
_calls++;
yield return _calls;
}
public static void Main()
{
var seq = Counter();
foreach (var n in seq) Console.WriteLine(n);
foreach (var n in seq) Console.WriteLine(n);
Console.WriteLine("total runs: " + _calls);
}
}การอ่านสถานะปัจจุบัน
เนื่องจากการทำงานถูกเลื่อนเวลา ตัววนซ้ำจึงอ่าน source เมื่อมีการแจกแจง ไม่ใช่เมื่อกำหนด ดังนั้นการเปลี่ยนแปลงที่เกิดขึ้นหลังจากกำหนด query จะปรากฏให้เห็น
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var list = new List<int> { 1, 2 };
IEnumerable<int> doubled = Double(list);
list.Add(3); // added before enumeration
foreach (var n in doubled) Console.WriteLine(n);
}
static IEnumerable<int> Double(IEnumerable<int> nums)
{
foreach (var n in nums) yield return n * 2;
}
}การบังคับให้ทำงานทันที
หากต้องการเก็บภาพรวมของผลลัพธ์ ให้ทำให้ลำดับข้อมูลเป็นข้อมูลจริงด้วย ToList() หรือ ToArray() การทำเช่นนี้จะเรียกใช้ตัววนซ้ำหนึ่งครั้งและจัดเก็บค่าไว้ ทำให้ไม่ขึ้นต่อกับการเปลี่ยนแปลงของ source ในภายหลัง
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
var list = new List<int> { 1, 2 };
List<int> snapshot = Double(list).ToList(); // executed now
list.Add(99);
Console.WriteLine(string.Join(",", snapshot));
}
static IEnumerable<int> Double(IEnumerable<int> nums)
{
foreach (var n in nums) yield return n * 2;
}
}การหยุดทำงานเมื่อได้คำตอบช่วยลดงาน
เนื่องจากผู้ใช้ผลลัพธ์ดึงค่าแบบเลื่อนเวลา การดำเนินการอย่างการค้นหารายการแรกที่ตรงเงื่อนไขจึงหยุดได้ก่อนกำหนด ผู้สร้างจะไม่คำนวณค่าที่ไม่มีใครร้องขอ
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
static IEnumerable<int> Numbers()
{
for (int i = 1; ; i++)
{
Console.WriteLine("checking " + i);
yield return i;
}
}
public static void Main()
{
int firstBig = Numbers().First(n => n > 3);
Console.WriteLine("found " + firstBig);
}
}ข้อผิดพลาดที่เลื่อนเวลา
ข้อผิดพลาดภายในตัววนซ้ำจะไม่ถูกส่งออกมาเมื่อเรียกใช้เมธอด แต่จะเกิดขึ้นเมื่อการแจกแจงเดินทางมาถึงบรรทัดที่มีปัญหา เรื่องนี้อาจทำให้ประหลาดใจหากคุณคาดว่าจะมีการตรวจสอบตั้งแต่ต้น
using System;
using System.Collections.Generic;
public class Program
{
static IEnumerable<int> Risky(int divisor)
{
yield return 10 / divisor; // throws only during enumeration
}
public static void Main()
{
var seq = Risky(0);
Console.WriteLine("no error yet");
try { foreach (var n in seq) Console.WriteLine(n); }
catch (DivideByZeroException) { Console.WriteLine("caught during enumeration"); }
}
}การผสานสายการประมวลผลแบบเลื่อนเวลาทำได้อย่างประหยัด
การเชื่อมตัวดำเนินการแบบเลื่อนเวลาหลายตัวไม่ได้ทำให้ตัวดำเนินการเหล่านั้นทำงานซ้ำ ๆ แต่ละค่าจะไหลผ่านสายการประมวลผลทั้งหมดเพียงครั้งเดียวเมื่อมีการร้องขอ จึงมีประสิทธิภาพและใช้หน่วยความจำอย่างเหมาะสม
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
var result = Enumerable.Range(1, 1000000)
.Where(n => n % 2 == 0)
.Select(n => n * n)
.Take(3);
foreach (var n in result) Console.WriteLine(n);
}
}ควรทำให้ข้อมูลเป็นจริงเมื่อใด
ทำให้ข้อมูลเป็นจริงเมื่อคุณจะแจกแจงหลายครั้ง เมื่อ source อาจเปลี่ยนแปลง หรือเมื่อจำเป็นต้องเก็บผลลัพธ์ก่อนปล่อยทรัพยากร มิฉะนั้นให้คงการทำงานแบบเลื่อนเวลาไว้เพื่อประหยัดหน่วยความจำและการทำงาน
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
var evens = Enumerable.Range(1, 10).Where(n => n % 2 == 0).ToList();
// Safe to enumerate many times now
Console.WriteLine("count: " + evens.Count);
Console.WriteLine("sum: " + evens.Sum());
}
}ลองทำด้วยตนเอง
สังเกตการประมวลผลแบบเลื่อนเวลาโดยตรง: กำหนด query จากนั้นเปลี่ยนแปลง source และการเปลี่ยนแปลงจะปรากฏในผลลัพธ์เมื่อมีการแจกแจงเท่านั้น
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
var source = new List<int> { 1, 2, 3 };
var query = source.Where(n => n > 1); // not executed yet
source.Add(10);
source.Remove(2);
// Executes now, reflecting all changes
Console.WriteLine(string.Join(", ", query));
}
}ตรวจสอบอย่างรวดเร็ว
ใช้เหตุผลเกี่ยวกับการประมวลผลแบบเลื่อนเวลา
สรุปทบทวน
การประเมินแบบเลื่อนเวลา (แบบขี้เกียจ) จะทำงานกับลำดับข้อมูลเมื่อมีการแจกแจงเท่านั้น
- การกำหนด query ไม่ทำงานใด ๆ แต่การใช้ผลลัพธ์จะทำให้เกิดการทำงาน
- การแจกแจงซ้ำจะเรียกใช้ตัววนซ้ำอีกครั้ง
- ระบบจะอ่าน source เมื่อมีการแจกแจง ดังนั้นการเปลี่ยนแปลงภายหลังจึงปรากฏให้เห็น
- ข้อผิดพลาดจะปรากฏระหว่างการแจกแจง
- ใช้
ToList()/ToArray()เพื่อทำให้ข้อมูลเป็นจริงเมื่อจำเป็น
คำถามที่พบบ่อย
บทเรียน “ความหมายของการประเมินค่าแบบขี้เกียจ” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “ความหมายของการประเมินค่าแบบขี้เกียจ” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส C# Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส C# Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “ความหมายของการประเมินค่าแบบขี้เกียจ”
ทำความเข้าใจการทำงานของตัววนซ้ำที่เลื่อนออกไป คุณปฏิบัติ C# Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน C# Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน C# Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน
บทเรียน “ความหมายของการประเมินค่าแบบขี้เกียจ” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน C# Academy นี้ได้ไหม
ได้ บทเรียน C# Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- เมธอดตัววนซ้ำด้วย yield return
- yield break และการสิ้นสุดก่อนกำหนด
- ความหมายของการประเมินค่าแบบขี้เกียจ
- ชนิด Enumerable แบบกำหนดเอง