Dlaczego tablice zerowe są normą?


112

Pytanie zadane tu przypomniał mi dyskusji miałem u kolegi programisty. Argumentował, że tablice oparte na zerach powinny być zastąpione tablicami opartymi na zerach, ponieważ tablice oparte na zerach są szczegółami implementacji, które pochodzą ze sposobu działania tablic i wskaźników oraz sprzętu komputerowego, ale tego rodzaju rzeczy nie powinny być odzwierciedlone na wyższym poziomie Języki.

Teraz nie jestem zbyt dobry w debatowaniu, więc nie mogłem naprawdę zaoferować żadnych dobrych powodów, by trzymać się tablic zerowych, które wydają się bardziej odpowiednie. Dlaczego zero jest powszechnym punktem wyjścia dla tablic?


W tablicy n-elementowej element „n” nie jest obecny. Tablica n-elementowa ma elementy o liczbie od 0 do n-1. Czy nie jest lepiej, jeśli mamy tablicę zaczynającą się od 1, a więc tablica n-elementowa faktycznie reprezentuje n elementów obecnych w tablicy.

Lubię tablice zerowe, ponieważ pierwszy bit w bajcie to 2 ^ 0, a nie 2 ^ 1. Czasami mi to pomaga :)
e-MEE

Jeśli spojrzymy na tę listę, na przykład en.wikipedia.org/wiki/… , zauważymy, że większość języków specyficznych dla domeny zaczyna indeksowanie od 1, a większość języków ze szkoły myślenia cs od 0. Jeśli szuka się nieco dłużej , zauważy, że przeprowadzono tak wiele głupich dyskusji na ten temat, że prawdopodobnie nie ma sensu rozmawiać z żadnym z nich, aby zmienić ich sposób postępowania. W obronie „1” większość ludzi na świecie używa tego. Większość programistów używa „0”. Większość ludzi nie rozumie programistów, więc to sprawia, że ​​myślisz ...
Rook

Nie mogę uwierzyć, że to pytanie zostało przeniesione z StackOverflow do programistów, a następnie zamknięte jako niekonstruktywne. To jest głupie.
paercebal

Odpowiedzi:


105

Nie sądzę, aby ktokolwiek z nas mógł przedstawić mocniejszy argument niż artykuł Edsgera W. Dijkstry „Dlaczego numeracja powinna zaczynać się od zera” .


Przywołał kilka statystyk i użył dowodu matematycznego. Ale jestem pewien, że ktoś nadal mógłby się kłócić. Chociaż zatwierdzam, więc nie chciałbym.

6
Artykuł Dijkstry dotyczy stylu, ale jego argumenty dotyczą prostoty i łatwości użycia ... +1.
paercebal,

80

Argument autorytetu

Cóż ... Najwyraźniej większość języków, w tym bardzo nowe, są zerowane. Ponieważ te języki zostały napisane przez dość wykwalifikowanych ludzi, twój przyjaciel musi się mylić ...

Dlaczego jeden

dlaczego 1 byłby lepszym początkowym indeksem niż zero? Dlaczego nie 2 lub 10? Sama odpowiedź jest interesująca, ponieważ pokazuje wiele na temat procesu obrony ludzi.

Pierwszym argumentem jest to, że jest to bardziej naturalne, ponieważ 1-ty jest zwykle jeden przed wszystkimi innymi, przynajmniej dla większości ludzi ...

Argument numer jeden polega na tym, że ostatni indeks jest również rozmiarem tablicy ...

Wciąż jestem pod wrażeniem „jakości” powodów, dla których zwykle słyszę tego rodzaju argumenty ... A nawet więcej, gdy przypominam sobie, że…

Dlaczego nie zero?

... Notacje „oparte na jednym” są pozostałościami kultury zachodniej, która ignorowała istnienie zera przez stulecia, jeśli nie więcej.

Wierzcie lub nie, oryginalny kalendarz gregoriański pochodzi z -3, -2, -1, 1, 2, 3 ... Spróbuj wyobrazić sobie problem, jaki przyczynił się do zachodniej nauki (na przykład, ile lat od 1 stycznia -2 do 1 stycznia, aby zobaczyć, niż oryginalny kalendarz gregoriański koliduje z czymś tak prostym jak odejmowanie ...).

Trzymanie się tablic opartych na jednym jest jak (no cóż, będę zmodowany za to ... ^ _ ^ ...), trzymanie się mil i jardów w 21 wieku ...

Dlaczego zero? Ponieważ to matematyka!

Pierwszy (Ups ... Przepraszam ... Spróbuję ponownie)

Zero , zero jest niczym, jedno jest czymś. Niektóre teksty religijne utrzymują, że „na początku nic nie było”. Niektóre dyskusje związane z komputerem mogą być równie palące jak debaty religijne, więc ten punkt nie jest tak poza tematami, jak się wydaje ... ^ _ ^

Po pierwsze , łatwiej jest pracować z tablicą zerową i zignorować jej zerową wartość niż pracować z tablicą zerową i zhakować, aby znaleźć jej zerową wartość. Ten powód był prawie tak głupi jak poprzedni, ale oryginalny argument na korzyść tablic opartych na jednostkach również był dość błędny.

Po drugie , pamiętajmy, że mając do czynienia z liczbami, są duże szanse, że poradzisz sobie z matematyką w jednej czy innej chwili, a kiedy masz do czynienia z matematyką, są duże szanse, że nie masz ochoty na głupie włamania, by obejść przestarzałe konwencje. Notacja oparta na One nękała matematykę i daty również od stuleci, a ucząc się na własnych błędach, powinniśmy starać się tego unikać w naukach zorientowanych na przyszłość (w tym na języki komputerowe).

