순차 콜렉션이 인덱스 0 또는 인덱스 1에서 시작해야합니까?


25

여러 채널이있는 장치의 개체 모델을 만들고 있습니다. 클라이언트와 내가 사용하는 명사는 ChannelChannelSet입니다. ( "세트"는 순서가 있고 적절한 세트가 아니기 때문에 의미 적으로 정확하지 않습니다. 그러나 그것은 다른 시간에 대한 문제입니다.)

C #을 사용하고 있습니다. 사용 예는 다음과 같습니다 ChannelSet.

// load a 5-channel ChannelSet
ChannelSet channels = ChannelSetFactory.FromFile("some_5_channel_set.json");

Console.Write(channels.Count);
// -> 5

foreach (Channel channel in channels) {
    Console.Write(channel.Average);
    Console.Write(",  ");
}
// -> 0.3,  0.3,  0.9,  0.1,  0.2

모두 멋쟁이입니다. 그러나 클라이언트는 프로그래머가 아니며 인덱싱 0 으로 인해 혼란 스러울 것입니다. 첫 번째 채널은 채널 1입니다. 그러나 C #과의 일관성을 유지하기 위해 ChannelSet인덱스를 0에서 유지하고 싶습니다 .

이로 인해 개발자 팀과 고객이 상호 작용할 때 연결이 끊어 질 수 있습니다. 그러나 이것이 코드베이스 내에서 이것이 어떻게 처리되는지에 대한 불일치가 잠재적 인 문제입니다. 예를 들어, 최종 사용자 ( 1 인덱싱으로 생각하는 )가 채널 13을 편집 하는 UI 화면이 있습니다 .

채널 13 또는 12?

Save버튼은 결국 어떤 코드를 만들 것입니다. ChannelSet1이 색인 된 경우 :

channels.GetChannel(13).SomeProperty = newValue;  // notice: 13

또는 인덱스가 0 인 경우 :

channels.GetChannel(12).SomeProperty = newValue;  // notice: 12

이 문제를 어떻게 처리해야할지 잘 모르겠습니다. ChannelSetC # 유니버스의 다른 모든 배열 및 목록 인터페이스와 일치 하는 정렬 된 정수 색인 항목 목록 ( ) 을 유지하는 것이 좋습니다 (0 인덱싱 ChannelSet). 그러나 UI와 백엔드 사이의 모든 코드는 번역이 필요하며 (1을 빼면) 교활하고 일반적인 off-by-one 오류가 이미 얼마나 많은지 알고 있습니다.

그래서, 당신과 같은 결정이 당신을 물린 적이 있습니까? 인덱스를 0 또는 1로해야합니까?


60
"배열 지수는 0 또는 1에서 시작해야 하는가? 0.5에 대한 내 타협은 적절한 고려없이 거부되었다." — Stan Kelly-Bootle
gnat

2
@gnat 사무실에서 혼자있는 것이 좋습니다. 컴퓨터 화면을 쳐다 보는 웃음 소리는 대개 생산성을 나타내는 좋은 지표는 아닙니다!
kdbanman

4
세트에서 채널의 위치로 채널을 식별해야합니까? 대신 다른 것으로 식별 할 수 없습니까 (예 : TV 채널 인 경우 주파수)?
svick

@ svick, 그게 좋은 지적입니다. 나중에 다른 식별자로 채널 액세스를 허용 할 수 있지만 클라이언트의 기본 용어는 실제로 하나의 색인 된 채널 번호 인 것 같습니다.
kdbanman

빠른 읽기에서 효율성의 문제로 인덱스를 사용할 필요가없는 것처럼 보이므로 일종의 Map을 사용하여 어레이에 대해 생각할 필요없이 채널 ID와 채널을 직접 연결할 수 있습니다. 나는 이것이 Rob이 얻는 것이라고 생각하며, 인덱스 사용을 선택하면 그의 해결책에도 동의합니다.
Matthew 읽기

답변:


24

Channel의 위치와 식별자를 접목하는 것처럼 느껴집니다 ChannelSet. 다음은 코드 / 코멘트가 현재 어떻게 보이는지에 대한 시각화입니다.

