LINQ Aggregate 알고리즘 설명
이것은 절름발이로 들릴 수 있지만에 대한 정말 좋은 설명을 찾을 수 없었습니다 Aggregate
.
좋음은 짧고 설명 적이며 포괄적이고 작고 명확한 예를 의미합니다.
이해하기 가장 쉬운 정의 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
및 4
만들기 위해 10
.
예 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
시퀀스의 첫 번째 요소를 곱합니다 . 이 결과는 이월되고 시퀀스의 다음 숫자를 곱하여 결과를 제공합니다 . 이것은 시퀀스의 나머지 2 개 요소를 통해 계속됩니다.10
50
20
1000
라이브 예제 : http://rextester.com/ZXZ64749
문서 : http://msdn.microsoft.com/en-us/library/bb548651.aspx
추가
위의 예 2에서는 문자열 연결을 사용하여 쉼표로 구분 된 값 목록을 만듭니다. 이것은 Aggregate
이 답변의 의도가 사용 된 것을 설명하는 단순한 방법 입니다. 그러나이 기술을 사용하여 실제로 많은 양의 쉼표로 구분 된 데이터를 생성하는 경우를 사용하는 것이 더 적절할 수 있으며 StringBuilder
, Aggregate
이는 시드 된 오버로드를 사용하여 StringBuilder
.
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
그것은 부분적으로 당신이 말하는 과부하에 달려 있지만 기본 아이디어는 다음과 같습니다.
- "현재 값"으로 시드로 시작
- 시퀀스를 반복합니다. 시퀀스의 각 값에 대해 :
- 변환 할 수있는 사용자 지정 기능 적용
(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
유용한 -은 "맞춤형"집계 방법은 나를 위해 일반적으로 충분하다.
Super short Aggregate는 Haskell / ML / F #에서 폴드처럼 작동합니다.
약간 더 긴 .Max (), .Min (), .Sum (), .Average ()는 모두 시퀀스의 요소를 반복하고 각각의 집계 함수를 사용하여 집계합니다. .Aggregate ()는 개발자가 시작 상태 (일명 시드) 및 집계 함수를 지정할 수 있다는 점에서 일반화 된 집계 자입니다.
나는 당신이 짧은 설명을 요청한 것을 알고 있지만 다른 사람들이 몇 가지 짧은 답변을 주었을 때 나는 당신이 약간 더 긴 것에 관심이있을 것이라고 생각했습니다.
코드가 포함 된 긴 버전 foreach를 사용하여 한 번, .Aggregate를 사용하여 샘플 표준 편차 를 구현하는 방법을 보여주는 한 가지 방법이 무엇인지 보여줄 수 있습니다 . 참고 : 여기서 성능의 우선 순위를 지정하지 않았으므로 불필요하게 colleciton에 대해 여러 번 반복합니다.
먼저 2 차 거리의 합을 만드는 데 사용되는 도우미 함수 :
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));
}
그런 다음 .Aggregate 사용 :
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)
);
따라서 .Aggregate가하는 일은이 집계 패턴을 캡슐화하고 .Aggregate의 구현이 다음과 같이 보일 것으로 예상하는 것입니다.
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
그렇다면 .Aggregate는 가독성에 도움이 될까요? 일반적으로 .Where, .Select, .OrderBy 등이 가독성에 크게 도움이된다고 생각하기 때문에 LINQ를 좋아합니다 (인라인 된 계층 형 .Selects를 피하는 경우). Aggregate는 완전성 이유로 Linq에 있어야하지만 개인적으로 .Aggregate는 잘 작성된 foreach에 비해 가독성을 추가한다고 확신하지 않습니다.
사진은 천 단어의 가치가 있습니다
알림 :
Func<X, Y, R>
유형 의 결과를 반환하는X
and 유형의 두 입력이있는 함수입니다 .Y
R
Enumerable.Aggregate에는 세 가지 오버로드가 있습니다.
과부하 1 :
A Aggregate<A>(IEnumerable<A> a, Func<A, A, A> f)
예:
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)
예:
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가 아닙니다.
오버로드 2와 그 결과를 변환하는 함수를 사용하여 더 간결하게 작성할 수 있습니다.
삽화는 이 훌륭한 블로그 포스트 에서 채택되었습니다 .
Aggregate는 기본적으로 데이터를 그룹화하거나 합산하는 데 사용됩니다.
MSDN에 따르면 "집계 함수는 시퀀스에 누산기 함수를 적용합니다."
예 1 : 배열의 모든 숫자를 더합니다.
int[] numbers = new int[] { 1,2,3,4,5 };
int aggregatedValue = numbers.Aggregate((total, nextValue) => total + nextValue);
* 중요 : 기본적으로 초기 집계 값은 수집 순서에서 1 개 요소입니다. 즉 : 총 변수 초기 값은 기본적으로 1입니다.
변수 설명
total : 함수가 반환 한 합계 값 (집계 값)을 보유합니다.
nextValue : 배열 시퀀스의 다음 값입니다. 이 값은 집계 된 값, 즉 합계에 추가 된 것입니다.
예 2 : 배열의 모든 항목을 추가합니다. 또한 10부터 더하기 시작하도록 초기 누산기 값을 설정합니다.
int[] numbers = new int[] { 1,2,3,4,5 };
int aggregatedValue = numbers.Aggregate(10, (total, nextValue) => total + nextValue);
인수 설명 :
첫 번째 인수는 배열의 다음 값으로 덧셈을 시작하는 데 사용되는 초기 (시작 값, 즉 시드 값)입니다.
두 번째 인수는 2 int를 취하는 func 인 func입니다.
1. total : 계산 후 func에서 반환 한 합계 값 (집계 값) 이전과 동일하게 유지됩니다.
2.nextValue : : 배열 시퀀스의 다음 값입니다. 이 값은 집계 된 값, 즉 합계에 추가 된 것입니다.
또한이 코드를 디버깅하면 집계 작동 방식을 더 잘 이해할 수 있습니다.
Jamiec의 답변 에서 많은 것을 배웠습니다 .
CSV 문자열을 생성하는 것이 유일한 필요하다면 이것을 시도 할 수 있습니다.
var csv3 = string.Join(",",chars);
여기에 100 만 개의 문자열이있는 테스트가 있습니다.
0.28 seconds = Aggregate w/ String Builder
0.30 seconds = String.Join
소스 코드는 여기
이미 여기에있는 모든 훌륭한 답변 외에도 일련의 변형 단계를 통해 항목을 안내하는데도 사용했습니다.
변환이로 구현 된 경우 Func<T,T>
에 여러 변환을 추가하고을 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
모두가 그의 설명을했습니다. 내 설명은 그와 같습니다.
Aggregate 메서드는 컬렉션의 각 항목에 함수를 적용합니다. 예를 들어, 컬렉션 {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
이 예제에서는 람다 식 대신 Add라는 명명 된 메서드가 전달됩니다.
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; }
짧고 필수적인 정의는 다음과 같을 수 있습니다. Linq Aggregate 확장 메서드는 목록의 요소에 적용되는 일종의 재귀 함수를 선언 할 수 있습니다. 피연산자는 두 개입니다. 목록에 표시되는 순서대로 요소, 한 번에 하나의 요소, 이전 재귀 반복의 결과 또는 아직 재귀가 아닌 경우 아무 것도 표시하지 않습니다.
이런 식으로 숫자의 계승을 계산하거나 문자열을 연결할 수 있습니다.
Aggregate
Linq Sorting과 같은 Fluent API 사용 에 대한 설명 입니다.
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 루프 대신 다음과 같이 사용 하는 것은 매우 쉽습니다 .
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);
다차원 정수 배열의 열 합계에 사용되는 집계
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()
);
Select with index는 Aggregate 함수 내에서 일치하는 열을 합산하고 새 배열을 반환하는 데 사용됩니다. {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
그러나 누적 된 유형 (int)이 소스 유형 (bool)과 다르기 때문에 부울 배열의 참 수를 계산하는 것이 더 어렵습니다. 여기서 두 번째 과부하를 사용하려면 시드가 필요합니다.
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
참고 URL : https://stackoverflow.com/questions/7105505/linq-aggregate-algorithm-explained
'programing' 카테고리의 다른 글
PostgreSQL에서 데이터베이스 복사본 생성 (0) | 2020.09.30 |
---|---|
JPA와 Hibernate의 차이점은 무엇입니까? (0) | 2020.09.30 |
임의의 범위를 1–5에서 1–7로 확장 (0) | 2020.09.30 |
동적으로 명명 된 속성을 JavaScript 개체에 추가 할 수 있습니까? (0) | 2020.09.30 |
숫자가 실수인지 정수인지 어떻게 확인합니까? (0) | 2020.09.30 |