인덱스 및 범위Indices and ranges

범위와 인덱스는 시퀀스의 단일 요소 또는 범위에 액세스하기 위한 간결한 구문을 제공합니다.Ranges and indices provide a succinct syntax for accessing single elements or ranges in a sequence.

이 자습서에서는 다음과 같은 작업을 수행하는 방법을 알아봅니다.In this tutorial, you'll learn how to:

  • 시퀀스에서 범위에 구문을 사용합니다.Use the syntax for ranges in a sequence.
  • 각 시퀀스의 시작 및 끝에 대한 설계 의사 결정을 이해합니다.Understand the design decisions for the start and end of each sequence.
  • IndexRange 형식에 대한 시나리오를 살펴봅니다.Learn scenarios for the Index and Range types.

인덱스 및 범위에 대한 언어 지원Language support for indices and ranges

이 언어 지원은 다음과 같은 두 가지 새 형식 및 두 가지 새 연산자를 사용합니다.This language support relies on two new types and two new operators:

  • System.Index는 인덱스를 시퀀스로 표현합니다.System.Index represents an index into a sequence.
  • 인덱스가 시퀀스의 끝을 기준으로 하도록 지정하는 끝부터 인덱스 연산자 ^입니다.The index from end operator ^, which specifies that an index is relative to the end of a sequence.
  • System.Range는 시퀀스의 하위 범위를 나타냅니다.System.Range represents a sub range of a sequence.
  • 범위의 시작과 끝을 피연산자로 지정하는 범위 연산자 ..입니다.The range operator .., which specifies the start and end of a range as its operands.

인덱스에 대한 규칙을 사용하여 시작하겠습니다.Let's start with the rules for indices. sequence배열을 고려합니다.Consider an array sequence. 0 인덱스는 sequence[0]과 동일합니다.The 0 index is the same as sequence[0]. ^0 인덱스는 sequence[sequence.Length]와 동일합니다.The ^0 index is the same as sequence[sequence.Length]. sequence[^0]sequence[sequence.Length]처럼 예외를 throw합니다.Note that sequence[^0] does throw an exception, just as sequence[sequence.Length] does. n이 어떤 숫자이든, 인덱스 ^nsequence[sequence.Length - n]과 동일합니다.For any number n, the index ^n is the same as sequence[sequence.Length - n].

string[] words = new string[]
{
                // index from start    index from end
    "The",      // 0                   ^9
    "quick",    // 1                   ^8
    "brown",    // 2                   ^7
    "fox",      // 3                   ^6
    "jumped",   // 4                   ^5
    "over",     // 5                   ^4
    "the",      // 6                   ^3
    "lazy",     // 7                   ^2
    "dog"       // 8                   ^1
};              // 9 (or words.Length) ^0

다음과 같이 ^1 인덱스를 사용하여 마지막 단어를 가져올 수 있습니다.You can retrieve the last word with the ^1 index. 초기화 아래에 다음 코드를 추가합니다.Add the following code below the initialization:

Console.WriteLine($"The last word is {words[^1]}");

한 범위는 어떤 범위의 시작을 지정합니다.A range specifies the start and end of a range. 여러 범위는 배타적입니다. 즉, 이 범위에 포함되지 않습니다.Ranges are exclusive, meaning the end is not included in the range. [0..sequence.Length]가 전체 범위를 나타내는 것처럼 [0..^0] 범위는 전체 범위를 나타냅니다.The range [0..^0] represents the entire range, just as [0..sequence.Length] represents the entire range.

다음 코드는 “quick”, “brown”, “fox”라는 단어를 포함하는 하위 범위를 만듭니다.The following code creates a subrange with the words "quick", "brown", and "fox". 이 하위 범위에는 words[1]부터 words[3]까지 포함되며,It includes words[1] through words[3]. words[4] 요소가 범위에 없습니다.The element words[4] isn't in the range. 다음 코드를 같은 메서드에 추가합니다.Add the following code to the same method. 대화형 창의 맨 아래에 다음 코드를 복사하여 붙여넣습니다.Copy and paste it at the bottom of the interactive window.