public sealed class ChannelSet
{
    private Channel[] channels;
    /// <summary>Retrieves the specified channel</summary>
    /// <param name="channelId">The id of the channel to return</param>
    Channel GetChannel(int channelId)
    {
        return channels[channelId-1];
    }
}

당신이 한 것처럼 느낌을하지 결정 하기 때문 Channel내에서의이 ChannelSet상위을 번호로 식별 및 하한가 인덱스해야하며, 따라서 그것의 C #을, 0을 기반으로. 각 채널을 자연스럽게 참조하는 방법이 1과 X 사이의 숫자이면 1과 X 사이의 숫자를 참조하십시오. 색인을 만들려고하지 마십시오.

0 기반 인덱스로 액세스 할 수있는 방법을 실제로 제공하려면 (최종 사용자 또는 코드를 소비하는 개발자에게 어떤 이점이 있습니까?) 인덱서 를 구현하십시오 .

public sealed class ChannelSet
{
    private Channel[] channels;
    /// <summary>Retrieves the specified channel</summary>
    /// <param name="channelId">The id of the channel to return</param>
    public Channel GetChannel(int channelId)
    {
        return channels[channelId-1];
    }

    /// <summary>Return the channel at the specified index</summary>
    public Channel this[int index]
    {
        return channels[index];
    }
}

2
이것은 정말 완전하고 간결한 답변입니다. 그것은 다른 답변에서 파생 된 접근법입니다.
kdbanman

7
Passersby, 나는이 답변을 수락했지만이 스레드에는 좋은 답변이 가득합니다.
kdbanman

@Rob. "인덱서 사용법을 알고 있습니다." -네오
Jason P Sallinger

35

색인 1로 UI를 표시하고 코드에서 색인 0을 사용하십시오.

즉, 저는 이와 같은 오디오 장치를 사용하여 채널에 인덱스 1을 사용했으며 코드가 좌절을 피하기 위해 "인덱스"또는 인덱서를 사용하지 않도록 설계했습니다. 일부 프로그래머는 여전히 불만을 제기하여 변경했습니다. 그런 다음 다른 프로그래머들이 불평했습니다.

하나만 골라 붙이십시오. 소프트웨어를 문 밖으로 가져 오는 거대한 계획에서 해결해야 할 더 큰 문제가 있습니다.


12
이에 대한 예외 : 1 기반 언어를 사용하는 경우. 코드는 나머지 코드 및 언어와 일치해야하므로 C #에서는 0 기반, VBA (!) 또는 Lua에서는 1 기반입니다. UI는 인간이 기대하는 것이어야합니다 (거의 항상 1 기반).
user253751

1
@ immibis-좋은 지적, 나는 그것을 생각하지 못했습니다.
Telastyn

3
BASIC에서 옛날 프로그래밍에 대해 가끔 놓칠 수있는 한 가지는 "OPTION BASE"입니다.
Brian Knoblauch

1
@ BlueRaja-DannyPflughoeft : 항상 Option Base바보 라고 생각했지만 배열에서 개별 하한을 개별적으로 지정할 수있는 일부 언어가 제공되는 기능을 좋아했습니다. 예를 들어 연도 별 데이터 배열이있는 경우 배열에 차원을 가질 [firstYear..lastYear]수있는 것보다 항상 element에 액세스하는 것보다 좋을 수 있습니다 [thisYear-firstYear].
supercat

1
@ BlueRaja-DannyPflughoeft 한 남자의 버그는 다른 남자의 기능입니다 ...
Brian Knoblauch

21

둘 다 사용하십시오.

UI를 핵심 코드와 혼합하지 마십시오. 내부적으로 (라이브러리로서) 최종 사용자가 배열의 각 요소를 어떻게 호출하는지 "알지 않고"코딩해야합니다. "자연적인"0 인덱스 배열 및 컬렉션을 사용하십시오.

UI와 데이터를 결합하는 프로그램의 일부인보기는 사용자의 정신 모델과 실제 작업을 수행하는 '라이브러리'간에 데이터를 올바르게 변환하도록주의해야합니다.

왜 이것이 더 낫습니까?

  • 코드는 더 깔끔하고 색인 생성을 해석 할 수 있습니다. 또한 프로그래머가 부 자연스러운 규칙을 따르고 기억하지 않아도되므로 도움이됩니다.
  • 사용자는 원하는 색인을 사용합니다. 당신은 그들을 귀찮게하고 싶지 않습니다.

