อธิบายขั้นตอนวิธีการรวม LINQ


721

นี่อาจฟังดูง่อย แต่ฉันไม่สามารถหาคำอธิบายที่ดีAggregateได้

ดีหมายถึงสั้นบรรยายที่ครอบคลุมด้วยตัวอย่างเล็ก ๆ และชัดเจน

คำตอบ:


1015

คำจำกัดความที่ง่ายที่สุดที่จะเข้าใจAggregateคือการดำเนินการกับแต่ละองค์ประกอบของรายการโดยคำนึงถึงการดำเนินงานที่ผ่านมา กล่าวคือจะดำเนินการกับองค์ประกอบที่หนึ่งและที่สองและดำเนินการไปข้างหน้า จากนั้นมันจะทำงานกับผลลัพธ์ก่อนหน้าและองค์ประกอบที่สาม เป็นต้น

ตัวอย่าง 1. การรวมตัวเลข

var nums = new[]{1,2,3,4};
var sum = nums.Aggregate( (a,b) => a + b);
Console.WriteLine(sum); // output: 10 (1+2+3+4)

นี้จะเพิ่ม1และจะทำให้2 3แล้วเพิ่ม3(ผลมาจากก่อนหน้า) และ3(องค์ประกอบในลำดับถัดไป) 6เพื่อให้ แล้วเพิ่ม6และจะทำให้410

ตัวอย่างที่ 2 สร้าง csv จากอาร์เรย์ของสตริง

var chars = new []{"a","b","c", "d"};
var csv = chars.Aggregate( (a,b) => a + ',' + b);
Console.WriteLine(csv); // Output a,b,c,d

วิธีนี้ใช้ได้ผลเหมือนกัน Concatenate aเครื่องหมายจุลภาคและเพื่อให้b a,bแล้วเชื่อมa,b ด้วยเครื่องหมายจุลภาคและเพื่อให้c a,b,cและอื่น ๆ

ตัวอย่างที่ 3 การคูณตัวเลขด้วยเมล็ด

เพื่อความสมบูรณ์มีเกินพิกัดของAggregateซึ่งจะมีค่าเมล็ดพันธุ์

var multipliers = new []{10,20,30,40};
var multiplied = multipliers.Aggregate(5, (a,b) => a * b);
Console.WriteLine(multiplied); //Output 1200000 ((((5*10)*20)*30)*40)

เหมือนตัวอย่างข้างต้น, เริ่มต้นนี้มีมูลค่า5และคูณได้โดยองค์ประกอบแรกของลำดับการให้ผลของการ10 50ผลที่ได้นี้จะดำเนินไปข้างหน้าและคูณด้วยจำนวนถัดไปในลำดับที่จะให้เป็นผลมาจาก20 1000สิ่งนี้จะดำเนินต่อไปผ่านองค์ประกอบที่เหลืออีก 2 ลำดับ

ตัวอย่างสด: http://rextester.com/ZXZ64749
เอกสาร: http://msdn.microsoft.com/en-us/library/bb548651.aspx


ภาคผนวก

ตัวอย่างที่ 2 ด้านบนใช้การต่อข้อมูลสตริงเพื่อสร้างรายการค่าคั่นด้วยเครื่องหมายจุลภาค นี่เป็นวิธีที่ง่ายในการอธิบายการใช้Aggregateซึ่งเป็นความตั้งใจของคำตอบนี้ แต่ถ้าใช้เทคนิคนี้ในการสร้างจริงเป็นจำนวนมากคั่นด้วยเครื่องหมายจุลภาคข้อมูลก็จะเหมาะสมกว่าที่จะใช้StringBuilderและนี่คือทั้งหมดเข้ากันได้กับการใช้เมล็ดเกินที่จะเริ่มต้นAggregateStringBuilder

var chars = new []{"a","b","c", "d"};
var csv = chars.Aggregate(new StringBuilder(), (a,b) => {
    if(a.Length>0)
        a.Append(",");
    a.Append(b);
    return a;
});
Console.WriteLine(csv);

อัปเดตตัวอย่าง: http://rextester.com/YZCVXV6464