string[] quickBrownFox = words[1..4];
foreach (var word in quickBrownFox)
    Console.Write($"< {word} >");
Console.WriteLine();

다음 코드는 “lazy”와 “dog”를 포함하는 하위 범위를 만듭니다.The following code creates a subrange with "lazy" and "dog". 이 하위 범위에는 words[^2]words[^1]이 포함되며.It includes words[^2] and words[^1]. 끝 인덱스 words[^0]는 포함되지 않습니다.The end index words[^0] isn't included. 다음 코드도 추가합니다.Add the following code as well:

string[] lazyDog = words[^2..^0];
foreach (var word in lazyDog)
    Console.Write($"< {word} >");
Console.WriteLine();

다음 예제는 시작만, 끝만, 그리고 시작과 끝이 모두 열린 범위를 만듭니다.The following examples create ranges that are open ended for the start, end, or both:

string[] allWords = words[..]; // contains "The" through "dog".
string[] firstPhrase = words[..4]; // contains "The" through "fox"
string[] lastPhrase = words[6..]; // contains "the, "lazy" and "dog"
foreach (var word in allWords)
    Console.Write($"< {word} >");
Console.WriteLine();
foreach (var word in firstPhrase)
    Console.Write($"< {word} >");
Console.WriteLine();
foreach (var word in lastPhrase)
    Console.Write($"< {word} >");
Console.WriteLine();

범위나 인덱스를 변수로 선언할 수도 있습니다.You can also declare ranges or indices as variables. 그러면 이 변수를 [] 문자 사이에 사용할 수 있습니다.The variable can then be used inside the [ and ] characters:

Index the = ^3;
Console.WriteLine(words[the]);
Range phrase = 1..4;
string[] text = words[phrase];
foreach (var word in text)
    Console.Write($"< {word} >");
Console.WriteLine();

다음 샘플에서는 이러한 선택에 대한 여러 이유를 보여 줍니다.The following sample shows many of the reasons for those choices. x, yz를 수정하여 다양한 조합을 시도해 봅니다.Modify x, y, and z to try different combinations. 실험할 때는 올바른 조합을 위해 xy보다 작고 yz보다 작은 값을 사용합니다.When you experiment, use values where x is less than y, and y is less than z for valid combinations. 새 메서드에 다음 코드를 추가합니다.Add the following code in a new method. 다양한 조합을 시도해 봅니다.Try different combinations:

int[] numbers = Enumerable.Range(0, 100).ToArray();
int x = 12;
int y = 25;
int z = 36;

Console.WriteLine($"{numbers[^x]} is the same as {numbers[numbers.Length - x]}");
Console.WriteLine($"{numbers[x..y].Length} is the same as {y - x}");

Console.WriteLine("numbers[x..y] and numbers[y..z] are consecutive and disjoint:");
Span<int> x_y = numbers[x..y];
Span<int> y_z = numbers[y..z];
Console.WriteLine($"\tnumbers[x..y] is {x_y[0]} through {x_y[^1]}, numbers[y..z] is {y_z[0]} through {y_z[^1]}");

Console.WriteLine("numbers[x..^x] removes x elements at each end:");
Span<int> x_x = numbers[x..^x];
Console.WriteLine($"\tnumbers[x..^x] starts with {x_x[0]} and ends with {x_x[^1]}");

Console.WriteLine("numbers[..x] means numbers[0..x] and numbers[x..] means numbers[x..^0]");
Span<int> start_x = numbers[..x];
Span<int> zero_x = numbers[0..x];
Console.WriteLine($"\t{start_x[0]}..{start_x[^1]} is the same as {zero_x[0]}..{zero_x[^1]}");
Span<int> z_end = numbers[z..];
Span<int> z_zero = numbers[z..^0];
Console.WriteLine($"\t{z_end[0]}..{z_end[^1]} is the same as {z_zero[0]}..{z_zero[^1]}");

인덱스 및 범위에 대한 형식 지원Type support for indices and ranges