12

두 가지 다른 각도에서 컬렉션을 볼 수 있습니다.

(1) 우선 배열 또는 목록과 같은 일반적인 순차적 컬렉션 입니다. 0컨벤션에 따라 인덱스 는 분명히 올바른 솔루션입니다. 충분한 항목을 할당하고 채널 번호를 색인에 할당하십시오 1.

(2) 컬렉션은 본질적으로 채널 식별자와 채널 정보 객체 간의 매핑 입니다. 채널 식별자는 순차적 인 정수 범위입니다. 내일은 같은 것일 수 있습니다 [1, 2, 3, 3a, 4, 4.1, 6, 8, 13]. 이 순서 집합을 매핑 키로 사용합니다.

접근 방식 중 하나를 선택하고 문서화 한 다음이를 준수하십시오. 유연성 관점에서, 채널 번호 (적어도 표시된 이름)의 표현은 미래에 상대적으로 변경 될 가능성이 높기 때문에 (2)로 넘어갑니다.


나는 당신의 대답의 2 부를 정말로 감사합니다. 나는 그런 것을 채택 할 것이라고 생각합니다. 인덱스 접근은 ( channels[int])입니다 제로 인덱스 정수 및 가져 오기 접근 GetChannelByFrequency, GetChannelByName, GetChannelByNumber유연한 될 것입니다.
kdbanman

1
두 번째 접근 방식이 가장 좋습니다. 지역 방송사 에서 LCN 이 1에서 201 사이 인 32 개의 채널을 제공합니다. 이것은 희소 배열 (84 % 비어 있음)이 필요합니다. 개념적으로 전화 번호로 색인 된 배열에 사람 모음을 저장하는 것과 같습니다.
켈리 토마스

3
또한, UI는 드롭로 표시있어 너무 작은 모든 컬렉션은 극히 데이터 구조로 배열의 특정 성능 특성 이점 가능성. 따라서 사용자가 "채널 1"이라고하는 것도 코드에서 "1"이라고 할 수도 있고 a Dictionary또는을 사용할 수도 SortedDictionary있습니다.
Steve Jessop

