Bộ sưu tập tuần tự của tôi nên bắt đầu ở chỉ số 0 hay chỉ số 1?


25

Tôi đang tạo một mô hình đối tượng cho một thiết bị có nhiều kênh. Các danh từ được sử dụng giữa khách hàng và tôi là ChannelChannelSet. ("Đặt" không chính xác về mặt ngữ nghĩa, vì nó được đặt hàng và một bộ thích hợp không. Nhưng đó là một vấn đề trong một thời gian khác.)

Tôi đang sử dụng C #. Dưới đây là một ví dụ sử dụng của 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

Tất cả là dandy. Tuy nhiên, các khách hàng không phải là lập trình viên và họ hoàn toàn sẽ bị nhầm lẫn bởi việc lập chỉ mục bằng không - kênh đầu tiên là kênh 1 cho họ. Nhưng, để thống nhất với C #, tôi muốn giữ ChannelSetchỉ số từ 0 .

Điều này chắc chắn sẽ gây ra một số ngắt kết nối giữa nhóm nhà phát triển của tôi và khách hàng khi họ tương tác. Nhưng tệ hơn, bất kỳ sự không nhất quán nào trong cách xử lý vấn đề này trong cơ sở mã là một vấn đề tiềm ẩn. Ví dụ: đây là màn hình UI nơi người dùng cuối ( người nghĩ theo chỉ mục 1 ) đang chỉnh sửa kênh 13:

Kênh 13 hay 12?

SaveNút đó cuối cùng sẽ dẫn đến một số mã. Nếu ChannelSetlà 1 chỉ mục:

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

hoặc điều này nếu nó không được lập chỉ mục:

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

Tôi không thực sự chắc chắn làm thế nào để xử lý việc này. Tôi cảm thấy như đó là một cách thực hành tốt để giữ một danh sách các thứ (chỉ mục) được sắp xếp theo thứ tự số nguyên ChannelSetphù hợp với tất cả các giao diện mảng và danh sách khác trong vũ trụ C # (bằng cách lập chỉ mục bằng 0 ChannelSet). Nhưng sau đó, mọi đoạn mã giữa UI và phụ trợ sẽ cần một bản dịch (trừ đi 1) và tất cả chúng ta đều biết các lỗi off-by-one thông thường và nguy hiểm như thế nào.

Vì vậy, có một quyết định như thế này bao giờ cắn bạn? Tôi nên có chỉ số hoặc một chỉ số?


60
"Các chỉ số mảng có nên bắt đầu từ 0 hay 1 không? Tôi nghĩ rằng sự thỏa hiệp 0,5 của tôi đã bị từ chối mà không có sự cân nhắc đúng đắn." - Stan Kelly-Bootle
gnat

2
@gnat Thật là một điều tốt khi tôi ở một mình trong văn phòng - những tiếng cười nhìn chằm chằm vào màn hình máy tính thường không phải là chỉ số tốt về năng suất!
kdbanman

4
Các kênh phải được xác định bởi vị trí của chúng trong tập hợp? Bạn không thể xác định chúng bằng một cái gì khác thay vào đó (ví dụ: tần suất nếu đó là các kênh TV)?
Svick 10/07/2015

@svick, đó là một điểm tuyệt vời. Tôi có thể cho phép truy cập kênh bởi các số nhận dạng khác nhau sau đó, nhưng biệt ngữ chính của khách hàng thực sự có vẻ là một số kênh được lập chỉ mục.
kdbanman

Từ việc đọc nhanh, có vẻ như không cần sử dụng các chỉ số như một vấn đề hiệu quả, vì vậy bạn có thể sử dụng một số loại Bản đồ để liên kết trực tiếp ID và kênh của kênh mà không cần phải suy nghĩ về mảng. Tôi nghĩ rằng đây là những gì Rob đang làm và tôi cũng đồng ý với giải pháp của anh ấy nếu bạn chọn sử dụng các chỉ số.
Matthew Đọc

Câu trả lời:


24

Cảm giác như bạn đang kết hợp Định danh cho Channelvị trí của nó trong a ChannelSet. Sau đây là hình dung của tôi về cách mã / nhận xét của bạn sẽ nhìn vào lúc này:

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];
    }
}

Có vẻ như bạn đã quyết định rằng vì Channeltrong phạm vi a ChannelSetđược xác định bởi các số có giới hạn trên và dưới nên chúng phải là chỉ mục và do đó dựa trên C #, 0. Nếu cách tự nhiên để đề cập đến từng kênh là một số trong khoảng từ 1 đến X, hãy tham khảo chúng theo số từ 1 đến X. Đừng thử và buộc chúng trở thành chỉ mục.