인덱스 및 범위는 시퀀스에서 단일 요소 또는 요소의 하위 범위에 액세스하기 위한 명확하고 간결한 구문을 제공합니다.Indexes and ranges provide clear, concise syntax to access a single element or a sub-range of elements in a sequence. 인덱스 식은 일반적으로 시퀀스의 요소 형식을 반환합니다.An index expression typically returns the type of the elements of a sequence. 범위 식은 일반적으로 소스 시퀀스와 동일한 시퀀스 형식을 반환합니다.A range expression typically returns the same sequence type as the source sequence.

형식이 Index 또는 Range 매개 변수를 사용하여 인덱서를 제공하는 경우 각각 인덱스 또는 범위를 명시적으로 지원합니다.If a type provides an indexer with an Index or Range parameter, it explicitly supports indices or ranges respectively. 형식이 단일 Range 매개 변수를 사용하는 인덱서를 제공하는 경우 System.Span<T>와 같은 다른 시퀀스 형식을 반환 하도록 선택할 수 있습니다.When the type provides an indexer that takes a single Range parameter, it may choose to return a different sequence type, such as System.Span<T>.

이름이 Length 또는 Count이고 액세스 가능한 getter 및 반환 형식 int를 갖는 속성이 있는 경우 형식은 countable입니다.A type is countable if it has a property named Length or Count with an accessible getter and a return type of int. 인덱스 또는 범위를 명시적으로 지원하지 않는 countable 형식은 해당 형식에 대한 암시적 지원을 제공할 수 있습니다.A countable type that doesn't explicitly support indices or ranges may provide an implicit support for them. 자세한 내용은 기능 제한 참고암시적 인덱스 지원암시적 범위 지원 섹션을 참조하세요.For more information, see the Implicit Index support and Implicit Range support sections of the feature proposal note. 암시적 범위 지원을 사용하는 범위는 소스 시퀀스와 동일한 시퀀스 형식을 반환합니다.Ranges using implicit range support return the same sequence type as the source sequence.

예를 들어, .NET 형식 Array, String, Span<T>ReadOnlySpan<T>은 인덱스와 범위를 모두 지원합니다.For example, the following .NET types support both indices and ranges: Array, String, Span<T>, and ReadOnlySpan<T>. List<T>는 인덱스는 지원하고 범위는 지원하지 않습니다.The List<T> supports indices but doesn't support ranges.

인덱스 및 범위에 대한 시나리오Scenarios for indices and ranges

전체 시퀀스의 하위 범위에 대한 특정 분석을 수행할 때 범위와 인덱스를 사용하게 됩니다.You'll often use ranges and indices when you want to perform some analysis on subrange of an entire sequence. 새 구문에서는 어떤 하위 범위가 관련되었는지 더 명확히 이해할 수 있습니다.The new syntax is clearer in reading exactly what subrange is involved. 로컬 함수 MovingAverageRange를 인수로 사용합니다.The local function MovingAverage takes a Range as its argument. 그러면 메서드가 최솟값, 최댓값, 평균을 계산할 때 이 범위만 열거합니다.The method then enumerates just that range when calculating the min, max, and average. 프로젝트에 다음 코드를 시도해 봅니다.Try the following code in your project:

int[] sequence = Sequence(1000);

for(int start = 0; start < sequence.Length; start += 100)
{
    Range r = start..(start+10);
    var (min, max, average) = MovingAverage(sequence, r);
    Console.WriteLine($"From {r.Start} to {r.End}:    \tMin: {min},\tMax: {max},\tAverage: {average}");
}

for (int start = 0; start < sequence.Length; start += 100)
{
    Range r = ^(start + 10)..^start;
    var (min, max, average) = MovingAverage(sequence, r);
    Console.WriteLine($"From {r.Start} to {r.End}:  \tMin: {min},\tMax: {max},\tAverage: {average}");
}

(int min, int max, double average) MovingAverage(int[] subSequence, Range range) =>
    (
        subSequence[range].Min(),
        subSequence[range].Max(),
        subSequence[range].Average()
    );

int[] Sequence(int count) =>
    Enumerable.Range(0, count).Select(x => (int)(Math.Sqrt(x) * 100)).ToArray();

완료된 코드는 dotnet/samples 리포지토리에서 다운로드할 수 있습니다.You can download the completed code from the dotnet/samples repository.