Po trzecie , jeśli chodzi o tablice języka komputerowego związane ze sprzętem, przydziel tablicę C zawierającą 21 liczb całkowitych i przesuń wskaźnik 10 wskaźników w prawo, a otrzymasz naturalną tablicę [-10 do 10]. Nie jest to naturalne w przypadku sprzętu. Ale to jest matematyka. Oczywiście matematyka może być przestarzała, ale kiedy ostatni raz sprawdzałem, większość ludzi na świecie uważała, że ​​tak nie jest.

Cztery , jak już wskazano w innym miejscu, nawet dla dyskretnej pozycji (lub odległości zmniejszonych do dyskretnych wartości), pierwszy indeks będzie wynosił zero, podobnie jak podłoga w budynku (zaczynając od zera), malejące odliczanie (3, 2, 1, ZERO !), wysokość gruntu, pierwszy piksel obrazu, temperatura (zero Kelvina, zero absolutne lub zero stopni Celsjusza, jako temperatura zamarzania wody 273 K). W rzeczywistości jedyne, co tak naprawdę zaczyna się od jednego, to tradycyjny sposób „ pierwszy , drugi , trzeci itd.” notacja iteracyjna , która naturalnie prowadzi mnie do następnego punktu ...

Pięć następny punkt (który naturalnie podąża za poprzedni ) jest to, że pojemniki wysokiego szczebla powinny być dostępne, a nie przez indeks, ale przez iteratory , chyba indeksów sami mają istotną wartość. Dziwię się, że twój rzecznik „języka wyższego poziomu” o tym nie wspomniał. W przypadku gdy sam indeks jest ważny, możesz postawić zakład w połowie czasu, gdy masz na myśli pytanie matematyczne. Dlatego chciałbyś, aby twój kontener był przyjazny dla matematyki, a nie wyłączony z matematyki, jak „twój stary kalendarz gregoriański” od 1, i potrzebujesz regurgitowanych hacków, aby działał.

Wniosek

Argument podany przez twojego programistę jest błędem, ponieważ niepotrzebnie wiąże nawyki mówione / pisane, które z natury są rozmyte, z językami komputerowymi (gdzie nie chcesz, aby twoja instrukcja była niewyraźna), a także dlatego, że błędnie przypisujesz sprzęt Przyczyną tego problemu jest przekonanie cię, gdy języki będą coraz bardziej abstrakcyjne, że tablica zerowa należy już do przeszłości.

Macierze zerowe są zerowane ze względów matematycznych. Nie z powodów związanych ze sprzętem.

Teraz, jeśli jest to problem dla twojego programisty, poproś go, aby zaczął programować przy użyciu prawdziwych konstrukcji wysokiego poziomu, takich jak iteratory i pętle foreach.


zero stopni celsjusza wynosi 273,15K;)

2
Wiem (mam dyplom magistra fizyki), ale czułem, że zabawa z dziesiętnymi była mniej
istotna

20
Twoje akapity są oznaczone jako „zero, pierwsze, drugie, trzecie, cztery, pięć”. Aby zachować spójność, należy użyć liczb głównych („zero, jeden, dwa, trzy, cztery, pięć”) lub liczb porządkowych („zero, pierwszy, drugi, trzeci, czwarty, piąty”). :-)
ShreevatsaR

10
Podobnie, przez pierwszy rok naszego życia nie mamy jednego roku, ale zero lat

3
@Nikita Rybak: Niesamowite jest to, że przegapiłeś to, co widzieli przed tobą wszyscy komentatorzy: Oczywiście odpowiedź Billa Jaszczurki jest właściwa. Właśnie dlatego głosowałem na niego +1 i dlatego wybrano go jako najlepszą odpowiedź na pytanie. Moja odpowiedź dotyczy raczej wyśmiewania się z błędnych powodów, jakie stoją za tablicami opartymi na 1 i oferowania konkretnych przypadków, w których tablica oparta na 1 byłaby uciążliwa. Mimo to jestem zaskoczony, że znalazłeś „ani jednego przekonującego”, nawet biorąc pod uwagę, że przyczyny są zmieszane z ironią ...
paercebal,

47

Półotwarte interwały komponują się dobrze. Jeśli masz do czynienia 0 <= i < limi chcesz rozszerzyć o nelementy, nowe elementy mają indeksy w zakresie lim <= i < lim + n. Praca z tablicami zerowymi ułatwia arytmetykę podczas dzielenia lub konkatenacji tablic lub zliczania elementów . Jeden ma nadzieję, że prostsze arytmetyka prowadzi do mniejszej liczby błędów Fencepost .


+1 za półotwarte interwały - to po prostu ułatwia wszystko.
Eclipse

29

Pewne typy manipulacji tablicami stają się szalone w przypadku tablic opartych na 1, ale pozostają prostsze w przypadku tablic opartych na 0.

W pewnym momencie wykonałem programowanie analizy numerycznej. Pracowałem z algorytmami do manipulacji skompresowanymi, rzadkimi macierzami, napisanymi zarówno w FORTRAN, jak i C ++.

Algorytmy FORTRAN miały wiele a[i + j + k - 2], podczas gdy C ++ a[i + j + k], ponieważ tablica FORTRAN była oparta na 1, podczas gdy tablica C ++ była oparta na 0.


Zgadzam się. Jedynym momentem, w którym uznaję tablicę opartą na 1, jest chwila, gdy chcę zrobić miejsce na indeks zerowy. Na przykład, jeśli mam tablicę obiektów i używam ich indeksów jako uchwytów i chcę mieć uchwyt zerowy.
Fabio Ceconello,

Uderzyłem też w niepotrzebną komplikację 1 tablic opartych, 0 tablic opartych, według mojego ograniczonego doświadczenia, zawsze tworzyło wyraźniejszy kod do indeksowania tablic z rzadkim wyjątkiem.

