for 루프에서 <또는 <=를 사용해야합니다.


124

루프를 7 번 반복해야한다면 다음을 사용하세요.

for (int i = 0; i < 7; i++)

또는:

for (int i = 0; i <= 6; i++)

두 가지 고려 사항이 있습니다.

  • 공연
  • 가독성

성능을 위해 Java 또는 C #을 가정합니다. "미만"또는 "작거나 같음"을 사용하는 것이 중요합니까? 다른 언어에 대한 통찰력이있는 경우 어떤 언어를 선택하십시오.

가독성을 위해 0 기반 배열을 가정하고 있습니다.

UPD : 0 기반 배열에 대한 언급이 혼란 스러울 수 있습니다. 배열 요소를 반복하는 것에 대해 말하는 것이 아닙니다. 일반적인 루프입니다.

이 매직 넘버가 무엇인지 설명하는 상수를 사용하는 것에 대한 좋은 점이 아래에 있습니다. 그래서 내가 " int NUMBER_OF_THINGS = 7"를 가졌다면 " i <= NUMBER_OF_THINGS - 1"는 이상하게 보일 것입니다.


나는 말하고 싶습니다 : 전체 배열을 통해 실행되는 경우 왼쪽에 숫자를 빼거나 더하지 마십시오.
Letterman

답변:


287

첫 번째는 더 관용적 입니다. 특히 반복 횟수를 나타냅니다 (0 기반 의미에서). 1 기반 (예 : JDBC, IIRC)을 사용할 때 <=를 사용하고 싶을 수 있습니다. 그래서:

for (int i=0; i < count; i++) // For 0-based APIs

for (int i=1; i <= count; i++) // For 1-based APIs

실제 코드에서는 성능 차이가 미미할 것으로 예상됩니다.


30
성능 차이가 거의 없다는 것이 거의 보장됩니다. x86과 같은 많은 아키텍처에는 "마지막 비교에서 작거나 같음"명령이 있습니다. 성능 차이를 확인할 수있는 가장 가능성있는 방법은 제대로 구현되지 않은 일종의 해석 언어 일 것입니다.
Wedge

3
대신! = 사용을 고려 하시겠습니까? 나는 그것이 가장 명확하게 i를 루프 카운터로 설정한다고 말하고 싶습니다.
yungchin

21
나는 보통 그렇지 않습니다. 너무 낯설다. 또한 누군가가 루프 중에 실수로 i를 증가 시키면 매우 긴 루프가 될 위험이 있습니다.
Jon Skeet

5
STL 반복자를 사용한 일반 프로그래밍에서는! =를 사용해야합니다. 그것은 (우연한 이중 증가) 나에게 문제가되지 않았습니다. 나는 지수 <(또는> 내림차순)가 더 명확하고 관습 적이라는 데 동의합니다.
Jonathan Graehl

2
<를 사용하여 배열의 길이를 반복하면 JIT가 배열 액세스를 최적화합니다 (바운드 검사 제거). 따라서 <=를 사용하는 것이 더 빠를 것입니다. 그래도 난 그것을 확인하지 않은
피규

72

두 루프 모두 7 번 반복됩니다. 나는 당신이 다른 것에 대한 정말 좋은 이유가 없다면 7이있는 것이 더 읽기 쉽고 명확하다고 말하고 싶습니다.


Java를 처음 배우기 시작한 때가 기억납니다. 저는 항상 1 기반 인덱스를 사용했기 때문에 0 기반 인덱스의 개념이 싫었습니다. 그래서 나는 항상 <= 6 변형을 사용합니다 (질문에서 볼 수 있듯이). for 루프가 실제로 종료되었을 때 결국 더 혼란 스러울 것이기 때문입니다. 그것은 단지 <사용하기 간단합니다
제임스 하우

55

대학에서 8086 어셈블리를했을 때부터는 다음과 같이하는 것이 더 성능이 좋았던 것을 기억합니다.

for (int i = 6; i > -1; i--)

있었기 때문에 서명이 없으면 점프를 의미 JNS 작업 입니다. 이를 사용하면 비교 값을 얻기 위해 각주기 후에 메모리 조회가없고 비교도 수행되지 않습니다. 요즘 대부분의 컴파일러는 레지스터 사용을 최적화하므로 메모리가 더 이상 중요하지 않지만 여전히 불필요한 비교를 얻을 수 있습니다.