11
คำอธิบายอื่นสำหรับคำอธิบายแรกคือฟังก์ชั่นที่คุณให้จะรวมสมาชิกสองคนแรกเสมอจนกว่าอาร์เรย์จะถูกย่อขนาดไปยังองค์ประกอบหนึ่ง ดังนั้น[1,2,3,4]จะ[3,3,4]แล้ว[6,4]และใน[10]ที่สุด แต่แทนที่จะส่งกลับอาร์เรย์ของค่าเดียวคุณก็จะได้ค่ากลับมา
David Raab

2
ฉันสามารถแยก / ออกจากฟังก์ชันการรวมได้หรือไม่? ตัวอย่างเช่น chars.Aggregate ((a, b) => {ถ้า (a == 'a') ทำลายผลรวมทั้งหมดกลับเป็น + ',' + b})
Jeff Tian

13
@JeffTian - ฉันขอแนะนำให้ผูกมัดTakeWhileแล้วAggregate- thats beatuty ของส่วนขยายนับ - พวกเขาจะ chainable ได้ง่าย TakeWhile(a => a == 'a').Aggregate(....)ดังนั้นคุณจึงจบลงด้วย ดูตัวอย่างนี้: rextester.com/WPRA60543
Jamiec

2
ในฐานะที่เป็น sidenote บนภาคผนวกบล็อกทั้งหมดสามารถถูกแทนที่ได้อย่างง่ายดายvar csv = string.Join(",", chars)(ไม่จำเป็นต้องรวมหรือ stringbuilders) - แต่ใช่ฉันรู้ว่าจุดของคำตอบคือการให้ตัวอย่างการใช้งานของการรวมดังนั้นมันเจ๋ง แต่ฉันก็ยังอยากจะพูดถึงมันไม่แนะนำสำหรับการเข้าร่วมสายมีวิธีการเฉพาะสำหรับที่ ....
T_D

2
การใช้งานทั่วไปอื่น ๆ (จนถึงตอนนี้ที่ฉันเคยเห็นแม้แต่ในรหัสการผลิต) คือรับรายการขั้นต่ำหรือสูงสุดเช่นvar biggestAccount = Accounts.Aggregate((a1, a2) => a1.Amount >= a2.Amount ? a1 : a2);
Franck

133

ส่วนหนึ่งขึ้นอยู่กับว่าคุณกำลังพูดถึงเรื่องอะไรมากไป แต่แนวคิดพื้นฐานคือ:

  • เริ่มต้นด้วยเมล็ดเป็น "ค่าปัจจุบัน"
  • วนซ้ำตามลำดับ สำหรับแต่ละค่าในลำดับ:
    • ใช้ฟังก์ชั่นที่ผู้ใช้ระบุเพื่อแปลง(currentValue, sequenceValue)เป็น(nextValue)
    • ชุด currentValue = nextValue
  • คืนสุดท้าย currentValue

คุณอาจพบว่าAggregateโพสต์ในซีรีส์ Edulinq ของฉันมีประโยชน์ - ซึ่งรวมถึงคำอธิบายโดยละเอียด (รวมถึงการโอเวอร์โหลดที่หลากหลาย) และการใช้งาน

ตัวอย่างง่ายๆอย่างหนึ่งที่ใช้AggregateแทนCount:

// 0 is the seed, and for each item, we effectively increment the current value.
// In this case we can ignore "item" itself.
int count = sequence.Aggregate(0, (current, item) => current + 1);

หรืออาจรวมความยาวของสตริงทั้งหมดในลำดับของสตริง:

int total = sequence.Aggregate(0, (current, item) => current + item.Length);

โดยส่วนตัวแล้วฉันไม่ค่อยพบว่าAggregateมีประโยชน์ - วิธีการรวมตัวที่ "เหมาะ" มักจะดีพอสำหรับฉัน


6
@ จอนมีการรวมแบบอะซิงโครนัสที่แยกรายการออกเป็นทรีเพื่อให้สามารถแยกการทำงานระหว่างแกนได้หรือไม่ ดูเหมือนว่าการออกแบบของวิธีนี้สอดคล้องกับแนวคิดของ "ลด" หรือ "พับ" แต่ฉันไม่รู้ว่ามันกำลังทำอย่างนั้นจริง ๆ ภายใต้ประทุนหรือทำซ้ำผ่านรายการของรายการ
AaronLS