Czym różniłyby się indeksy FORTRAN i C ++ o 2, gdyby ich odpowiednie indeksy były przesunięte tylko o 1? Ponadto, dlaczego minus 2? Jeśli FORTRAN jest oparty na 1, to czy nie dodałbyś 2 (lub 1)?
RexE

@ RexE: Tak to działa i dlatego jest tak skomplikowane w przypadku tablic 1.
Jay Bazuzi,

@ RexE: Załóżmy, że emulujesz tablicę 3d płaską. Następnie w bazie 0 element (0 0 0) odpowiada elementowi 0 w płaskiej tablicy. OTOH, jeśli jest oparty na 1, element (1 1 1) odpowiada elementowi 1 w płaskiej tablicy: 1 + 1 + 1-2.

22

Indeks w tablicy nie jest tak naprawdę indeksem. Jest to po prostu przesunięcie, które jest odległością od początku tablicy. Pierwszy element znajduje się na początku tablicy, więc nie ma odległości. Dlatego przesunięcie wynosi 0.


3
W przypadku większości projektowanych obecnie języków jest to tak naprawdę szczegół implementacji, który nie powinien pojawiać się w tym języku (chyba że istnieją inne lepsze powody, aby to zrobić)
Jens Schauder

17

Powody są nie tylko historyczne: C i C ++ są nadal dostępne i szeroko stosowane, a arytmetyka wskaźników jest bardzo ważnym powodem, dla którego tablice zaczynają się od indeksu 0.

W przypadku innych języków, w których nie ma arytmetyki wskaźnika, to, czy pierwszy element ma indeks 0 czy 1, jest raczej konwencją niż czymkolwiek innym.
Problem polega na tym, że języki, które używają indeksu 1 jako pierwszego elementu, nie istnieją w próżni i zwykle muszą wchodzić w interakcje z bibliotekami, które często są pisane w - zgadłeś - C lub C ++ ...

VB i jego pochodne smaki ucierpiały z powodu tego, że tablice zaczynają się od 0 lub 1 i przez długi czas było źródłem problemów.

Konkluzja jest taka: nie ma znaczenia, jaki język uważa indeks pierwszego elementu, o ile jest spójny przez cały czas. Problem polega na tym, że uznanie 1 za pierwszy indeks utrudnia pracę w praktyce.


Zgoda. Spójność ma znaczenie, a jeśli nie masz luksusu unikania kodu niskiego poziomu (w tym C / C ++) całkowicie, to praca z tablicami opartymi na 1 wymaga jedynie problemów.
Shog9

Skoro już o tym mowa, pytanie: czy kiedykolwiek używasz kodu niskiego poziomu w sposób inny niż platforma? Innymi słowy, zawsze jesteś na tej czy innej platformie i musisz wiedzieć, która?
Dan Rosenstark,

1
Ponieważ ktoś, kto myśli, że VB .NET jest zwykle niesprawiedliwie oczerniany, muszę powiedzieć, że praktyka VB .NET na tablicach jest okropna. Podzielili różnicę i jeszcze bardziej ją pomylili: tablice zaczynają się od 0, ale Dim a as Integer (5) tworzy tablicę z 6 pozycjami. Wydawało się, że uzasadnienie było takie, że posiadanie dodatkowej pozycji było lepsze niż błędy w rozwiązywaniu problemów poza długością tablicy. Niestety w tej sprawie (i innych kwestiach, takich jak And and Or bitwise) spoczęły na wymaganiach wielu programistów VB6, którzy i tak nie używali VB .NET.
Kyralessa,

1
@ Kyralessa: Nie, uzasadnieniem była kompatybilność wsteczna z VB6 (asystent automatycznego uaktualnienia…), mimo że byli świadomi, że notacja jest sprzeczna z intuicją i podatna na błędy. Z drugiej strony, Andi Orbędąc bitowe ma nic wspólnego z VB6, to jedynym logicznym rozwiązaniem dla języka VB-type. Ty nie masz AndAlsoi OrElsedla operacji logicznych.
Konrad Rudolph,

Anda Orbitowe ma wszystko wspólnego z VB6, ponieważ były bitowe w VB6. Brzydkie operatory AndAlsoi OrElsepowinny być wykonane bitowo, ponieważ operacje bitowe są znacznie mniej powszechne niż operacje logiczne. W języku pozostało wiele takich brzydkich brodawek z powodu „wstecznej kompatybilności”, na przykład fakt, że ByVal jest otynkowany wszędzie, mimo że jest domyślny.
Kyralessa

10

Macierze zerowe mają swoje korzenie w C, a nawet asemblerze. W przypadku C matematyka wskaźnika działa w następujący sposób:

  • Każdy element tablicy zajmuje określoną liczbę bajtów. 32-bitowa liczba całkowita to (oczywiście) 4 bajty;
  • Adres tablicy jest zajmowany przez pierwszy element tablicy, a następnie kolejne elementy w ciągłych blokach o równej wielkości.

Aby to zilustrować, załóżmy, że int a[4]jest to 0xFF00, adresy to:

  • [0] -> 0xFF00;
  • [1] -> 0xFF04;
  • [2] -> 0xFF08;
  • a [3] -> 0xFF0C.

Zatem przy zerowych indeksach matematyka adresu jest prosta:

Adres elementu = adres tablicy + indeks * sizeof (typ)

W rzeczywistości wszystkie wyrażenia w C są równoważne:

  • a [2];
  • 2 [a]; i
  • * (a + 2).

W przypadku tablic opartych na jednej matematyce matematyka jest (zawsze) nieco bardziej skomplikowana.

Powody są więc w dużej mierze historyczne.


1
W pierwotnym pytaniu stwierdzono już, że „tablice oparte na zerach są szczegółami implementacji, które pochodzą ze sposobu działania tablic i wskaźników oraz sprzętu komputerowego, ale tego rodzaju rzeczy nie powinny być odzwierciedlone w językach wyższego poziomu”.

