Variadische Funktion

Dies ist eine alte Version dieser Seite, zuletzt bearbeitet am 3. Februar 2016 um 18:25 Uhr durch FUZxxl (Diskussion | Beiträge) (Änderung 151010817 von 79.196.135.248 rückgängig gemacht; diese Information steht bereits im nächsten Halbsatz. Artikel bitte vor dem Bearbeiten lesen.). Sie kann sich erheblich von der aktuellen Version unterscheiden.

Als variadische Funktion bezeichnet man in Programmiersprachen Funktionen, Prozeduren oder Methoden mit unbestimmter Arität, also solche, deren Parameteranzahl nicht bereits in ihrer Deklaration festgelegt ist. In einigen Sprachen wie C, C++, Java und Lua wird dies in der Funktionsdeklaration mit Auslassungspunkten angezeigt, der sogenannten Ellipse. An Stelle der Ellipse können beliebig viele Argumente (oder auch keine) übergeben werden. Nützlich sind variadische Funktionen beispielsweise bei der Verknüpfung mehrerer Zeichenketten oder beim Aufsummieren von Zahlenreihen und generell bei Operationen, die prinzipiell auf eine beliebige Anzahl Operanden angewandt werden können.

Umsetzung in verschiedenen Programmiersprachen

C und C++

Der Zugriff auf variadische Parameter erfolgt hier über spezielle Makros.[1] Einige Bibliotheksfunktionen von C sind mit Ellipse deklariert, da es in C keine Möglichkeit der Funktionsüberladung gibt, Beispiele sind die Bibliotheksfunktionen printf und scanf zur formatierten Aus- beziehungsweise Eingabe.[2] Ein grundlegendes Problem bei diesem Mechanismus ist, dass der aufgerufenen Funktion keine Information über Anzahl und Typ der übergebenen Argumente zur Verfügung steht. Der Programmierer muss also durch zusätzliche Maßnahmen (wie etwa mit dem format string bei printf) sicherstellen, dass die Argumente richtig interpretiert werden und nicht mehr Argumente verarbeitet werden, als tatsächlich vorhanden waren. Außerdem muss mindestens ein fester Parameter vor der Ellipse angegeben werden.

Die Funktionsparameter werden, bevor sie an die Funktion übergeben werden, automatisch nach folgendem Schema in größere Typen umgewandelt:

Ausgangstyp Zieltyp
char, signed char, short int
unsigned char, unsigned short unsigned int
float double

Alle anderen Typen bleiben erhalten.

Beispielsweise könnte man in C den Mittelwert von beliebig vielen Ganzzahlen über folgende Funktion errechnen lassen:

#include <stdarg.h>

double mittelwert(int num_zahlen, ...)
{
    va_list argumente;
    int i;
    double summe = 0;

    va_start(argumente, num_zahlen); /* Benötigt den letzten bekannten Parameter, um die Speicheradresse der anderen Parameter berechnen zu können */
    for (i = 0; i < num_zahlen; i++) {
        summe += va_arg(argumente, double); /* Inkremetiert argumente zum nächsten Argument */
    }
    va_end(argumente);

    return summe / num_zahlen;
}

In C++ gelten Ellipsen heutzutage als überholt, da C++ elegantere Möglichkeiten zur Funktionsüberladung und das Konzept der Defaultargumente bietet, und als schlechter Stil, da sie keine Typsicherheit bieten.[3] Sie sind jedoch die einzige Möglichkeit, eine nahezu unbegrenzte Anzahl an Argumenten zu übergeben.

In C++ gibt es zusätzlich noch die Möglichkeit, variadische Funktionstemplates zu benutzen. Diese sind typsicher, aber aufwändiger zu benutzen, da man die sogenannten "template parameter packs" rekursiv auflösen muss:

unsigned produkt(unsigned u) {return u;}  // Rekursions-Ende

template<typename... Args>
unsigned produkt(unsigned u1, Args... rest)
{
    return u1 * produkt(rest...);
}

// Aufruf:
unsigned p = produkt(1,2,3,4,5);

C#

In C# wird in der Deklaration das Schlüsselwort "params" verwendet. Auch der Typ wird mitangegeben. Einer solchen Methode kann eine durch Kommas getrennte Liste übergeben werden (diese Liste kann auch leer sein). Nach dem "params" Schlüsselwort sind keine weiteren Parameter mehr zulässig. Zudem darf das "params" Schlüsselwort nur einmal in der Methodendeklaration vorkommen.[4]

public class Summe
{
    public static int sum(params int[] list)
    {
        int result = 0;

        for (int i = 0; i < list.Length; i++)
        {
            result += list[i];
        }

        return result;
    }

    static void Main()
    {
        int a = sum();             // Ergebnis: a = 0
        int b = sum(1);            // Ergebnis: b = 1
        int c = sum(1, 2, 3, 4);   // Ergebnis: c = 10
    }
}

Go

Die Sprache Go erlaubt die Deklaration variadischer Funktionen über den Anhang einer Ellipse ... an den Namen des letzten Parameters.[5] Es können dann beliebig viele Parameter des angegebenen Types übergeben werden, diese sind dann über ein Slice in der aufgerufenen Funktion verfügbar. Ferner ist es möglich durch Anhängen einer Ellipse an den letzten Parameter im Aufruf einer variadischen Funktion, direkt ein Slice an Stelle einzelner Parameter zu übergeben. Es folgt ein Beispiel analog zu obigem C#-Beispiel.