Nếu bạn thực sự muốn cung cấp một cách để truy cập chúng bằng chỉ mục dựa trên 0 (điều này mang lại lợi ích gì cho người dùng cuối của bạn hoặc nhà phát triển sử dụng mã?) Thì hãy triển khai Trình lập chỉ mục :

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
Đây là một câu trả lời thực sự đầy đủ và súc tích. Đó chính xác là cách tiếp cận mà tôi đã rút ra từ các câu trả lời khác.
kdbanman

7
Người qua đường, tôi đã chấp nhận câu trả lời này, nhưng chủ đề này chứa đầy câu trả lời hay, vì vậy hãy đọc tiếp.
kdbanman

Rất hay, @Rob. "Tôi biết cách sử dụng Bộ lập chỉ mục." - Neo
Jason P Sallinger 13/07/2015

35

Hiển thị giao diện người dùng với chỉ số 1, sử dụng chỉ số 0 trong mã.

Điều đó nói rằng, tôi đã làm việc với các thiết bị âm thanh như thế này và sử dụng chỉ số 1 cho các kênh và thiết kế mã không bao giờ sử dụng "Chỉ mục" hoặc bộ chỉ mục để giúp tránh sự thất vọng. Một số lập trình viên vẫn phàn nàn, vì vậy chúng tôi đã thay đổi nó. Sau đó, các lập trình viên khác phàn nàn.

Chỉ cần chọn một và gắn bó với nó. Có những vấn đề lớn hơn để giải quyết trong kế hoạch lớn là đưa phần mềm ra khỏi cửa.


12
Ngoại lệ cho điều này: nếu bạn đang sử dụng ngôn ngữ dựa trên 1. Mã của bạn phải phù hợp với phần còn lại của mã và ngôn ngữ của bạn, vì vậy 0 dựa trên C #, 1 dựa trên VBA (!) Hoặc Lua. Giao diện người dùng của bạn phải là bất cứ điều gì con người mong đợi (hầu như luôn luôn dựa trên 1).
dùng253751

1
@immibis - Điểm tốt, tôi đã không nghĩ về điều đó.
Telastyn

3
Một điều tôi thỉnh thoảng bỏ lỡ về lập trình ngày xưa trong BASIC là "CƠ SỞ TÙY CHỌN" ...
Brian Knoblauch

1
@ BlueRaja-DannyPflughoeft: Trong khi tôi luôn nghĩ Option Baselà ngớ ngẩn, tôi đã thích tính năng một số ngôn ngữ được cung cấp cho phép mảng để chỉ định các giới hạn thấp hơn tùy ý. Ví dụ, nếu một người có một mảng dữ liệu theo năm, ngoài khả năng có một kích thước mảng [firstYear..lastYear]có thể đẹp hơn so với việc luôn phải truy cập phần tử [thisYear-firstYear].
supercat

1
@ BlueRaja-DannyPflughoeft Lỗi của một người là một tính năng của một người đàn ông khác ...
Brian Knoblauch

21

Sử dụng cả hai.

Không trộn UI với mã lõi của bạn. Bên trong (như một thư viện), bạn nên mã "mà không biết" làm thế nào mỗi phần tử trên mảng sẽ được gọi bởi người dùng cuối. Sử dụng các mảng và bộ sưu tập 0 "tự nhiên".

Phần của chương trình kết hợp dữ liệu với giao diện người dùng, chế độ xem, cần chú ý dịch chính xác dữ liệu giữa Mô hình tinh thần của người dùng và 'Thư viện' thực hiện công việc thực tế.

Tại sao điều này tốt hơn?

  • Mã có thể sạch hơn, không có hack để dịch chỉ mục. Điều này cũng giúp các lập trình viên bằng cách không mong đợi họ tuân theo và nhớ sử dụng các quy ước không tự nhiên.
  • Người dùng sẽ sử dụng lập chỉ mục mà họ mong đợi. Bạn không muốn làm phiền họ.

12

Bạn có thể thấy bộ sưu tập từ hai góc độ khác nhau.

(1) Ở nơi đầu tiên, một bộ sưu tập tuần tự thông thường , như một mảng hoặc một danh sách. Index từ 0rõ ràng là giải pháp đúng sau đó, theo quy ước. Bạn phân bổ đủ các mục và số bản đồ kênh cho các chỉ số, đó là tầm thường (chỉ trừ 1).

(2) Bộ sưu tập của bạn về cơ bản là ánh xạ giữa các định danh kênh và các đối tượng thông tin kênh. Nó chỉ xảy ra rằng các định danh kênh là một dãy số nguyên liên tiếp; ngày mai nó có thể là một cái gì đó giống như [1, 2, 3, 3a, 4, 4.1, 6, 8, 13]. Bạn sử dụng thứ tự này được đặt làm khóa ánh xạ.