그건 그렇고 당신의 루프에 7이나 6을 넣는 것은 " 매직 넘버 "를 도입하는 것 입니다. 가독성을 높이려면 Intent Revealing Name과 함께 상수를 사용해야합니다. 이렇게 :

const int NUMBER_OF_CARS = 7;
for (int i = 0; i < NUMBER_OF_CARS; i++)

편집 : 사람들은 어셈블리를 얻지 못하므로 더 완전한 예가 분명히 필요합니다.

(i = 0; i <= 10; i ++)에 대해 수행하는 경우 다음을 수행해야합니다.

    mov esi, 0
loopStartLabel:
                ; Do some stuff
    inc esi
                ; Note cmp command on next line
    cmp esi, 10
    jle exitLoopLabel
    jmp loopStartLabel
exitLoopLabel:

for (int i = 10; i> -1; i--)를 수행하면 다음과 같이 벗어날 수 있습니다.

    mov esi, 10
loopStartLabel:
                ; Do some stuff
    dec esi
                ; Note no cmp command on next line
    jns exitLoopLabel
    jmp loopStartLabel
exitLoopLabel:

방금 확인한 결과 Microsoft의 C ++ 컴파일러는이 최적화를 수행하지 않지만 다음과 같은 경우 수행합니다.

for (int i = 10; i >= 0; i--) 

따라서 Microsoft C ++ †를 사용하고 있고 오름차순이나 내림차순이 차이가없는 경우 빠른 루프를 사용하려면 다음을 사용해야합니다.

for (int i = 10; i >= 0; i--)

다음 중 하나가 아닌 :

for (int i = 10; i > -1; i--)
for (int i = 0; i <= 10; i++)

그러나 솔직히 "for (int i = 0; i <= 10; i ++)"의 가독성을 얻는 것이 일반적으로 하나의 프로세서 명령을 누락하는 것보다 훨씬 더 중요합니다.

† 다른 컴파일러는 다른 작업을 수행 할 수 있습니다.


4
"매직 넘버"사례는 일반적으로 <=보다 <를 사용하는 것이 더 나은 이유를 잘 보여줍니다.
Rene Saarsoo

11
다른 버전은 "for (int i = 10; i--;)"입니다. 어떤 사람들은 "for (int i = 10; i-> 0;)"를 사용하고 조합->이 의미하는 것처럼 가장합니다.
Zayenz 09.08.24

2
어셈블리 코드 +1
neuro

1
그러나 시간이 실제로 루프 카운터를 사용하게 될 때, 예를 들어 배열 인덱싱 7-i을 위해 거꾸로 계산하여 얻은 최적화를 휩쓸어 버릴 필요가 있습니다.
Lie Ryan 2011

@Lie, 이것은 항목을 순방향으로 처리해야하는 경우에만 적용됩니다. 이러한 종류의 루프에서 대부분의 작업을 수행하면 원하는 순서로 루프의 항목에 적용 할 수 있습니다. 예를 들어 값을 검색하는 경우 목록의 끝에서 시작하여 작업을 시작하거나 목록의 시작에서 작업을 수행 한 다음 작업을 수행하는 것은 중요하지 않습니다 (목록의 어느 끝이 항목인지 예측할 수 없다고 가정). 좋아하고 메모리 캐싱은 문제가되지 않습니다).
Martin Brown

27

<= array.length-1보다 읽기 쉽기 때문에 항상 <array.length를 사용합니다.

또한 <7이고 0 인덱스로 시작한다는 것을 알고 있다면 숫자가 반복 횟수라는 것은 직관적이어야합니다.


루프에서 사용할 때는 항상 Length 함수의 비용을 확인해야합니다. 예를 들어 C / C ++에서 strlen을 사용하면 비교하는 데 걸리는 시간이 엄청나게 늘어날 것입니다. 이것은 strlen이 전체 문자열을 반복하여 루프의 모든 반복이 아닌 한 번만 수행하고 싶은 답을 찾아야하기 때문입니다.
Martin Brown