// Deklariere eine Funktion Sum, die mit beliebig viele Parameter vom Typ int aufgerufen
// wird und die Summe ihrer Parameter zurückgibt.
func Sum(x ...int) (n int) {
    for i := range x { // Iteriere über alle Indizes von x
        n += x[i]
    }

    return
}

// Beispielfunktion zum Aufruf der Funktion Sum()
func ExampleSum() {
    Sum() // leere Summe. Gibt 0 zurück
    Sum(1, 2, 3) // Gibt 6 zurück. x hat den Wert []int{1, 2, 3}
    v := []int{23, 42, 1337}
    Sum(v...) // Wie Sum(23, 42, 1337)
}

Java

Auch in Java verwendet man in der Deklaration die Ellipse. Hier spricht man von Methoden mit variabler Argumentanzahl, oder kurz Varargs.[6] Im Gegensatz zu C und C++ wird aber der Typ mit angegeben. Im Hintergrund wird die Parameterliste in ein Array übersetzt, so dass auch innerhalb des Funktionsrumpfes der Parameter wie ein Array behandelt werden muss.[7]

JavaScript

In JavaScript werden die beliebig vielen Funktionsargumente dem arguments-Objekt übergeben, das ein Array-artiges Objekt mit den zusätzlichen Eigenschaften arguments.length und arguments.callee ist. Mit dem arguments-Objekt können sogar die Funktionsparameter überschrieben werden, wie das nachfolgende Beispiel zeigt.

function meineFunk(x) {
    arguments[0] = 5; // die Variable x wird hier überschrieben!
    ...               // x ist nun 5.
}

Lua

In Lua kann in der Parameterdeklaration einer Funktion als letztes (auch einziges) Argument die Ellipse ... angegeben werden. Diese kann dann an andere Funktionen weitergereicht werden, wodurch sie sich nach Länge (Anzahl) und einzelnen Elementen analysieren lässt, namentlich mittels select().

Perl

Da in Perl 5 die Argumente ohne Rücksicht auf ihren Typ im Spezialarray @_ landen, sind alle Subroutinen immer variadisch, so lange es kein Prototyp explizit verhindert. Die mit 5.20 eingeführten, optionalen Signaturen prüfen die Arität. Damit sind nur solche Routinen variadisch, deren letzter Parameter ein Array oder Hash (verlangt gerade Anzahl von Werten) ist.

In Perl 6 kann man als letzten Parameter einen slurpy Array angeben, welches mit dem Präfix * gekennzeichnet ist und alle zusätzlichen Argumente „aufsaugt“.

PHP

PHP unterstützt erst ab Version 4 variadische Funktionen. Bis Version 5.5 wird kein besonderes Kennzeichen in der Funktionsdeklaration verwendet; die Argumente können mit Hilfe spezieller Funktionen ausgewertet werden.[8] Ab Version 5.6 stehen variadische Funktionen als natives Sprachfeature über die Ellipse zur Verfügung.[9]

Ruby

In Ruby werden variable Argumentanzahlen durch einen Stern vor dem Parameternamen gekennzeichnet.[10] Auch hier wird dieser Parameter als ein Array behandelt, in dem alle Argumente gesammelt werden, die der vorgegeben Anzahl an Argumenten folgen.

Python

Python bietet dieselbe Funktionalität wie Ruby. Darüber hinaus kann man überzählige Argumente benennen und deren Namen zusammen mit dem Wert in einer Tabelle („Wörterbuch“ im Python-Jargon) speichern, wenn Parameternamen zwei Sterne vorangestellt sind.[11]

Haskell

Haskell erlaubt es nicht direkt, variable Argumentanzahlen zu verwenden, allerdings kann man dies über trickreiche Verwendung von Typklassen nahezu beliebig nachbilden. (So sind mit einer entsprechenden Implementation z. B. auch typsichere variadische Funktionen mit verschiedenen Argumenttypen möglich).[12][13]

Siehe auch

Einzelnachweise

  1. Variable Argumentlisten in C. Abgerufen am 9. September 2010.
  2. Definition von printf. Abgerufen am 9. September 2010 (englisch).
  3. Ellipses (and why to avoid them). Abgerufen am 9. September 2010 (englisch).
  4. Params in C#. Abgerufen am 23. August 2013 (englisch).
  5. Spezifikation der Sprache Go. Abgerufen am 26. März 2014 (englisch).
  6. Varargs in Java. Abgerufen am 26. März 2014.
  7. Varargs in Java im Sprachüberblick auf oracle.com. Abgerufen am 1. September 2011 (englisch).
  8. Variadische Funktionen in PHP. Abgerufen am 9. September 2010.
  9. PHP Dokumentation: Funktionsparameter. Abgerufen am 8. September 2015.
  10. David Thomas, Andrew Hunt: Programmieren mit Ruby. Addison-Wesley, München 2002, ISBN 3-8273-1965-X, S. 281 (eingeschränkte Vorschau in der Google-Buchsuche).
  11. Abschnitt Function Definition in der Python-Schnellübersicht. Abgerufen am 9. September 2010 (englisch).
  12. Implementierung in Haskell mit detaillierter Beschreibung. Abgerufen am 12. August 2010 (englisch).
  13. Beschreibung des Vorgehen bei der Haskell-Variante von printf zur Erzeugung einer polyvariadischen Funktion. Abgerufen am 12. August 2010 (englisch).