Chọn một trong những cách tiếp cận, ghi lại nó và tuân theo nó. Từ quan điểm linh hoạt, tôi sẽ đi với (2), vì đại diện của số kênh (ít nhất là tên được hiển thị) tương đối có khả năng thay đổi trong tương lai.


Tôi thực sự đánh giá cao phần 2 của câu trả lời của bạn. Tôi nghĩ rằng tôi sẽ chấp nhận một cái gì đó giống như nó. Các accessor index ( channels[int]) sẽ là zero số nguyên được lập chỉ mục, và accessors Nhận GetChannelByFrequency, GetChannelByName, GetChannelByNumbersẽ được linh hoạt.
kdbanman

1
Cách tiếp cận thứ hai chắc chắn là tốt nhất. Các đài truyền hình địa phương của tôi cung cấp 32 kênh với LCN trong khoảng từ 1 đến 201. Điều này sẽ yêu cầu một mảng thưa thớt (trống 84%). Về mặt khái niệm, nó giống như lưu trữ một tập hợp những người trong một mảng được lập chỉ mục bằng số điện thoại.
Kelly Thomas

3
Hơn nữa, bất kỳ bộ sưu tập nào nhỏ đến mức được thể hiện bằng danh sách thả xuống UI rất khó có thể hưởng lợi từ các đặc tính hiệu suất cụ thể của một mảng như cấu trúc dữ liệu. Vì vậy, thứ được gọi là "kênh 1" của người dùng cũng có thể được gọi là "1" trong mã và sử dụng một Dictionaryhoặc SortedDictionary.
Steve Jessop