2
@Martin Brown : Java에서 (그리고 C #을 믿습니다) String은 불변이고 Array는 불변 길이이기 때문에 String.length와 Array.length는 일정합니다. 그리고 String.length 및 Array.length는 필드 (함수 호출 대신)이므로 O (1)이어야합니다. C ++의 경우, 왜 도대체 C- 문자열을 사용하고 있습니까?
Lie Ryan 2011

18

최적화 관점에서 보면 중요하지 않습니다.

코드 스타일 관점에서 보면 <. 이유:

for ( int i = 0; i < array.size(); i++ )

훨씬 더 읽기 쉽습니다.

for ( int i = 0; i <= array.size() -1; i++ )

또한 <는 즉시 반복 횟수를 제공합니다.

<에 대한 또 다른 투표는 실수로 인한 실수를 방지 할 수 있다는 것입니다.


10

@Chris, .NET에서 비용이 많이 드는 .Length에 대한 귀하의 진술은 사실이 아니며 단순한 유형의 경우 정반대입니다.

int len = somearray.Length;
for(i = 0; i < len; i++)
{
  somearray[i].something();
}

실제로보다 느립니다

for(i = 0; i < somearray.Length; i++)
{
  somearray[i].something();
}

나중은 런타임에 의해 최적화 된 경우입니다. 런타임은 i가 배열에 대한 유효한 인덱스임을 보장 할 수 있으므로 경계 검사가 수행되지 않습니다. 전자의 경우 런타임은 루프 전에 수정되지 않았 음을 보장 할 수 없으며 모든 인덱스 조회에 대해 배열의 경계 검사를 강제합니다.


Java에서 .Length는 경우에 따라 비용이 많이들 수 있습니다. stackoverflow.com/questions/6093537/for-loop-optimization b'coz는 .lenghtOR을 호출합니다 .size. 나는 확실하지 않지만 자신이 없지만 확인하고 싶습니다.
Ravi Parekh

6

성능과 관련하여 효과적인 차이는 없습니다. 따라서 해결하려는 문제의 맥락에서 이해하기 쉬운 것을 사용합니다.


5

나는 선호한다:

for (int i = 0; i < 7; i++)

나는 그것이 "루프를 7 번 반복하는 것"으로 더 쉽게 번역된다고 생각한다.

성능에 미치는 영향에 대해 잘 모르겠습니다. 차이점이 모두 정리 될 것이라고 생각합니다.


4

Java 1.5에서는 다음을 수행 할 수 있습니다.

for (int i: myArray) {
    ...
}

따라서 어레이 케이스의 경우 걱정할 필요가 없습니다.


4

성능 차이는 없다고 생각합니다. 두 번째 형식은 확실히 더 읽기 쉽지만 마지막 반복 번호를 찾기 위해 정신적으로 하나를 뺄 필요는 없습니다.

편집 : 다른 사람들이 동의하지 않는 것을 봅니다. 개인적으로 저는 루프 구조에서 실제 인덱스 번호를보고 싶습니다. 아마도 Perl의 0..6구문을 더 연상시키기 때문일 수 (0,1,2,3,4,5,6)있습니다. 7이 표시되면 그 옆에있는 연산자를 확인하여 실제로 인덱스 7에 도달하지 않았는지 확인해야합니다.


"마지막 반복 횟수"가 "반복 횟수"보다 더 중요하다고 생각하는지 여부에 따라 다릅니다. 0 기반 API를 사용하면 항상 1 씩 다릅니다 ...
Jon Skeet

4

대부분의 사람들이 읽을 것이기 때문에 "<7"버전을 사용한다고 말하고 싶습니다. 따라서 사람들이 코드를 훑어 보면 코드를 잘못 해석 할 수 있습니다.

"<"가 "<="보다 빠른지 걱정하지 않고 가독성을 높이십시오.

속도를 높이려면 다음을 고려하십시오.

for (int i = 0; i < this->GetCount(); i++)
{
  // Do something
}

성능을 높이려면 다음과 같이 약간 재정렬 할 수 있습니다.

const int count = this->GetCount();
for (int i = 0; i < count; ++i)
{
  // Do something
}

루프에서 GetCount ()가 제거되고 (모든 루프에서 쿼리되므로) "i ++"가 "++ i"로 변경됩니다.


읽기 쉽기 때문에 두 번째 것이 더 좋지만 실제로 매번 this-> GetCount ()를 다시 계산합니까? 나는 이것을 변경할 때 이것에 잡혔고 카운트는 동일하게 유지되어 내가 할 일을 강요했다 .. while this-> GetCount ()
osp70

GetCount ()는 첫 번째 예제에서 모든 반복을 호출합니다. 두 번째 예에서는 한 번만 호출됩니다. 이 경우 필요 GetCount 당신이 외부를 유지하려고하지 않는 경우 (), 루프에 그것을 가지고 다음 매번 호출 할 수 있습니다.
Mark Ingram

예, 시도해 보았고 당신 말이 맞습니다. 죄송합니다.
osp70

i ++보다 ++ i를 사용하면 어떤 차이가 있습니까?
Rene Saarsoo

int를 사용할 때 약간의 속도가 증가하지만 자신의 클래스를 증가시키는 경우 증가가 더 클 수 있습니다. 기본적으로 ++ i는 실제 값을 증가시킨 다음 실제 값을 반환합니다. i ++는 임시 변수를 만들고 실제 변수를 증가시킨 다음 temp를 반환합니다. ++ i에서는 var 생성이 필요하지 않습니다.
Mark Ingram

4

C ++에서는 !=모든 STL 컨테이너에서 사용할 수있는를 선호합니다 . 모든 STL 컨테이너 이터레이터가 비교할 수없는 것은 아닙니다.


이것은 내가 의도 한 값에 대해 카운터를 반복 할 수있는 아주 작은 외부 기회가 있기 때문에 조금 겁이납니다. 그러면 이것을 무한 루프로 만듭니다. 가능성은 멀고 쉽게 감지되지만 < 더 안전 하다고 느낍니다 .
Rob Allen

2
코드에 이와 같은 버그가있는 경우 조용히 계속하는 것보다 충돌하고 태우는 것이 좋습니다. :-)

