Argomento predefinito - Default argument
Nella programmazione di computer , un argomento predefinito è un argomento di una funzione che un programmatore non è tenuto a specificare. Nella maggior parte dei linguaggi di programmazione, le funzioni possono richiedere uno o più argomenti. Di solito, ogni argomento deve essere specificato per intero (questo è il caso del linguaggio di programmazione C ). I linguaggi successivi (ad esempio, in C ++ ) consentono al programmatore di specificare argomenti predefiniti che hanno sempre un valore, anche se non viene specificato quando si chiama la funzione.
Argomenti predefiniti in C ++
Considera la seguente dichiarazione di funzione:
int MyFunc(int a, int b, int c = 12);
Questa funzione accetta tre argomenti, di cui l'ultimo ha un valore predefinito di dodici. Il programmatore può chiamare questa funzione in due modi:
int result = MyFunc(1, 2, 3);
result = MyFunc(1, 2);
Nel primo caso il valore per l'argomento chiamato c è specificato come normale. Nel secondo caso, l'argomento viene omesso e al suo posto verrà utilizzato il valore predefinito 12 .
Non è possibile sapere se l'argomento è stato specificato dal chiamante o se è stato utilizzato il valore predefinito.
Il metodo sopra menzionato è particolarmente utile quando si desidera impostare criteri di default in modo che la funzione possa essere chiamata con o senza parametri. Considera quanto segue:
void PrintGreeting(std::ostream& stream = std::cout) {
// This outputs a message to the given stream.
stream << "hello world!";
}
La chiamata di funzione:
PrintGreeting();
per impostazione predefinita stamperà " ciao mondo !" allo standard output std::cout (tipicamente lo schermo). D'altra parte, qualsiasi oggetto di tipo std::ostreampuò ora essere passato alla stessa funzione e la funzione verrà stampata sul flusso dato invece che sullo standard output.
PrintGreeting(std::cerr);
Poiché i valori degli argomenti predefiniti sono "inseriti" nel sito della chiamata piuttosto che nel corpo della funzione che viene chiamata, le funzioni virtuali prendono i valori degli argomenti predefiniti dal tipo statico del puntatore o del riferimento attraverso il quale viene effettuata la chiamata, piuttosto che dal tipo dinamico dell'oggetto che fornisce il corpo della funzione virtuale.
struct Base {
virtual std::pair<int, int> Foo(int x = 1) {
return {x, 1};
}
};
struct Derived : public Base {
std::pair<int, int> Foo(int x = 2) override {
return {x, 2};
}
};
int main() {
Derived d;
Base& b = d;
assert(d.Foo() == std::make_pair(2, 2));
assert(b.Foo() == std::make_pair(1, 2));
}
Metodi sovraccaricati
Alcuni linguaggi, come Java , non hanno argomenti predefiniti. Tuttavia, lo stesso comportamento può essere simulato utilizzando il metodo di sovraccarico per creare metodi di sovraccarico con lo stesso nome, che accettano un numero diverso di argomenti; e le versioni con meno argomenti chiamano semplicemente le versioni con più argomenti, con gli argomenti predefiniti come argomenti mancanti:
int MyFunc(int a, int b) { return MyFunc(a, b, 12); }
int MyFunc(int a, int b, int c) { /* main implementation here */ }
Tuttavia, oltre a molti altri svantaggi , poiché gli argomenti predefiniti non sono modellati nel sistema dei tipi, il tipo di callback (ovvero una funzione di ordine superiore) non può esprimere che accetta nessuno degli overload né simulare gli argomenti predefiniti con funzioni sovraccaricate. Considerando che, in JavaScript la definizione della funzione non sovraccaricata può sostituire il valore predefinito quando il valore di input è undefined(indipendentemente dal fatto che fosse implicitamente undefinedtramite l'assenza dell'argomento nel sito della chiamata o un undefinedvalore passato esplicitamente ); che è modellato come un tipo di parametro argomento opzionale ?: in TypeScript . La soluzione di JavaScript non viene risolta staticamente (ovvero non in fase di compilazione, motivo per cui TypeScript modella solo le opzioni e non i valori predefiniti nella firma del tipo della funzione) quindi incorre in un sovraccarico di runtime aggiuntivo, sebbene fornisca maggiore flessibilità in quanto i callback possono indipendentemente controllare i loro valori predefiniti invece che dettati centralmente dalla (firma del tipo di callback nella) firma del tipo della funzione che immette la richiamata. La soluzione TypeScript può essere simulata in Java con il Optionaltipo tranne che l'analogo di un implicito undefinedper ogni argomento assente è un esplicito Optional.<Integer>absent()nel sito della chiamata.
Valutazione
Per ogni chiamata di funzione i valori degli argomenti predefiniti devono essere passati alla funzione chiamata.
Se un valore di argomento predefinito contiene effetti collaterali, è significativo quando tali effetti collaterali vengono valutati: una volta per l'intero programma (al momento dell'analisi, della compilazione o del caricamento) o una volta per chiamata di funzione, al momento della chiamata.
Python è un linguaggio notevole che valuta le espressioni negli argomenti predefiniti una volta, nel momento in cui viene valutata la dichiarazione di funzione. Se si desidera la valutazione per chiamata di funzione, può essere replicata facendo in modo che l'argomento predefinito sia un valore sentinella , ad esempio None, e quindi facendo in modo che il corpo della funzione valuti gli effetti collaterali del valore predefinito solo se è stato passato il valore sentinella.
Per esempio:
import random
def eager(a=random.random()):
return a
x = eager()
y = eager()
assert x == y
def lazy(a=None):
if a is None:
a = random.random()
return a
x = lazy()
y = lazy()
assert x != y
Estensione
Generalmente un argomento predefinito si comporterà in modo identico a un argomento passato da parametro o una variabile locale dichiarata all'inizio della funzione e avrà lo stesso ambito ed estensione (durata) di un parametro o di un'altra variabile locale, vale a dire una variabile automatica che viene deallocata alla cessazione della funzione.
In altri casi un argomento predefinito può invece essere allocato staticamente. Se la variabile è mutabile, manterrà il suo valore tra le chiamate di funzione, come con una variabile statica .
Questo comportamento si trova in Python per i tipi mutabili, come gli elenchi. Come per la valutazione, per garantire la stessa estensione di una variabile locale, si può utilizzare un valore sentinella:
def eager(a=[]):
return a
x = eager()
x += [1]
assert eager() == [1]
def lazy(a=None):
if a is None:
a = []
return a
x = lazy()
x += [1]
assert lazy() == []