1
Nguyên tắc ít ngạc nhiên nhất sẽ ngụ ý rằng operator [] (int index)nên dựa trên 0, trong khi operator [] (IOrdered index)thì không. (Xin lỗi về cú pháp gần đúng, C # của tôi rất rỉ sét.)
9000

2
@kdbanman: cá nhân, tôi sẽ lập luận rằng ngay khi bạn quá tải []trong một ngôn ngữ sử dụng sự quá tải này để tra cứu bằng khóa tùy ý, thì các lập trình viên không được cho rằng các khóa bắt đầu 0và tiếp tục, và nên từ bỏ thói quen đó. Chúng có thể bắt đầu từ 0, hoặc 1, hoặc 1000, hoặc chuỗi "Channel1", đó là toàn bộ điểm sử dụng toán tử []với các khóa tùy ý. OTOH, nếu đây là C và ai đó đang nói "tôi có nên để phần tử 0trong một mảng không được sử dụng để bắt đầu từ 1" thì tôi sẽ không nói "rõ ràng là có", đó sẽ là một cuộc gọi thân thiết nhưng tôi nghiêng về "Không".
Steve Jessop

3

Mọi người và con chó của họ sử dụng chỉ số dựa trên không. Sử dụng các chỉ mục một trong ứng dụng của bạn sẽ khiến bạn gặp sự cố bảo trì mãi mãi.

Bây giờ những gì bạn hiển thị trong giao diện người dùng, điều đó hoàn toàn phụ thuộc vào bạn và khách hàng của bạn. Chỉ hiển thị i + 1 dưới dạng số kênh, cùng với kênh #i, nếu điều đó làm cho khách hàng của bạn hài lòng.

Nếu bạn trưng ra một lớp, thì bạn sẽ đưa nó cho các lập trình viên. Những người mà bạn bối rối bởi một chỉ số dựa trên zero không phải là lập trình viên, vì vậy có một sự ngắt kết nối ở đây.

Bạn dường như lo lắng về việc chuyển đổi giữa UI và mã. Nếu điều đó làm bạn lo lắng, hãy tạo một lớp mang biểu tượng giao diện người dùng của số kênh. Một cuộc gọi biến số 12 thành đại diện UI "Kênh 13" và một cuộc gọi biến "Kênh 13" thành số 12. Dù sao đi nữa, trong trường hợp khách hàng thay đổi ý định, vì vậy chỉ có hai dòng mã thay đổi. Và nó hoạt động nếu khách hàng yêu cầu chữ số La Mã, hoặc chữ A đến Z.


1
Tôi không thể xác minh câu trả lời của bạn vì con chó của tôi sẽ không cho tôi biết loại chỉ số mà nó sử dụng.
armani

3

Bạn đang trộn lẫn hai khái niệm: lập chỉ mục và nhận dạng. Chúng không giống nhau và không nên nhầm lẫn.

Mục đích của việc lập chỉ mục là gì? Truy cập ngẫu nhiên nhanh chóng. Nếu hiệu suất không phải là vấn đề và với mô tả của bạn thì không, thì việc sử dụng bộ sưu tập có chỉ mục là không cần thiết và có thể là tình cờ.

Nếu bạn (hoặc nhóm của bạn) đang bị nhầm lẫn bởi chỉ số so với danh tính, thì bạn có thể giải quyết vấn đề đó bằng cách không có chỉ mục. Sử dụng từ điển hoặc liệt kê và nhận kênh theo giá trị.

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

Cái thứ nhất thì rõ ràng hơn, cái thứ hai ngắn hơn - chúng có thể hoặc không được dịch sang cùng một IL, nhưng dù bằng cách nào, nếu bạn đang sử dụng cái này cho một lần thả xuống, nó sẽ đủ nhanh.


2

Bạn đang hiển thị một giao diện thông qua ChannelSetlớp mà bạn mong muốn người dùng của mình tương tác. Tôi sẽ làm cho nó tự nhiên để họ sử dụng càng tốt. Nếu bạn mong đợi người dùng của bạn sẽ bắt đầu đếm từ 1 thay vì 0, thì hãy hiển thị lớp này và cách sử dụng nó với mong muốn đó.


1
Vấn đề nằm ở đó! Người dùng cuối của tôi dự kiến ​​sẽ bắt đầu từ 1 và người dùng dev của tôi (bao gồm cả tôi) sẽ bắt đầu từ 0.
kdbanman

1
Và vì vậy tôi khuyên bạn nên điều chỉnh lớp học của mình để đáp ứng nhu cầu của người dùng cuối.
Bernard

2

Lập chỉ mục dựa trên 0 là phổ biến và được bảo vệ bởi những người quan trọng, bởi vì tuyên bố cho thấy kích thước của đối tượng.

Với các máy tính hiện đại, với các máy tính mà người dùng của bạn sẽ sử dụng, kích thước của đối tượng có quan trọng không?

Nếu ngôn ngữ của bạn (C #) không hỗ trợ cách khai báo phần tử đầu tiên và cuối cùng của mảng, bạn có thể chỉ cần khai báo một mảng lớn hơn và sử dụng các hằng số được khai báo (hoặc biến động) để xác định bắt đầu và kết thúc hợp lý của vùng hoạt động của mảng.

Đối với các đối tượng UI, bạn gần như chắc chắn có thể đủ khả năng sử dụng một đối tượng từ điển cho ánh xạ của mình, (nếu bạn có 10 triệu đối tượng, bạn có vấn đề về UI) và nếu đối tượng từ điển tốt nhất hóa ra là một danh sách với lãng phí không gian ở hai đầu, bạn không mất gì quan trọng.


Trên thực tế, các chỉ mục dựa trên không và dựa trên một phải liên quan đến cách thức mảng được xây dựng trong bộ nhớ. Các chỉ mục dựa trên zero sử dụng bộ nhớ ngoài (bên ngoài mảng) để xác định kích thước, trong khi các chỉ mục dựa trên một sử dụng bộ nhớ trong (chỉ mục 0) để ghi nhớ kích thước của mảng ( thông thường, dù sao ). Nó không phổ biến cho "kích thước của đối tượng" nhưng thường liên quan đến cách bộ nhớ được phân bổ bên trong cho các mảng. Bất kể, tôi sẽ không đề xuất các byte bị rò rỉ một cách liều lĩnh ở đây và ở đó "chỉ vì." Nhưng từ điển thường là một ý tưởng tốt hơn dù sao.
phyrfox

@phyrfox: Sự trừu tượng mà tôi thích cho việc lập chỉ mục dựa trên zero là nói rằng các chỉ mục xác định vị trí giữa các đối tượng; đối tượng thứ nhất nằm giữa các chỉ số 0 và 1, đối tượng thứ hai giữa 1 và 2, v.v. Với x <= y <= z, các phạm vi [x, y] và [y, z] sẽ liên tiếp nhưng không trùng nhau và hoặc cả hai có thể trống không có trường hợp góc đặc biệt cần thiết. Khi áp dụng mô hình "chỉ số ngồi giữa các mục" với lập chỉ mục dựa trên số không, một danh sách gồm sáu mục trải dài trong phạm vi từ 0 đến 6; với lập chỉ mục một dựa trên nó sẽ nằm trong khoảng từ 1 đến 7. Lưu ý rằng sự trừu tượng hóa này rất phù hợp với các con trỏ "vượt ra ngoài".
supercat
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.