이것이 정답입니다. 반복자에 대한 수요가 적고 코드에 오류가있는 경우 나타날 가능성이 더 높습니다. <에 대한 주장은 근시안적입니다. CPU 시간을 지불 할 때 70 년대에는 무한 루프가 좋지 않을 수 있습니다. '! ='는 버그를 숨길 가능성이 적습니다.
David Nehme

1
반복기를 반복하는 것은 카운터를 사용한 반복과는 완전히 다른 경우입니다. ! =는 반복자에 필수적입니다.
DJClayworth

4

Edsger Dijkstra 1982 년에 이에 대한 기사썼습니다. 여기서 그는 lower <= i <upper를 주장합니다.

가장 작은 자연수가 있습니다. b) 및 d)에서와 같이 하한을 제외하면 부자 연수 영역에 언급 된 하한 인 가장 작은 자연수에서 시작하는 하위 시퀀스에 대한 힘이 적용됩니다. 그것은 추악하므로 하한의 경우 a) 및 c)에서와 같이 ≤를 선호합니다. 이제 가장 작은 자연수에서 시작하는 하위 시퀀스를 고려하십시오. 상한을 포함하면 시퀀스가 ​​빈 시퀀스로 축소 될 때 후자가 부자연스러워집니다. 그것은 추악하므로 상한선에 대해 우리는 a) 및 d)에서와 같이 <를 선호합니다. 우리는 규칙 a)가 선호된다는 결론을 내립니다.


3

첫째, 6 또는 7을 사용하지 마십시오.

사용하기 더 좋음 :

int numberOfDays = 7;
for (int day = 0; day < numberOfDays ; day++){

}

이 경우 사용하는 것보다 낫습니다

for (int day = 0; day <= numberOfDays  - 1; day++){

}