1
가장 놀랍지 않은 원칙은 operator [] (int index)0에 기반 을 두어야하지만 operator [] (IOrdered index)그렇지 않은 것을 암시합니다 . (근사한 구문에 대한 사과, 내 C #은 매우 녹슨입니다.)
9000

2
@ kdbanman : 개인적으로, []이 키를 임의의 키로 조회하기 위해이 과부하를 사용하는 언어로 과부하가 걸리 자마자 프로그래머는 키가 시작되고 0연속적 이라고 가정 해서는 안되며 그 습관을 예리하게해야합니다. 그들은에서 시작 수도 0, 또는 1, 또는 1000, 또는 문자열 "Channel1", 그 연산자를 사용하여 전체 지점의 []임의의 키. OTOH, 이것이 C 였고 누군가가 " 0시작하기 위해 사용되지 않은 배열에 요소 를 남겨 두어야합니까? 1"라고 말하면 "분명히 예"라고 말하지 않을 것입니다. "아니".
Steve Jessop

3

모두와 그들의 개는 0부터 시작하는 인덱스를 사용합니다. 응용 프로그램 내에서 단일 기반 인덱스를 사용하면 유지 관리 문제가 영원히 발생합니다.

이제 사용자 인터페이스에 표시되는 내용은 전적으로 귀하와 귀하의 클라이언트에게 달려 있습니다. 채널 번호 i와 함께 채널 번호로 i + 1을 표시하면 고객이 만족할 수 있습니다.

클래스를 공개하면 프로그래머에게 공개합니다. 0부터 시작하는 인덱스로 혼란스러워하는 사람들은 프로그래머가 아니므로 여기서 연결이 끊어집니다.

UI와 코드 간의 변환에 대해 걱정하는 것 같습니다. 그래도 걱정이되면 채널 번호의 사용자 인터페이스 표현이 포함 된 클래스를 만드십시오. 한 번의 호출은 숫자 12를 UI 표시 "채널 13"으로 바꾸고 한 번의 호출은 "채널 13"을 숫자 12로 바꾸는 것입니다. 바꾸다. 고객이 로마 숫자 또는 문자 A에서 Z까지를 요청하면 작동합니다.


1
개가 어떤 종류의 색인을 사용하는지 알려주지 않기 때문에 답을 확인할 수 없습니다.
armani

3

인덱싱과 ID라는 두 가지 개념을 혼합하고 있습니다. 그것들은 같은 것이 아니며 혼동해서는 안됩니다.

인덱싱의 목적은 무엇입니까? 빠른 임의 접근. 성능에 문제가없고 설명이없는 경우 인덱스가있는 컬렉션을 사용하는 것은 불필요하며 우연한 것일 수 있습니다.

당신 (또는 당신의 팀)이 인덱스와 정체성에 의해 혼란스러워하는 경우 인덱스가 없어서 그 문제를 해결할 수 있습니다. 사전이나 열거 가능한 것을 사용하고 값으로 채널을 가져옵니다.

channels.First(c=> c.Number == identity).SomeProperty = newValue;
channels[identity].SomeProperty = newValue;

첫 번째는 더 명확하고 두 번째는 더 짧습니다. 동일한 IL로 변환되거나 변환되지 않을 수 있지만 드롭 다운에 이것을 사용하면 충분히 빠릅니다.


2

ChannelSet클래스를 통해 사용자가 상호 작용할 것으로 예상 되는 인터페이스를 노출하고 있습니다 . 나는 그들이 가능한 한 자연스럽게 사용하도록 만들 것입니다. 사용자가 0 대신 1부터 계산을 시작할 것으로 예상되면이 클래스와 사용법을 염두에두고 노출하십시오.


1
거기에 문제가있다! 최종 사용자는 1부터 시작하고 개발자 (나 자신도 포함)는 0부터 시작해야합니다.
kdbanman

1
따라서 최종 사용자의 요구에 맞게 수업을 조정하는 것이 좋습니다.
Bernard

2

선언은 객체의 크기를 보여주기 때문에 0 기반 색인 생성이 대중적이며 중요한 사람들에 의해 방어되었습니다.

최신 컴퓨터에서는 사용자가 사용할 컴퓨터와 함께 객체의 크기가 중요합니까?

langauge (C #)가 배열의 첫 번째 요소와 마지막 요소를 선언하는 명확한 방법을 지원하지 않으면 더 큰 배열을 선언하고 선언 된 상수 (또는 동적 변수)를 사용하여 논리 시작과 끝을 식별 할 수 있습니다 배열의 활성 영역

UI 객체의 경우 매핑에 사전 객체를 거의 사용할 여유가 있으며 (천만 객체가 있으면 UI 문제가 있음) 가장 좋은 사전 객체가 양쪽 끝에 공간이 낭비되어 중요한 것을 잃지 않았습니다.


실제로 0 기반 인덱스와 1 기반 인덱스는 배열이 메모리에 구성되는 방식과 관련이 있습니다. 0부터 시작하는 인덱스는 외부 메모리 (어레이 외부)를 사용하여 크기를 결정하는 반면, 1부터 시작하는 인덱스는 내부 메모리 (인덱스 0)를 사용하여 배열의 크기 ( 보통 어쨌든 ) 를 기억합니다 . "객체의 크기"에는 인기가 없지만 일반적으로 메모리가 내부적으로 배열에 할당되는 방식과 관련이 있습니다. 어쨌든, 나는 여기저기서 바이트를 무모하게 누출시키지 않는 것이 좋습니다. 그러나 사전은 일반적으로 더 나은 아이디어입니다.
phyrfox

@phyrfox : 제로 기반 색인 생성을 선호하는 추상화는 색인이 객체 사이의 위치 식별한다는 것입니다 . 첫 번째 개체는 인덱스 0과 1 사이에 있고 두 번째 개체는 1과 2 사이에 있습니다. 특별한 코너 케이스가 없어도 비어있을 수 있습니다. 0부터 시작하는 색인화를 사용하여 "항목 간 표시"모델을 채택 할 때 6 개의 항목 목록은 0에서 6까지의 범위에 걸쳐 있습니다. 1부터 시작하여 1부터 7까지의 색인이 생성됩니다.이 추상화는 "한 쪽"포인터와 잘 맞습니다.
supercat
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.