Warto wspomnieć, że języki, które zezwalają na tablice oparte na N, zwykle generują kod z „przesunięciami” tablicy automatycznie obliczanymi przy zerowym koszcie działania.
Roddy,

8

Jeśli używasz tablic zerowych, długość tablicy jest zbiorem prawidłowych indeksów. Przynajmniej tak mówi arytmetyka Peano:

0 = {}
1 = 0 U {0} = {0}
2 = 1 U {1} = {0,1}
3 = 2 U {2} = {0,1,2}
...
n = n-1 U {n-1} = {0,1,2...n-1}

Jest to więc w pewnym sensie najbardziej naturalny zapis.


7

Ponieważ istnieje silna korelacja między tablicami i wskaźnikami w C

char* p = "hello";
char q[] = "hello";

assert(p[1] == q[1]);

assert(*p == *q)

* p jest takie samo jak * (p + 0)

posiadanie początkowego indeksu 1 spowoduje później ból głowy


5

Kupa jest jednym z przykładów zalet tablic 1. Biorąc pod uwagę indeks i , indeks i „s rodzic i dziecko są w lewo

PARENT[ i ] = i ÷ 2

LCHILD[ i ] = i × 2

Ale tylko dla tablic opartych na 1. Dla macierzy opartych na 0 masz

PARENT[ i ] = ( i + 1) ÷ 2 - 1

LCHILD[ i ] = ( i + 1) × 2–1

A potem masz właściwość, że i jest również rozmiarem pod-tablicy tego indeksu (tj. Indeksów w zakresie [1, i ]).

Ale w końcu to nie ma znaczenia, ponieważ możesz przekształcić tablicę opartą na 0 w tablicę opartą na 1, przydzielając jeden element więcej niż zwykle i ignorując zero. Dlatego możesz wyrazić zgodę, aby uzyskać korzyści z tablic opartych na 1, gdy jest to właściwe, i zachować tablice oparte na 0 dla czystszej arytmetyki w prawie wszystkich innych sytuacjach.


4

Mam wrażenie, że jest to całkowicie arbitralne. Nie ma nic specjalnego w tablicach zerowych lub opartych na jednym. Od czasu wyzwolenia się z Visual Basic (głównie czasami robię małe rzeczy w Excelu) nie pracowałem z tablicami opartymi na 1 i ... to samo. Faktem jest, że jeśli potrzebujesz trzeciego elementu tablicy, jest to tylko szczegół implementacji, który nazywa się 3 lub 2. Jednak 99% pracy, którą wykonujesz z tablicami, interesują tylko dwa absolutne punkty: pierwszy element i liczba lub długość. Ponownie, to tylko szczegół implementacji, że pierwszy element nazywa się zero zamiast jednego lub że ostatni element nazywa się count-1 lub zamiast tego count.

Edycja: Niektórzy z ankieterów wspominali, że tablice oparte na 1 są bardziej podatne na błędy słupków ogrodzeniowych. Z mojego doświadczenia, myśląc o tym teraz, jest to prawda. Pamiętam, jak myślałem w VB: „albo to zadziała, albo wybuchnie, bo mnie nie ma”. W Javie to się nigdy nie zdarza. Chociaż myślałem, że jestem coraz lepszy, niektórzy z ankieterów wskazują przypadki, w których tablice oparte na 0 dają lepszą arytmetykę, NAWET, gdy nie musisz radzić sobie z językiem niższego poziomu.


W PHP większość funkcji wyszukiwania wewnątrz ciągu zwraca FALSE, jeśli nie została znaleziona. Nie -1.
jmucchiello,

Mylisz liczbę z indeksem ostatniego elementu. Liczba pustych tablic wynosi zawsze 0, bez względu na to, czy używasz tablic zerowych, czy jednopartych. Zaletą tablic opartych na jednym jest to, że liczba jest pozycją ostatniego elementu (ale to jedyna zaleta).

Prawda w odniesieniu do obu tych punktów: ostatnia połowa została usunięta, ponieważ jest taka sama dla tablic zera lub zera: liczba wynosi 0, jeśli masz zero elementów.
Dan Rosenstark,

Miałem na myśli ostatnią połowę mojej odpowiedzi ...
Dan Rosenstark,

4

Jako 10-letni programista C / C ++, z bardzo silnym doświadczeniem w Pascalu i Delphi, wciąż brakuje mi silnego sprawdzania powiązań tablicowych i typów indeksów oraz elastyczności i bezpieczeństwa z tym związanych. Oczywistym przykładem tego są wartości danych macierzy dla każdego miesiąca.

Pascal:

 Type Month = (Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec);

  Var Days[Month] of integer;

  ... 
  if Year mod 4 = 0 then // yes this is vastly simplified for leap years and yes i don't know what the comment marker is in pascal and no i won't go look it up
    Days[Feb] := 29
  else
    Days[Feb] := 28;

Pisanie podobnego kodu w językach C bez użycia +/- 1 lub „magicznych liczb” jest dość trudne. Zwróć uwagę, że wyrażenia takie jak Days [2] i Days [Jan + Dec] po prostu nie będą się kompilować, co może wydawać się brutalne dla osób, które nadal myślą w C lub asemblerze.

Muszę powiedzieć, że istnieje wiele aspektów języków Pascal / Delphi, których nie umknęło mi trochę, ale tablice oparte na zerach C wydają się po prostu „głupie” dla porównania.


Warto zauważyć, że Twój algorytm nie jest poprawny dla roku 2100. en.wikipedia.org/wiki/Leap_year#Algorytm

2
Wiem ;-) Jednak to było poprawne dla roku 2000. Po prostu gram „spot pedant” ...
Roddy

Znajdź pedanta! LOL.
jcollum,

Tak. Unikaj całego problemu, oprzyj tablicę tak, jak chcesz.
Loren Pechtel