더 나은 (Java / C #) :

for(int day = 0; day < dayArray.Length; i++){

}

그리고 더 나은 (C #)

foreach (int day in days){// day : days in Java

}

역방향 루프는 실제로 더 빠르지 만 (다른 프로그래머가 아니라면) 읽기가 더 어렵 기 때문에 피하는 것이 좋습니다. 특히 C #, Java에서 ...


2

나는이 경우 7이 의미가 있다고 말하는 군중의 의견에 동의하지만 6이 중요한 경우에는 6 번째 색인까지만 개체에 대해 행동하고 있음을 분명히하고 싶다고 말하고 <=은 6을 더 쉽게 볼 수 있기 때문에 더 좋습니다.


그래서, <I 크기에 비해 <= LAST_FILLED_ARRAY_SLOT
크리스 Cudmore

2

대학으로 돌아와서이 두 연산이 CPU에서 계산 시간이 비슷하다는 것을 기억합니다. 물론 우리는 어셈블리 수준에서 이야기하고 있습니다.

그러나 C # 또는 Java를 사용하는 경우 하나가 다른 것보다 속도가 향상 될 것이라고 생각하지 않습니다. 몇 나노초를 얻는다는 것은 혼동 할 가치가 없습니다.

개인적으로 저는 비즈니스 구현 관점에서 이해하기 쉬운 코드를 작성하고 읽기 쉬운 지 확인합니다.


2

이것은 "잘못된 코드를 잘못 보이게 만들기" 범주 바로 아래에 있습니다.

Java 또는 C #과 같은 제로 기반 인덱싱 언어에서 사람들은 index < count조건의 변화에 익숙합니다 . 따라서이 사실상의 규칙을 활용하면 하나의 오류가 더 명확 해집니다.

성능과 관련하여 : 메모리 풋 프린트 가치가있는 좋은 컴파일러는 문제가없는 것처럼 렌더링해야합니다.


2

약간 제쳐두고 .Net에서 배열이나 다른 컬렉션을 반복 할 때

foreach (string item in myarray)
{
    System.Console.WriteLine(item);
}

숫자 for 루프보다 더 읽기 쉽습니다. 물론 이것은 실제 카운터 Int 자체가 루프 코드에서 사용되지 않는다고 가정합니다. 성능 변화가 있는지 모르겠습니다.


또한 루프 중에 컬렉션 크기를 수정하지 않아야합니다.
Jeff B

For (i = 0, i <myarray.count, i ++)
Rob Allen

1

i <7을 작성하는 데는 여러 가지 좋은 이유가 있습니다. 7 번 반복되는 루프에 숫자 7이 있으면 좋습니다. 성능은 사실상 동일합니다. 거의 모든 사람들이 i <7을 씁니다. 가독성을 위해 작성하는 경우 모든 사람이 즉시 인식 할 수있는 양식을 사용하십시오.


1

나는 항상 선호했습니다.

for ( int count = 7 ; count > 0 ; -- count )

당신의 근거는 무엇입니까? 진심으로 관심이 있습니다.
Chris Cudmore

리버스 루핑에는 완벽하게 괜찮습니다. 그런 것이 필요하다면
ShoeLace

1
한 가지 이유는 uP 수준에서 0이 빠르다는 것입니다. 또 다른 하나는 그것이 나에게 잘 읽고 카운트를 통해 몇 번 더 남았는지 쉽게 알 수 있다는 것입니다.
kenny

1

<를 사용하는 습관을 들이면 배열을 반복 할 때 사용자와 독자 모두에게 일관성이 있습니다. 모든 사람이 표준 컨벤션을 갖는 것이 더 간단 할 것입니다. 그리고 0 기반 배열이있는 언어를 사용하는 경우 <가 규칙입니다.

이것은 <와 <= 사이의 성능 차이보다 거의 확실히 중요합니다. 먼저 기능과 가독성을 목표로 한 다음 최적화하십시오.

또 다른 참고 사항은 i ++보다는 ++ i를하는 것이 더 낫다는 것입니다. 페치 및 증분은 임시 증분이 필요하고 반입은 필요하지 않기 때문입니다. 정수의 경우 컴파일러는 임시 거리를 최적화 할 수 있지만 반복 유형이 더 복잡하면 불가능할 수 있습니다.


1

매직 넘버를 사용하지 마십시오.

왜 7입니까? (또는 그 문제에 대해 6).

사용하려는 번호에 올바른 기호를 사용하십시오.

어떤 경우에는 사용하는 것이 더 낫다고 생각합니다

for ( int i = 0; i < array.size(); i++ )

1

'<'및 '<='연산자는 정확히 동일한 성능 비용입니다.

'<'연산자는 표준이며 0부터 시작하는 루프에서 읽기가 더 쉽습니다.

i ++ 대신 ++ i를 사용하면 C ++에서 성능이 향상되지만 C #에서는 성능이 향상되지 않습니다. Java에 대해 잘 모릅니다.


1

사람들이 관찰했듯이 언급 한 두 가지 대안 중 어느 것도 차이가 없습니다. 이를 확인하기 위해 JavaScript에서 간단한 벤치마킹을 수행했습니다.

여기 에서 결과를 볼 수 있습니다 . 여기서 명확하지 않은 것은 1 차와 2 차 테스트의 위치를 ​​바꾸면 두 테스트의 결과가 바뀐다는 것입니다. 이것은 분명히 메모리 문제입니다. 그러나 세 번째 테스트, 반복 순서를 반대로하는 테스트는 분명히 더 빠릅니다.


두 번째 경우에 i = 1로 시작하는 이유는 무엇입니까?
Eugene Katz

음, 아마 어리석은 실수일까요? 나는 이것을 꽤 빨리, 아마 15 분 정도 채찍질했다.
David Wees

1

모두가 말했듯이 배열 외부에 대해서도 0 인덱스 반복기를 사용하는 것이 일반적입니다. 모든 것이에서 시작 0하고 끝나고 n-1하한이 항상 <=이고 상한이 항상 <인 경우 코드를 검토 할 때해야 할 생각이 훨씬 적습니다.


1

좋은 질문입니다. 내 대답 : 유형 A 사용 ( '<')

  • 얼마나 많은 반복이 있는지 명확하게 볼 수 있습니다 (7).
  • 두 끝점의 차이는 범위의 너비입니다.
  • 문자가 적을수록 가독성이 높아집니다.
  • 마지막 요소i < strlen(s)색인 보다 총 요소 수가 더 많으 므로 균일 성이 중요합니다.

또 다른 문제는이 전체 구조에 있습니다. 3 번i 표시 되므로 오타가 발생할 수 있습니다. for-loop 구조는 무엇을해야하는지 대신 어떻게해야하는지 말합니다 . 나는 이것을 채택하는 것이 좋습니다.

BOOST_FOREACH(i, IntegerInterval(0,7))

이것은 더 명확하고 동일한 asm 명령어 등을 exaclty하기 위해 컴파일됩니다. 원한다면 IntegerInterval 코드를 요청하십시오.


1

너무 많은 답변이 있지만 추가 할 것이 있다고 생각합니다.

내가 선호하는 것은 리터럴 숫자가 "i"가 루프에서 취할 값 을 명확하게 보여주는 입니다. 따라서 0부터 시작하는 배열을 반복하는 경우 :

for (int i = 0; i <= array.Length - 1; ++i)

그리고 배열을 반복하지 않고 반복하는 경우 1부터 7까지 계산하는 것은 매우 직관적입니다.

for (int i = 1; i <= 7; ++i)

가독성은 프로파일 링 할 때까지 성능을 능가합니다. 그때까지는 컴파일러 나 런타임이 코드로 무엇을할지 모르기 때문입니다.


1

!=대신 사용할 수도 있습니다 . 이렇게하면 초기화에서 오류가 발생하면 무한 루프가 발생하여 오류가 더 일찍 발견되고 이로 인해 발생하는 모든 문제가 루프에 갇히도록 제한됩니다. 그것).


0

어느 쪽이든 괜찮다고 생각하지만, 선택하면 둘 중 하나를 고수하십시오. <=을 사용하는 데 익숙하다면 <를 사용하지 말고 그 반대도 마찬가지입니다.

나는 <=를 선호하지만 0에서 시작하는 인덱스로 작업하는 상황에서는 아마도 <를 사용하려고 할 것입니다. 그래도 모두 개인적인 취향입니다.


0

엄격히 논리적 관점에서 보면 평등을 테스트 하는 정확한 이유 < count보다 더 효율적 이라고 생각해야 합니다.<= count<=


나는 그렇게 생각하지 않는다. 어셈블러에서는 cmp eax, 7 jl LOOP_START 또는 cmp eax, 6 jle LOOP_START 모두 동일한 양의 사이클이 필요합니다.
Treb

<=는! (>)로 구현 될 수 있습니다.
JohnMcG

! (>)는 여전히 두 개의 명령어이지만 Treb은 JLE와 JL이 모두 동일한 수의 클록 사이클을 사용하므로 <및 <=가 동일한 시간을 사용한다는 점이 정확합니다.
Jacob Krall
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.