@ จอน: edulink ที่กล่าวถึงข้างต้นใช้งานไม่ได้คุณสามารถเปลี่ยนเส้นทางฉันไปยังลิงก์ที่ถูกต้องได้ และคุณสามารถระบุให้เฉพาะเจาะจงมากขึ้นเกี่ยวกับฟังก์ชั่นการรวมคำว่า "ที่เหมาะ" ที่คุณใช้ในคำตอบของคุณ
Koushik

1
@ Koushik: ฉันได้แก้ไขลิงก์ในโพสต์แล้ว โดยฟังก์ชั่นการรวม "ปรับแต่ง" ฉันหมายถึงสิ่งต่าง ๆ เช่น Max / Min / Count / Sum
Jon Skeet

62

ผลรวมสั้นพิเศษเช่นการพับใน Haskell / ML / F #

อีกต่อไปเล็กน้อย . Max (), .Min (), .Sum (),. เฉลี่ย () จะวนซ้ำทุกองค์ประกอบในลำดับและรวมเข้าด้วยกันโดยใช้ฟังก์ชันการรวมที่เกี่ยวข้อง .Aggregate () เป็นตัวรวบรวมทั่วไปที่ช่วยให้นักพัฒนาสามารถระบุสถานะเริ่มต้น (aka เมล็ด) และฟังก์ชั่นรวม

ฉันรู้ว่าคุณขอคำอธิบายสั้น ๆ แต่ฉันคิดว่าคนอื่นให้คำตอบสั้น ๆ สองสามข้อฉันคิดว่าคุณอาจจะสนใจอีกต่อไปเล็กน้อย

Long version with code วิธีหนึ่งในการแสดงให้เห็นว่ามันจะแสดงให้เห็นว่าคุณใช้Sample Standard Deviation ได้อย่างไรเมื่อใช้ foreach และเมื่อใช้. Agregate หมายเหตุ: ฉันไม่ได้จัดลำดับความสำคัญของการแสดงที่นี่ดังนั้นฉันจึงวนซ้ำหลาย ๆ ครั้งโดยไม่จำเป็น

ครั้งแรกที่ฟังก์ชั่นตัวช่วยใช้ในการสร้างผลรวมของระยะทางกำลังสอง:

static double SumOfQuadraticDistance (double average, int value, double state)
{
    var diff = (value - average);
    return state + diff * diff;
}

จากนั้นตัวอย่างค่าเบี่ยงเบนมาตรฐานโดยใช้ ForEach:

static double SampleStandardDeviation_ForEach (
    this IEnumerable<int> ints)
{
    var length = ints.Count ();
    if (length < 2)
    {
        return 0.0;
    }

    const double seed = 0.0;
    var average = ints.Average ();

    var state = seed;
    foreach (var value in ints)
    {
        state = SumOfQuadraticDistance (average, value, state);
    }
    var sumOfQuadraticDistance = state;

    return Math.Sqrt (sumOfQuadraticDistance / (length - 1));
}

จากนั้นเมื่อใช้. ปิด:

static double SampleStandardDeviation_Aggregate (
    this IEnumerable<int> ints)
{
    var length = ints.Count ();
    if (length < 2)
    {
        return 0.0;
    }

    const double seed = 0.0;
    var average = ints.Average ();

    var sumOfQuadraticDistance = ints
        .Aggregate (
            seed,
            (state, value) => SumOfQuadraticDistance (average, value, state)
            );

    return Math.Sqrt (sumOfQuadraticDistance / (length - 1));
}

โปรดทราบว่าฟังก์ชั่นเหล่านี้เหมือนกันยกเว้นวิธีคำนวณ sumOfQuadraticDistance:

var state = seed;
foreach (var value in ints)
{
    state = SumOfQuadraticDistance (average, value, state);
}
var sumOfQuadraticDistance = state;

เมื่อเทียบกับ:

var sumOfQuadraticDistance = ints
    .Aggregate (
        seed,
        (state, value) => SumOfQuadraticDistance (average, value, state)
        );

ดังนั้นสิ่งที่ Agregate ทำก็คือมันห่อหุ้มรูปแบบตัวรวบรวมนี้และฉันคาดหวังว่าการดำเนินการของ. Agregate จะมีลักษณะเช่นนี้:

public static TAggregate Aggregate<TAggregate, TValue> (
    this IEnumerable<TValue> values,
    TAggregate seed,
    Func<TAggregate, TValue, TAggregate> aggregator
    )
{
    var state = seed;

    foreach (var value in values)
    {
        state = aggregator (state, value);
    }

    return state;
}

การใช้ฟังก์ชันส่วนเบี่ยงเบนมาตรฐานจะมีลักษณะดังนี้:

var ints = new[] {3, 1, 4, 1, 5, 9, 2, 6, 5, 4};
var average = ints.Average ();
var sampleStandardDeviation = ints.SampleStandardDeviation_Aggregate ();
var sampleStandardDeviation2 = ints.SampleStandardDeviation_ForEach ();

Console.WriteLine (average);
Console.WriteLine (sampleStandardDeviation);
Console.WriteLine (sampleStandardDeviation2);

IMHO

ดังนั้น. Agregate จะช่วยให้อ่านง่ายหรือไม่? โดยทั่วไปฉันรัก LINQ เพราะฉันคิดว่าที่นี่เลือก. และโดยมากช่วยให้อ่านได้ง่าย (ถ้าคุณหลีกเลี่ยงการเรียงลำดับชั้นแบบอินไลน์. การรวมจะต้องอยู่ใน Linq ด้วยเหตุผลที่ครบถ้วน แต่โดยส่วนตัวแล้วฉันไม่เชื่อว่า. Agregate จะเพิ่มความสามารถในการอ่านเมื่อเปรียบเทียบกับ foreach ที่เขียนได้ดี


+1 ยอดเยี่ยม! แต่วิธีการขยายSampleStandardDeviation_Aggregate()และSampleStandardDeviation_ForEach()ไม่สามารถเป็นprivate(โดยค่าเริ่มต้นหากไม่มีตัวระบุการเข้าถึง) ดังนั้นควรมีการเพิ่มขึ้นด้วยpublicหรือinternalดูเหมือนว่าสำหรับฉัน
Fulproof

FYI: ถ้าฉันจำได้อย่างถูกต้องวิธีการขยายในตัวอย่างของฉันเป็นส่วนหนึ่งของคลาสเดียวกันที่ใช้พวกเขา ==> งานส่วนตัวในกรณีนี้
อีกตัวหนึ่ง

39

ภาพที่มีค่าพันคำ

คำเตือน:
Func<X, Y, R>เป็นฟังก์ชั่นที่มีสองปัจจัยการผลิตชนิดXและผลตอบแทนที่เป็นผลมาจากประเภทYR

Enumerable.Aggate มีสามโอเวอร์โหลด:


เกิน 1:

A Aggregate<A>(IEnumerable<A> a, Func<A, A, A> f)

Aggregate1

ตัวอย่าง:

new[]{1,2,3,4}.Aggregate((x, y) => x + y);  // 10


การโอเวอร์โหลดนี้ง่าย แต่มีข้อ จำกัด ดังต่อไปนี้:

  • ลำดับจะต้องมีองค์ประกอบอย่างน้อยหนึ่งอย่างอื่นฟังก์ชั่นจะโยน
    InvalidOperationException
  • องค์ประกอบและผลลัพธ์จะต้องเป็นประเภทเดียวกัน



เกินพิกัด 2:

B Aggregate<A, B>(IEnumerable<A> a, B bIn, Func<B, A, B> f)

Aggregate2

ตัวอย่าง:

var hayStack = new[] {"straw", "needle", "straw", "straw", "needle"};
var nNeedles = hayStack.Aggregate(0, (n, e) => e == "needle" ? n+1 : n);  // 2


เกินพิกัดนี้ทั่วไปมากขึ้น:

  • ต้องระบุค่าเมล็ดพันธุ์ ( bIn)
  • คอลเลกชันสามารถว่างเปล่า
    ในกรณีนี้ฟังก์ชั่นจะให้ค่าเมล็ดเป็นผล
  • องค์ประกอบและผลลัพธ์อาจมีประเภทที่แตกต่างกัน



เกินพิกัด 3:

C Aggregate<A,B,C>(IEnumerable<A> a, B bIn, Func<B,A,B> f, Func<B,C> f2)


โอเวอร์โหลดที่สามไม่มีประโยชน์มากสำหรับ IMO
สามารถเขียนแบบเดียวกันได้อย่างชัดเจนยิ่งขึ้นโดยใช้ overload 2 ตามด้วยฟังก์ชันที่แปลงผลลัพธ์


ภาพประกอบถูกดัดแปลงมาจากบล็อกที่ยอดเยี่ยมนี้


นี่จะเป็นคำตอบที่ยอดเยี่ยม .... สำหรับคำถามเกี่ยวกับ Haskel แต่ไม่มี. overload Aggegateในเป็นFunc<T, T, T>.
Jamiec

4
ใช่แล้ว คุณใช้มันในคำตอบของคุณเอง!
3dGrabber

1
Upvoting เนื่องจากคุณอธิบายอย่างรอบคอบว่าเกิดอะไรขึ้นเมื่อลำดับว่างเปล่า ให้Nเป็นจำนวนองค์ประกอบในแหล่งที่มา เราสังเกตว่าการโอเวอร์โหลดที่ไม่ได้ใช้ให้seedใช้ฟังก์ชั่นการสะสมN -1 ครั้ง; ในขณะที่เกินพิกัดอื่น ๆ (ที่ไม่ใช้seed) ใช้ฟังก์ชั่นสะสมNครั้ง
Jeppe Stig Nielsen

17

โดยทั่วไปการรวมจะใช้ในการจัดกลุ่มหรือสรุปข้อมูล

อ้างอิงจาก MSDN "ฟังก์ชันการรวมใช้ฟังก์ชันสะสมตามลำดับ"

ตัวอย่างที่ 1: เพิ่มตัวเลขทั้งหมดในอาร์เรย์

int[] numbers = new int[] { 1,2,3,4,5 };
int aggregatedValue = numbers.Aggregate((total, nextValue) => total + nextValue);

* สำคัญ: มูลค่ารวมเริ่มต้นโดยค่าเริ่มต้นคือ 1 องค์ประกอบในลำดับของการรวบรวม ie: ค่าเริ่มต้นของตัวแปรทั้งหมดจะเป็น 1 โดยค่าเริ่มต้น

คำอธิบายตัวแปร

ทั้งหมด: มันจะเก็บค่าผลรวม (มูลค่ารวม) ที่ส่งคืนโดย func

nextValue: เป็นค่าถัดไปในลำดับของอาร์เรย์ ค่านี้จะถูกเพิ่มเข้าไปในค่าที่สรุปรวมคือผลรวม

ตัวอย่างที่ 2: เพิ่มรายการทั้งหมดในอาร์เรย์ ตั้งค่าเริ่มต้นสะสมด้วยเพื่อเริ่มต้นเพิ่มด้วยจาก 10

int[] numbers = new int[] { 1,2,3,4,5 };
int aggregatedValue = numbers.Aggregate(10, (total, nextValue) => total + nextValue);

คำอธิบายข้อโต้แย้ง:

อาร์กิวเมนต์แรกคือค่าเริ่มต้น (ค่าเริ่มต้นเช่นค่าเมล็ด) ซึ่งจะถูกใช้เพื่อเริ่มการบวกกับค่าถัดไปในอาร์เรย์

อาร์กิวเมนต์ที่สองคือ func ซึ่งเป็น func ที่ใช้ 2 int

1.total: สิ่งนี้จะถือเหมือนก่อนที่มูลค่ารวม (มูลค่ารวม) ที่ส่งคืนโดย func หลังจากการคำนวณ

2.nextValue: มันเป็นค่าถัดไปในลำดับอาเรย์ ค่านี้จะถูกเพิ่มเข้าไปในค่าที่สรุปรวมคือผลรวม

การดีบักรหัสนี้จะทำให้คุณเข้าใจมากขึ้นเกี่ยวกับวิธีการรวมของงาน


7

เรียนรู้มากมายจากคำตอบของ Jamiec

หากจำเป็นเพียงอย่างเดียวคือการสร้างสตริง CSV คุณอาจลองทำสิ่งนี้

var csv3 = string.Join(",",chars);

นี่คือการทดสอบที่มี 1 ล้านสาย

0.28 seconds = Aggregate w/ String Builder 
0.30 seconds = String.Join 

รหัสที่มาอยู่ที่นี่


เมื่อฉันรันรหัสเดียวกันในdotnetfiddle.netตามที่ระบุไว้ในลิงก์ฉันได้รับ "ข้อผิดพลาดร้ายแรง: เกินขีด จำกัด การใช้หน่วยความจำ" สำหรับ "string.Join" แต่ Aggregate จะทำงานได้ตามปกติเสมอ ดังนั้นฉันเชื่อว่านี่ไม่แนะนำให้ใช้ String.Join
Manish Jain

แปลก? เมื่อฉันแสดงความคิดเห็นครั้งแรกที่หยุดดูซึ่งรวมสำหรับ; จากนั้นฉันไม่ได้รับ "ข้อผิดพลาดร้ายแรง: เกินขีด จำกัด การใช้หน่วยความจำ" กรุณาอธิบาย! Link: dotnetfiddle.net/6YyumS
Manish Jain

dotnetfiddle.net มีการ จำกัด หน่วยความจำเมื่อถึงการหยุดการทำงาน ถ้าคุณย้ายรหัสรวมก่อนรหัส String.Join คุณอาจได้รับข้อผิดพลาดสำหรับการรวม
Rm558

7

นอกเหนือจากคำตอบที่ยอดเยี่ยมทั้งหมดที่นี่แล้วฉันยังใช้มันเพื่อเดินรายการผ่านชุดของขั้นตอนการแปลง

หากมีการใช้การแปลงในรูปแบบ a Func<T,T>คุณสามารถเพิ่มการแปลงหลายรายการลงใน a List<Func<T,T>>และใช้Aggregateเพื่อดำเนินการตามตัวอย่างของTแต่ละขั้นตอน

ตัวอย่างที่เป็นรูปธรรมมากขึ้น

คุณต้องการรับstringค่าและดำเนินการผ่านชุดของการแปลงข้อความที่สามารถสร้างขึ้นทางโปรแกรม

var transformationPipeLine = new List<Func<string, string>>();
transformationPipeLine.Add((input) => input.Trim());
transformationPipeLine.Add((input) => input.Substring(1));
transformationPipeLine.Add((input) => input.Substring(0, input.Length - 1));
transformationPipeLine.Add((input) => input.ToUpper());

var text = "    cat   ";
var output = transformationPipeLine.Aggregate(text, (input, transform)=> transform(input));
Console.WriteLine(output);

สิ่งนี้จะสร้างสายการเปลี่ยนแปลง: ลบช่องว่างนำหน้าและส่วนท้าย -> ลบอักขระตัวแรก -> ลบอักขระตัวสุดท้าย -> แปลงเป็นตัวพิมพ์ใหญ่ ขั้นตอนในห่วงโซ่นี้สามารถเพิ่มเอาออกหรือจัดลำดับใหม่ได้ตามต้องการเพื่อสร้างไปป์ไลน์การแปลงชนิดใดก็ได้ที่ต้องการ

ผลลัพธ์ที่ได้จากท่อนี้โดยเฉพาะคือว่าจะกลายเป็น" cat ""A"


นี้สามารถกลายเป็นมีประสิทธิภาพมากเมื่อคุณตระหนักว่าTสามารถเป็นอะไร สามารถใช้สำหรับการแปลงภาพเช่นตัวกรองโดยใช้BitMapเป็นตัวอย่าง


4

คำนิยาม

วิธีการรวมเป็นวิธีการขยายสำหรับคอลเลกชันทั่วไป วิธีการรวมใช้ฟังก์ชั่นกับแต่ละรายการของคอลเลกชัน ไม่เพียง แต่ใช้ฟังก์ชั่นเท่านั้น แต่ยังรับผลลัพธ์เป็นค่าเริ่มต้นสำหรับการวนซ้ำครั้งถัดไป ดังนั้นเราจะได้ค่าที่คำนวณได้ (min, max, avg หรือค่าทางสถิติอื่น ๆ ) จากการรวบรวม

ดังนั้นวิธีการรวมเป็นรูปแบบของการใช้งานที่ปลอดภัยของฟังก์ชั่นซ้ำ

ปลอดภัยเนื่องจากการเรียกซ้ำจะวนซ้ำในแต่ละรายการของคอลเลกชันและเราไม่สามารถหยุดการวนซ้ำไม่สิ้นสุดโดยเงื่อนไขการออกไม่ถูกต้อง เรียกซ้ำเนื่องจากผลลัพธ์ของฟังก์ชันปัจจุบันถูกใช้เป็นพารามิเตอร์สำหรับการเรียกใช้ฟังก์ชันถัดไป

ไวยากรณ์:

collection.Aggregate(seed, func, resultSelector);
  • เมล็ด - ค่าเริ่มต้นโดยค่าเริ่มต้น;
  • func - ฟังก์ชั่นวนซ้ำของเรา มันอาจเป็นแลมบ์ดานิพจน์ผู้รับมอบสิทธิ์ Func หรือประเภทฟังก์ชั่น TF (T result, T nextValue);
  • resultSelector - มันสามารถเป็นฟังก์ชั่นเช่น func หรือนิพจน์เพื่อคำนวณ, เปลี่ยน, เปลี่ยน, แปลงผลลัพธ์สุดท้าย

มันทำงานอย่างไร:

var nums = new[]{1, 2};
var result = nums.Aggregate(1, (result, n) => result + n); //result = (1 + 1) + 2 = 4
var result2 = nums.Aggregate(0, (result, n) => result + n, response => (decimal)response/2.0); //result2 = ((0 + 1) + 2)*1.0/2.0 = 3*1.0/2.0 = 3.0/2.0 = 1.5

การใช้งานจริง:

  1. ค้นหาแฟคทอเรียลจากหมายเลข n:

int n = 7;
var numbers = Enumerable.Range(1, n);
var factorial = numbers.Aggregate((result, x) => result * x);

ซึ่งทำสิ่งเดียวกันกับฟังก์ชั่นนี้:

public static int Factorial(int n)
{
   if (n < 1) return 1;

   return n * Factorial(n - 1);
}
  1. Aggregate () เป็นหนึ่งในวิธีการขยาย LINQ ที่ทรงพลังที่สุดเช่น Select () และ Where () เราสามารถใช้มันเพื่อแทนที่ Sum (), Min () ฟังก์ชัน Max (), Avg () หรือเปลี่ยนโดยใช้บริบทเพิ่มเติม:
    var numbers = new[]{3, 2, 6, 4, 9, 5, 7};
    var avg = numbers.Aggregate(0.0, (result, x) => result + x, response => (double)response/(double)numbers.Count());
    var min = numbers.Aggregate((result, x) => (result < x)? result: x);
  1. การใช้วิธีการขยายที่ซับซ้อนมากขึ้น:
    var path = @“c:\path-to-folder”;

    string[] txtFiles = Directory.GetFiles(path).Where(f => f.EndsWith(“.txt”)).ToArray<string>();
    var output = txtFiles.Select(f => File.ReadAllText(f, Encoding.Default)).Aggregate<string>((result, content) => result + content);

    File.WriteAllText(path + summary.txt”, output, Encoding.Default);

    Console.WriteLine(“Text files merged into: {0}”, output); //or other log info

คำตอบแรกที่ดีงาม ทำได้ดี! อัปยศมันเป็นคำถามเก่าหรือคุณจะมี upvotes มากมาย
Jamiec

1

นี่เป็นคำอธิบายเกี่ยวกับการใช้Aggregateงาน Fluent API เช่น Linq Sorting

var list = new List<Student>();
var sorted = list
    .OrderBy(s => s.LastName)
    .ThenBy(s => s.FirstName)
    .ThenBy(s => s.Age)
    .ThenBy(s => s.Grading)
    .ThenBy(s => s.TotalCourses);

และให้เราเห็นว่าเราต้องการที่จะใช้ฟังก์ชั่นการเรียงลำดับที่ใช้ชุดของเขตข้อมูลนี้เป็นเรื่องง่ายมากที่ใช้Aggregateแทน for-loop เช่นนี้

public static IOrderedEnumerable<Student> MySort(
    this List<Student> list,
    params Func<Student, object>[] fields)
{
    var firstField = fields.First();
    var otherFields = fields.Skip(1);

    var init = list.OrderBy(firstField);
    return otherFields.Skip(1).Aggregate(init, (resultList, current) => resultList.ThenBy(current));
}

และเราสามารถใช้สิ่งนี้:

var sorted = list.MySort(
    s => s.LastName,
    s => s.FirstName,
    s => s.Age,
    s => s.Grading,
    s => s.TotalCourses);

1

ทุกคนให้คำอธิบายของเขา คำอธิบายของฉันเป็นเช่นนั้น

วิธีการรวมใช้ฟังก์ชั่นกับแต่ละรายการของคอลเลกชัน ตัวอย่างเช่นเรามีคอลเลกชัน {6, 2, 8, 3} และฟังก์ชั่น Add (ตัวดำเนินการ +) ที่มันทำ (((6 + 2) +8) +3) และส่งคืน 19

var numbers = new List<int> { 6, 2, 8, 3 };
int sum = numbers.Aggregate(func: (result, item) => result + item);
// sum: (((6+2)+8)+3) = 19

ในตัวอย่างนี้มีวิธีการตั้งชื่อผ่านเพิ่มแทนนิพจน์แลมบ์ดา

var numbers = new List<int> { 6, 2, 8, 3 };
int sum = numbers.Aggregate(func: Add);
// sum: (((6+2)+8)+3) = 19

private static int Add(int x, int y) { return x + y; }

0

คำจำกัดความที่สั้นและจำเป็นอาจเป็นเช่นนี้: วิธีการขยาย Linq Aggregate ช่วยให้สามารถประกาศฟังก์ชันแบบเรียกซ้ำที่ใช้กับองค์ประกอบของรายการตัวถูกดำเนินการของสอง: องค์ประกอบตามลำดับที่ปรากฏในรายการ องค์ประกอบหนึ่งในเวลาและผลของการซ้ำซ้ำก่อนหน้านี้หรือไม่มีอะไรถ้ายังไม่ได้เรียกซ้ำ

ด้วยวิธีนี้คุณสามารถคำนวณแฟคทอเรียลของตัวเลขหรือสตริงที่ต่อกันได้


0

ผลรวมที่ใช้ในการหาผลรวมของคอลัมน์ในอาร์เรย์จำนวนเต็มหลายมิติ

        int[][] nonMagicSquare =
        {
            new int[] {  3,  1,  7,  8 },
            new int[] {  2,  4, 16,  5 },
            new int[] { 11,  6, 12, 15 },
            new int[] {  9, 13, 10, 14 }
        };

        IEnumerable<int> rowSums = nonMagicSquare
            .Select(row => row.Sum());
        IEnumerable<int> colSums = nonMagicSquare
            .Aggregate(
                (priorSums, currentRow) =>
                    priorSums.Select((priorSum, index) => priorSum + currentRow[index]).ToArray()
                );

เลือกด้วยดัชนีจะใช้ภายในฟังก์ชันรวมเพื่อรวมคอลัมน์ที่ตรงกันและส่งกลับอาร์เรย์ใหม่ {3 + 2 = 5, 1 + 4 = 5, 7 + 16 = 23, 8 + 5 = 13}

        Console.WriteLine("rowSums: " + string.Join(", ", rowSums)); // rowSums: 19, 27, 44, 46
        Console.WriteLine("colSums: " + string.Join(", ", colSums)); // colSums: 25, 24, 45, 42

แต่การนับจำนวน trues ในอาเรย์บูลีนนั้นยากกว่าเนื่องจากชนิดสะสม (int) แตกต่างจากชนิดต้นทาง (บูล) ที่นี่เมล็ดมีความจำเป็นเพื่อที่จะใช้เกินพิกัดที่สอง

        bool[][] booleanTable =
        {
            new bool[] { true, true, true, false },
            new bool[] { false, false, false, true },
            new bool[] { true, false, false, true },
            new bool[] { true, true, false, false }
        };

        IEnumerable<int> rowCounts = booleanTable
            .Select(row => row.Select(value => value ? 1 : 0).Sum());
        IEnumerable<int> seed = new int[booleanTable.First().Length];
        IEnumerable<int> colCounts = booleanTable
            .Aggregate(seed,
                (priorSums, currentRow) =>
                    priorSums.Select((priorSum, index) => priorSum + (currentRow[index] ? 1 : 0)).ToArray()
                );

        Console.WriteLine("rowCounts: " + string.Join(", ", rowCounts)); // rowCounts: 3, 1, 2, 2
        Console.WriteLine("colCounts: " + string.Join(", ", colCounts)); // colCounts: 3, 2, 1, 2
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.