Nie zdziwiłbym się, gdyby Twój przeciętny kompilator Pascal przypisał Jan = 0, Dec = 11 podczas generowania kodu maszynowego :-)

4

Powodem, dla którego zaczyna się od 0, a nie 1, jest to, że możesz myśleć o przesunięciu, jak daleko od początku pamięci tablicy jest ten element. To nie znaczy daj mi 0 element - to znaczy daj mi element, który od początku ma 0 elementów.

Innym sposobem na to jest to, że są one (w przeważającej części) równoważne:

array[n]

*(array + n)

Powodem, dla którego nigdy nie zostanie zmieniony standard, jest fakt, że C istnieje już od około 40 lat. Nie ma ważnego powodu, aby to zmienić, a jeśli tak, cały istniejący kod, który zależy od początku tablicy o wartości 0, zostałby uszkodzony.


W rzeczywistości możesz przepisać array[n]jak n[array]w C. Nie jest to dobry pomysł, jest to mylące! Ale jest to legalne (przynajmniej do C89) ze względu na powyższą tożsamość i fakt, że dodawanie jest przemienne.
Donal Fellows,

To szalony sposób na napisanie tego - coś, co zobaczyłeś w jakimkolwiek kodzie, który musiałeś zachować, byłoby wielkim znakiem ostrzegawczym. Na szczęście jeszcze tego nie spotkałem ...
Dennis Munsie

4

Kod zawierający niektóre oryginalne informacje o pozycji / pozycji względnej są znacznie czytelniejsze, a tablice zaczynają się od 0.

Na przykład: Kod do kopiowania wektora w określonej pozycji w większym wektorze jest problemem z tablicami zaczynającymi się od 1:

function copyAtPos (dest, vect, i):
    for i from 1 -> vect.length do
        dest[pos+i-1] = vect[i]

W przeciwieństwie do tablic rozpoczynających się od 0:

function copyAtPos (dest, vect, i):
    for i from 0 -> vect.length-1 do
        dest[pos+i] = vect[i]

Jeśli zaczniesz pisać formułę dużych zwojów, stanie się to koniecznością.


3

Dlaczego nie 2, 3 lub 20? To nie jest tak, że posiadanie tablic opartych na 1 jest łatwiejsze lub prostsze do zrozumienia niż tablice oparte na zerach. Aby przełączyć się na tablice 1, każdy programista musiałby nauczyć się, jak pracować z tablicami.

Co więcej, gdy masz do czynienia z przesunięciami do istniejących tablic, ma to również większy sens. Jeśli odczytujesz 115 bajtów z tablicy, wiesz, że następny fragment zaczyna się od 115. I tak dalej, następny bajt ma zawsze rozmiar odczytanych bajtów. W oparciu o 1 musisz cały czas dodawać jeden.

A czasem trzeba poradzić sobie z fragmentami danych w tablicach, nawet w języku bez „prawdziwej” arytmetyki wskaźników. W java możesz mieć dane w plikach mapowanych w pamięci lub buforach. W takim przypadku wiesz, że blok i ma rozmiar * i. Przy indeksie opartym na 1 byłoby to w bloku * i + 1.

W przypadku indeksowania opartego na 1 wiele technik wymagałoby +1 w dowolnym miejscu.


Dlaczego nie 2, 3 lub 20? Ponieważ 0 to tożsamość addytywna, a 1 to tożsamość multiplikatywna. Są najbardziej sensowne.

3

Za pomocą tablic opartych na 1 przekształć tablicę jednowymiarową w tablicę wielowymiarową:

int w = 5, h = 5, d = 5;

int[] a1 = new int[w * h * d], new a2 = int[w,h,d];

for (int z = 1; z <= d; z++)

  for (int y = 1; y <= h; y++)

    for (int x = 1; x <= w; x++)

      a1[x + (y - 1) * w + (z - 1) * h] = a2[x,y,z];

Zauważ, że twoje indeksy y i z są oparte na 0 (y - 1, z - 1), nawet jeśli tablica jest oparta na 1. W niektórych okolicznościach nie można uniknąć indeksów opartych na 0. Aby zachować spójność, dlaczego nie zawsze używać indeksów opartych na 0?


3

Dlaczego chcesz, aby tablice zaczynały się od jednego?

Kiedy mówisz a[x][y], kompilator przekłada się na: a+(x*num_cols+y). Gdyby tablice zaczęły się od jednego, tak by się stało a+(x*num_cols+y-1). Byłaby to dodatkowa operacja arytmetyczna za każdym razem , gdy chcesz uzyskać dostęp do elementu tablicy. Dlaczego chcesz spowolnić programy?


1
właściwie musiałoby to być + ((x - 1) * num_cols) + y - 1) - zarówno x i y zaczynałyby się od 1.
Dennis Munsie

2

Wystąpię tutaj na kończynę i zasugeruję coś innego niż tablicę liczb całkowitych z kluczem.

Myślę, że twój współpracownik zaczyna tworzyć mapowanie „zestawu” jeden na jeden w świecie fizycznym, w którym zawsze zaczynamy liczyć od 1. Rozumiem to, kiedy nie robisz nic szczególnego, łatwo jest zrozumieć jakiś kod gdy jesteś zmapowany od 1 do 1 między oprogramowaniem a światem fizycznym.

Moja sugestia

Nie używaj tablic całkowitych do tego, co przechowujesz, ale używaj innego rodzaju słownika lub pary wartości kluczowych. Te mapy lepiej pasują do prawdziwego życia, ponieważ nie jesteś związany żadną liczbą całkowitą. Ma to swoje miejsce i zaleciłbym korzystanie z niego jak najwięcej ze względu na zalety koncepcji mapowania 1 do 1 między oprogramowaniem a światem fizycznym.

tj. kvp['Name Server'] = "ns1.example.com"; (To tylko jeden z miliona możliwych przykładów).

Zastrzeżenie

