Il mio commento ha ricevuto molti voti positivi, quindi lo sto espandendo come risposta.
La premessa di questa domanda prende una visione ristretta di ciò che i linguaggi di programmazione hanno effettivamente. Un sacco di lingue nella famiglia ML hanno liste piuttosto che matrici (semantica testa / coda invece di accesso casuale). E C non ha affatto hash hash . Inoltre, q / kdb + ha un contenitore tabella che riproduce un database simile a SQL in memoria.
Detto questo, tutti i linguaggi di programmazione pratica hanno loop e / o ricorsione. Questo tipo di flusso di controllo richiede che i dati possano essere indirizzati indirettamente.
Ad esempio, non posso dare un nome univoco a tutti i miei dati. Cioè, non posso chiamare le mie variabili alpha , bravo , charlie , ecc .; non solo esaurirò i nomi, ma non potrò nemmeno fare riferimento genericamente a una variabile in relazione a un'altra.
Quindi invece devo avere un indirizzamento indiretto, come x 1 , x 2 , x 3 , ecc. Questa è una vera natura degli array! In alternativa, posso prendere una vista ricorsiva e indagare su first-of-xs e remainder-of-xs . Finché ho un indirizzamento indiretto, posso usare loop o ricorsioni.
Come @DonalFellows menziona nei commenti, ci sono modelli di calcolo che sono Turing completi e tuttavia non hanno indirizzamento indiretto Nessuno in realtà scrive il codice in questo modo.
Quindi, da un punto di vista pratico, credo che gli array o le liste siano necessari per la completezza in un linguaggio di programmazione poiché supportano i tradizionali meccanismi di controllo del flusso. Tutti gli altri contenitori come hash e le tabelle sono più per comodità.