Domanda

Perché alcune lingue distinguono tra metodi che restituiscono un valore e metodi che non lo fanno?

vale a dire. in Oracle PL / SQL, dove la differenza principale tra una funzione e una procedura è che la funzione deve restituire un valore e la procedura non deve.

Allo stesso modo per le lingue che non lo fanno, perché no?


EDIT: ho trovato una domanda correlata che potrebbe interessare le persone a leggere questa domanda:

È stato utile?

Soluzione

Perché nelle concezioni originali della teoria e della pratica dell'informatica, le funzioni e le subroutine non avevano praticamente nulla a che fare l'una con l'altra.

FORTRAN è di solito accreditato come la prima lingua che ha implementato entrambi e dimostrato le distinzioni. (Anche il primo LISP ha avuto un ruolo un po 'opposto in questo, ma ha avuto un impatto limitato al di fuori del mondo accademico).

Seguendo le tradizioni della matematica (di cui CS faceva ancora parte negli anni '60) le funzioni erano viste solo come l'incapsulamento di calcoli matematici parametrizzati intesi unicamente a restituire un valore a un'espressione più ampia. Che potresti chiamarlo " bare " (F = AZIMUTH (SECONDS)) era semplicemente un banale caso d'uso.

Le subroutine, d'altra parte, erano viste come un modo per nominare un gruppo di istruzioni che avrebbero avuto qualche effetto. I parametri hanno dato una spinta enorme alla loro usabilità e l'unica ragione per cui sono stati autorizzati a restituire valori di parametri modificati è stata quella di poter riportare il loro stato senza dover fare affidamento su variabili globali.

Quindi, in realtà non avevano alcuna connessione concettuale, a parte l'incapsulamento e i parametri.

La vera domanda è: " Come hanno fatto tanti sviluppatori a vederli uguali? "

E la risposta è C.

Quando K + R originariamente progettava il loro linguaggio di tipo macro assembler di alto livello per il PDP-11 (potrebbe essere stato avviato sul PDP-8?), non avevano delusioni di indipendenza hardware. Praticamente ogni "unico" caratteristica del linguaggio era un riflesso del linguaggio e dell'architettura della macchina PDP (vedere i ++ e --i). Una di queste era la realizzazione che le funzioni e le subroutine potevano essere (e sempre erano) implementate in modo identico nel PDP, tranne per il fatto che il chiamante aveva semplicemente ignorato il valore di ritorno (in R0 [, R1]) per le subroutine.

Così nacque il puntatore del vuoto, e dopo che il linguaggio C aveva preso il controllo dell'intero mondo della programmazione, la percezione errata che questo artefatto dell'implementazione HW / OS (sebbene vero su quasi tutte le piattaforme successive) era la stessa della semantica del linguaggio.

Altri suggerimenti

In un'impostazione pura o tipizzata da effetti c'è un mondo di differenza, perché ovviamente i metodi che "non restituiscono nulla" sono utili solo per i loro effetti collaterali.

Questo è analogo alla distinzione tra espressioni e affermazioni, che può decodificare una lingua ed eliminare una classe di programmi solitamente sbagliati (che, ovviamente, è il motivo per cui C non lo fa;)).

Per fare un piccolo esempio, quando si distingue chiaramente tra espressioni e istruzioni, if (x = 3) , al contrario di if (x == 3) è sintatticamente errato (per l'utilizzo di un'istruzione in cui era prevista un'espressione) e non semplicemente un errore di tipo (per l'utilizzo di un numero intero in cui era previsto un valore booleano). Ciò ha il vantaggio di vietare anche if (x = true) che sarebbe consentito da una regola di tipo in un contesto in cui le assegnazioni sono espressioni che hanno il valore del loro operando di destra.

In un linguaggio che incapsula gli effetti con le monadi, l'importante distinzione diventa quella tra:

  • funzioni che restituiscono () che sono funzioni pure e possono restituire solo un valore vuoto inutile chiamato () o divergere
  • funzioni che restituiscono IO () (o unità in qualche altra monade) che sono funzioni senza " risultato " eccetto gli effetti nella monade IO (o qualunque)

Mi scusi se rispondo a una domanda di due anni, in particolare con qualcosa di unico nella mia lingua Felix http: // felix-lang. org ma qui va comunque :)

In Felix, le funzioni e le procedure sono sostanzialmente diverse, e non sono solo le procedure che hanno effetti collaterali e sono chiamate in istruzioni, mentre le funzioni non hanno effetti collaterali e sono usate nelle espressioni (perché Felix ha anche generatori che sono funzioni con effetti collaterali .. :)

No, il modello di esecuzione è sostanzialmente diverso, principalmente per motivi di prestazioni, ma non del tutto. Il modello è:

  • Le funzioni mettono il loro indirizzo di ritorno nello stack della macchina e anche il valore di ritorno.
  • Le procedure utilizzano un elenco collegato sull'heap. Il codice procedurale è piatto, non utilizza lo stack della macchina.

Questo è generalmente inefficiente, quindi perché farlo? La risposta è: le procedure Felix sono tutte potenzialmente co-routine (fibre). Possono passare il controllo a un'altra procedura accedendo a un canale. Ciò provoca uno scambio di controllo.

  • Per motivi di prestazioni, la copia dello stack della macchina in centrale non è un'opzione.
  • Per motivi di gestione della memoria, anche lo scambio di puntatori di stack non è un'opzione.

Il sistema operativo in genere scambia i puntatori dello stack con i thread, il che è ragionevolmente veloce, ma presenta un problema fondamentale sui computer con indirizzi lineari: è necessario limitare la dimensione massima dello stack a un valore ridicolmente ridotto o limitare il numero di thread ad un valore ridicolmente piccolo. Su una macchina a 32 bit, lo spazio degli indirizzi non è sufficiente per contemplare questa soluzione. Su una macchina a 64 bit, lo scambio di stack ha più potenziale, ma ovviamente le richieste degli utenti crescono sempre più rapidamente dell'hardware 3 giorni dopo il rilascio .. :)

Felix scambia solo un singolo puntatore alle pile basate su heap, quindi i cambi di contesto sono incredibilmente veloci e lo spazio degli indirizzi è molto ridotto. Naturalmente il costo è l'allocazione degli heap nelle chiamate di procedura.

Nel compilatore, gran parte dell'architettura del modello teorico è ottimizzata su un "as-if" base, quindi le prestazioni e l'implementazione effettive possono essere molto diverse dal modello teorico, a condizione che il compilatore possa dimostrare che non si può dire la differenza .. oltre a essere negata l'opportunità di fare una tazza di caffè con il tempo libero:)

Quindi qui hai una risposta diversa sul perché funzioni e procedure potrebbero essere trattate diversamente.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top