To zdecydowanie nie działa, gdy pracujesz z pojęciami opartymi na matematyce, głównie dlatego, że matematyka jest bliższa rzeczywistej implementacji komputera. Używanie zestawów Kvp nic tu nie pomoże, ale w rzeczywistości popsunie wszystko i sprawi, że będzie to bardziej problematyczne. Nie zastanawiałem się nad wszystkimi przypadkami, w których coś może działać lepiej jako kvp lub jako tablica.

Ostatecznym pomysłem jest użycie tablic zerowych lub par klucz-wartość tam, gdzie ma to sens, pamiętaj, że gdy masz tylko młotek, każdy problem zaczyna wyglądać jak gwóźdź ...


2

Osobiście jedynym argumentem jest widzenie indeksów tablic jako przesunięć. To po prostu ma sens.

Można powiedzieć, że jest to pierwszy element, ale przesunięcie pierwszego elementu względem początku tablicy wynosi zero. Jako takie, wzięcie początku tablicy i dodanie zera da pierwszy element.

Tak więc w obliczeniach łatwiej jest dodać zero, aby znaleźć pierwszy element, niż dodać jeden, a następnie usunąć jeden.

Myślę, że każdy, kto zrobił jakieś rzeczy na niższym poziomie, zawsze uważa, że ​​podstawową zasadą jest zero. A ludzie, którzy zaczynają lub są przyzwyczajeni do wyższego poziomu, często nie algorytmicznego programowania, mogą chcieć systemu bazowego. A może po prostu uprzedzają nas wcześniejsze doświadczenia.


Dokładnie - to w zasadzie konwencja oparta na językach niższego poziomu.

2

Jedyne dwa (bardzo) poważne powody, aby używać indeksów opartych na 0 zamiast indeksów opartych na 1, wydają się unikać ponownej edukacji wielu programistów ORAZ ze względu na zgodność wsteczną .

We wszystkich otrzymanych odpowiedziach nie widziałem żadnych innych poważnych argumentów przeciwko indeksom opartym na 1.

W rzeczywistości indeksy są naturalnie oparte na 1 i oto dlaczego.

Najpierw musimy zapytać: czy pochodziły tablice? Czy mają rzeczywiste odpowiedniki? Odpowiedź brzmi: tak modelujemy wektory i macierze w informatyce. Jednak wektory i macierz są pojęciami matematycznymi, które przed erą komputerową korzystały z indeksów opartych na 1 (i które nadal w większości wykorzystują obecnie indeksy oparte na 1).

W prawdziwym świecie indeksy są 1-bazowe.

Jak powiedział wcześniej Thomas, języki, które używały wskaźników 0-bazowych, w rzeczywistości używają przesunięć , a nie indeksów. A programiści używający tych języków myślą o przesunięciach , a nie indeksach. Nie byłoby problemu, gdyby rzeczy zostały jasno określone, ale tak nie jest. Wielu programistów używających offsetów wciąż mówi o indeksach. I wielu programistów używających indeksów wciąż nie wie, że C, C ++, C #, ... używają przesunięć.

To jest problem z sformułowaniami .

(Uwaga na temat pracy Diskstry - mówi dokładnie to, co powiedziałem powyżej : matematyk używa indeksów opartych na 1. Ale Diskstra sądzi, że matematyk nie powinien ich używać, ponieważ niektóre wyrażenia byłyby wówczas brzydkie (np .: 1 <= n <= 0 Cóż, nie jestem pewien, czy ma rację w tej sprawie - wykonanie takiej zmiany paradygmatu w celu uniknięcia tych wyjątkowych pustych sekwencji wydaje się dużym problemem dla małego rezultatu ...)


2
Matematycy nie zawsze używają wskaźników opartych na 1. Widziałem, że x0 używał wiele razy dla początkowej wartości sekwencji. To zależy od tego, który z nich jest wygodniejszy.

2

Czy zdenerwowałeś się kiedyś przez „XX wiek”, odnoszący się w rzeczywistości do XX wieku? Jest to dobra analogia do żmudnych rzeczy, z którymi masz do czynienia przez cały czas, gdy używasz tablic 1.

Rozważ typowe zadanie tablicowe, takie jak metoda odczytu .O IO.stream:

int Read(byte[] buffer, int offset, int length)

Oto, co sugeruję, aby zrobić, aby przekonać się, że tablice oparte na 0 są lepsze:

W każdym stylu indeksowania napisz klasę BufferedStream, która obsługuje czytanie. Możesz zmienić definicję funkcji Read (np. Użyj dolnej granicy zamiast przesunięcia) dla tablic 1. Nie potrzebujesz niczego wymyślnego, po prostu to ułatw.

Które z tych wdrożeń jest prostsze? Który ma przesunięte +1 i -1 tu i tam? Tak myślałem. W rzeczywistości argumentowałbym, że jedynym przypadkiem, w którym styl indeksowania nie ma znaczenia, jest to, że powinieneś był użyć czegoś, co nie było tablicą, na przykład Set.


To zła analogia myląca logikę całkowitą z liczbą zmiennoprzecinkową.

2

Wynika to z budowy tablic. Nie ma sensu, aby zaczynali od jednego. Tablica to podstawowy adres w pamięci, rozmiar i indeks. Aby uzyskać dostęp do n-tego elementu, należy:

base + n * element_size

Zatem 0 jest oczywiście pierwszym przesunięciem.


1

Istnieje kilka różnych sposobów realizacji tego:

  • Indeksy tablic oparte na 0
  • Indeksy tablic oparte na 1
  • tablice oparte na 0 lub 1 (jak VB 6.0 ... to naprawdę okropne)

Ostatecznie nie sądzę, żeby miało znaczenie, czy język używa tablic opartych na 0 czy 1. Myślę jednak, że najlepszym wyborem jest użycie tablic opartych na 0, z tego prostego powodu, że większość programistów jest przyzwyczajona do tej konwencji i jest to zgodne z ogromną większością już napisanego kodu.

Jedynym sposobem, w jaki naprawdę możesz się pomylić, jest niespójność, taka jak Visual Basic. Baza kodu, którą obecnie utrzymuję, jest podzielona między tablice oparte na 0 i 1; i niezwykle trudno jest ustalić, który jest który. Prowadzi to do irytująco szczegółowej pętli for:

dim i as integer, lb as integer, ub as integer
lb = LBound(array)
ub = UBound(array)
for i = lb to ub
       '...
next

hahaha Pamiętam to, człowieku, który ssał ...
Dan Rosenstark,

Myślę, że pamiętam nawet tablice zaczynające się od liczb ujemnych. To tylko jeden z wielu powodów, dla których trzymam się z dala od VB.

1

Zero jest naturalne, gdy mówimy o lokalizacji przedmiotu w kolekcji liniowej.

Pomyśl o półce pełnej książek - pierwsza książka znajduje się równo z boczną ścianą półki - to położenie zero.

Myślę, że to zależy od tego, czy uważasz, że indeksy tablicowe są sposobem na znalezienie rzeczy lub odniesienie do rzeczy.


1

Wolę indeks oparty na 0, ponieważ ponieważ modulo (i operator AND, gdy jest używany dla modulo) zawsze zwraca 0 dla niektórych wartości.

Często używam takich tablic:

int blah = array[i & 0xff];

Często mylę ten rodzaj kodu, gdy używam indeksów opartych na 1.


1

Trudno jest bronić 0-base bez programowania dużej ilości kodu opartego na tablicach, takich jak wyszukiwanie ciągów i różne algorytmy sortowania / scalania lub symulacji tablic wielowymiarowych w tablicy jednowymiarowej. Fortran jest oparty na 1 i potrzebujesz dużo kawy, aby poprawnie wykonać ten rodzaj kodu.

Ale wykracza poza to. Bardzo przydatnym nawykiem umysłowym jest myślenie o długości czegoś, a nie o wskaźnikach jego elementów. Na przykład, tworząc grafikę opartą na pikselach, znacznie łatwiej jest myśleć o współrzędnych jako spadających między pikselami, a nie na nich. W ten sposób prostokąt 3x3 zawiera 9 pikseli, a nie 16.

Nieco bardziej naciąganym przykładem jest pomysł patrzenia w przyszłość podczas analizowania lub drukowania sum częściowych w tabeli. Podejście „zdrowego rozsądku” mówi: 1) uzyskaj następny znak, token lub wiersz tabeli i 2) zdecyduj, co z tym zrobić. Podejście przewidujące mówi 1), zakładając, że możesz to zobaczyć, i zdecyduj, czy chcesz, oraz 2) jeśli chcesz, „zaakceptuj” (co pozwala zobaczyć następny). Jeśli więc napiszesz pseudo-kod, jest to o wiele prostsze.

Jeszcze innym przykładem jest użycie „goto” w językach, w których nie masz wyboru, takich jak pliki wsadowe MS-DOS. Podejście „zdrowego rozsądku” polega na dołączaniu etykiet do bloków kodu, które należy wykonać, i oznaczaniu ich jako takich. Często lepszym rozwiązaniem jest umieszczanie etykiet na końcach bloków kodu w celu pominięcia ich. Dzięki temu jest „uporządkowany” i znacznie łatwiejszy do modyfikacji.


1

Tak właśnie jest i ma to miejsce od wielu lat. Zmiana, a nawet debata, jest tak samo bezcelowa, jak zmiana lub debata o zmieniających się światłach. Zróbmy niebieski = stop, czerwony = idź.

Sprawdź zmiany wprowadzone w czasie w Przepisach numerycznych dla C ++. Użyli makr do sfałszowania indeksowania opartego na 1, ale w wydaniu z 2001 roku poddali się i dołączyli do stada. Na ich stronie www.nr.com mogą znajdować się materiały wyjaśniające na temat przyczyn tego stanu rzeczy

BTW, irytujące są również warianty określania zakresu poza tablicą. Przykład: python vs. IDL; a [100: 200] kontra [100: 199], aby uzyskać 100 elementów. Muszę tylko nauczyć się dziwactw każdego języka. Zmiana języka, który robi to w jeden sposób, aby dopasować do drugiego, spowodowałaby takie przekleństwa i zgrzytanie zębów i nie rozwiązałaby żadnego prawdziwego problemu.


1

Wolę tablice oparte na 0, ponieważ, jak wspominają inni, ułatwia to matematykę. Na przykład, jeśli mamy 1-wymiarową tablicę 100 elementów emulującą siatkę 10x10, to jaki jest indeks tablicy i elementu w rzędzie r, col c:

Oparte na 0: i = 10 * r + c
Na podstawie 1: i = 10 * (r - 1) + c

A biorąc pod uwagę indeks i, powrót do wiersza i kolumny to:

Oparte na 0: c = i% 10
         r = podłoga (i / 10)
1: c = (i - 1)% 10 + 1
         r = sufit (i / 10)

Biorąc pod uwagę, że powyższa matematyka jest wyraźnie bardziej złożona, gdy używasz tablic opartych na 1, logiczne wydaje się wybieranie tablic opartych na 0 jako standardu.

Myślę jednak, że ktoś mógłby twierdzić, że moja logika jest wadliwa, ponieważ zakładam, że istniałby powód do reprezentowania danych 2D w tablicy 1D. W C / C ++ napotkałem wiele takich sytuacji, ale muszę przyznać, że konieczność wykonywania takich obliczeń jest w pewnym stopniu zależna od języka. Jeśli tablice naprawdę cały czas wykonują całą matematykę indeksu dla klienta, to kompilator może po prostu przekonwertować dostęp do macierzy opartych na M na 0 w czasie kompilacji i ukryć wszystkie szczegóły implementacji przed użytkownikiem. W rzeczywistości do wykonania tego samego zestawu operacji można użyć dowolnej stałej czasowej kompilacji, chociaż takie konstrukcje prawdopodobnie po prostu doprowadziłyby do niezrozumiałego kodu.

Być może lepszym argumentem byłoby to, że zminimalizowanie liczby operacji na indeksie tablicowym w języku z tablicami opartymi na 1 wymagałoby wykonania podziału liczb całkowitych za pomocą funkcji pułapu. Jednak z matematycznego punktu widzenia dzielenie liczb całkowitych powinno zwrócić d reszty r, gdzie d i r są dodatnie. Dlatego tablice oparte na 0 powinny być używane w celu uproszczenia matematyki.

Na przykład, jeśli generujesz tabelę przeglądową z N elementami, najbliższym indeksem przed bieżącą wartością do tablicy dla wartości x byłoby (w przybliżeniu, ignorowanie wartości, gdzie wynikiem jest liczba całkowita przed zaokrągleniem):

Oparte na 0 z podłogą: podłoga ((N - 1) * x / xRange)
1 oparty na podłodze: podłoga ((N - 1) * x / xRange) + 1
1 oparty na ceil: ceil ((N - 1) * x / xRange)

Zauważ, że jeśli stosowana jest standardowa konwencja zaokrąglania w dół, tablice oparte na 1 wymagają dodatkowej operacji, co jest niepożądane. Tego rodzaju matematyki nie może ukryć kompilator, ponieważ wymaga wiedzy niższego poziomu o tym, co dzieje się za kulisami.


To dobry powód, dopóki nie będziesz mieć języków wyższego poziomu obsługujących tablice wielowymiarowe.

1

Założę się, że programista był po prostu zirytowany sprzeczną z intuicją tablicą opartą na 0 w codziennym myśleniu i opowiadał się za bardziej intuicyjnymi sposobami opisywania tablic. Ironiczne jest to, że jako ludzie spędziliśmy tyle czasu na wymyślenie „klas”, abyśmy mogli opisać rzeczy w bardziej ludzki sposób w naszym kodzie, ale kiedy patrzymy na tablice 0 na 1, wydaje się, że się rozłączamy sama logika.

Jeśli chodzi o komputer i matematycznie 0, prawdopodobnie będzie lepiej, ale czuję, że tutaj brakuje punktu. Jeśli chcielibyśmy opisać rzeczy w bardziej ludzki sposób (np. Klasy), dlaczego nie chcielibyśmy tego samego dla innych części języka? Czy nie jest to w równym stopniu logiczne lub uzasadnione (czy też ma wyższy priorytet w tej kwestii ...), aby uczynić język łatwiejszym do zrozumienia i użytecznym dla ludzi, a tym samym mniej podatnym na scenariusze, które mają tendencję do tworzenia błędów logicznych i bardziej podatne do szybszej produkcji użytecznego dzieła. Przykład PHP:

array(1 => 'January', 'February', 'March');

daje 1 tablicę na nasze żądanie.

Dlaczego nie mieć normy:

array('January', 'February', 'March');

Wyjątkiem jest:

array(0 => 'Value for scenario where 0 *has* to be used as the key',
      'value2', 'value3');

W przypadku, powiedzmy, PHP, zakładam, że 80% czasu to, że tablica 1 oparta na domyślnej składni zmniejszyłaby błędy logiczne w rzeczywistych przypadkach użycia, a przynajmniej nie powodowała więcej średnio, robiąc to dużo ułatwia koderowi szybsze generowanie użytecznego kodu. Pamiętaj, zakładam, że nadal będzie dostępna opcja, tablica (0 => „wartość”), gdy jest potrzebna, ale zakładam również, że przez większość czasu praktyczne jest posiadanie czegoś bliższego opisowi z prawdziwego świata.

To naprawdę nie wydaje się zbyt dalekie od wniosku, patrząc na to z tej perspektywy. Zbliżając się do interfejsu, czy jest to system operacyjny, czy język dla programisty, im bliżej ludzkiego myślenia i nawyków, którymi go projektujemy, tym szczęśliwsi w większości przypadków i mniej nieporozumień między człowiekiem a komputerem (ludzka logika - błędy) i szybszą produkcję itp. będziemy mieli. Jeśli 80% czasu w prawdziwym świecie opisuję rzeczy za pomocą 1 podczas tworzenia list lub liczenia, to komputer powinien idealnie interpretować moje znaczenie w sposób, który rozumie przy jak najmniejszej ilości informacji lub zmienić od mojego normalnego sposobu opisywania czegoś, jak to możliwe. Krótko mówiąc, im bliżej możemy modelować świat rzeczywisty, tym lepsza jakość abstrakcji. Więc to, czego chce, nie jest głupie, ponieważ jest to ostateczny cel i byłby dowodem potrzeby większej abstrakcji. Komputer nadal może ostatecznie postrzegać to jako specjalne zastosowanie tablicy opartej na 0. Nie obchodzi mnie to, w jaki sposób komputer interpretuje to, o ile jest to prostszy i bardziej intuicyjny sposób na opisanie tego, co chcę, przy mniejszej liczbie błędów w czasie.

To moje dwa centy. Poważnie wątpię, co miał na myśli lub co zostało zinterpretowane. Prawdopodobnie miał na myśli: „Nienawidzę mniej intuicyjnego sposobu mówienia komputerowi, czego chcę”. :) Czyż nie wszyscy? lol.


1

Jest to możliwe, jeśli zachowasz ostrożność podczas pisania „własnego” kodu. Możesz założyć, że indeks zaczyna się od n dla wszystkich n> = 0 i odpowiednio zaprogramować.

Jeśli chodzi o standard, Borealid ma świetny argument.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.