Welche Werkzeuge gibt es für die funktionale Programmierung in C?

Lesezeit: 7 Minuten

Benutzeravatar von Adam Rosenfield
Adam Rosenfeld

Ich habe in letzter Zeit viel darüber nachgedacht, wie ich funktionale Programmierung in C (nicht C++). Offensichtlich ist C eine prozedurale Sprache und unterstützt die funktionale Programmierung nicht wirklich nativ.

Gibt es Compiler-/Spracherweiterungen, die der Sprache einige funktionale Programmierkonstrukte hinzufügen? GCC bietet verschachtelte Funktionen als Spracherweiterung; Verschachtelte Funktionen können auf Variablen aus dem übergeordneten Stapelrahmen zugreifen, aber dies ist noch weit entfernt von ausgereiften Closures.

Eine Sache, die meiner Meinung nach in C sehr nützlich sein könnte, ist zum Beispiel, dass Sie überall dort, wo ein Funktionszeiger erwartet wird, einen Lambda-Ausdruck übergeben können, wodurch ein Abschluss erstellt wird, der in einen Funktionszeiger zerfällt. C++0x wird Lambda-Ausdrücke enthalten (was ich großartig finde); Ich suche jedoch nach Tools, die auf reines C anwendbar sind.

[Edit] Zur Verdeutlichung: Ich versuche nicht, ein bestimmtes Problem in C zu lösen, das besser für die funktionale Programmierung geeignet wäre; Ich bin nur neugierig, welche Tools es gibt, wenn ich das tun möchte.

  • Anstatt nach Hacks und nicht-portablen Erweiterungen zu suchen, um zu versuchen, C in etwas zu verwandeln, was es nicht ist, warum verwenden Sie nicht einfach eine Sprache, die die Funktionalität bietet, nach der Sie suchen?

    – Robert Gamble

    19. Oktober 2008 um 5:27 Uhr

  • Siehe auch verwandte Frage:

    – Andy Brice

    21. Oktober 2008 um 7:35 Uhr

  • @AndyBrice Diese Frage bezieht sich auf eine andere Sprache und scheint eigentlich keinen Sinn zu machen (C++ hat kein “Ökosystem” im gleichen Sinne wie.

    – Kyle Strand

    20. Januar 2016 um 5:46 Uhr

Benutzeravatar von Joe D
Joe D

Sie können die verschachtelten Funktionen von GCC verwenden, um Lambda-Ausdrücke zu simulieren. Tatsächlich habe ich ein Makro, das dies für mich erledigt:

#define lambda(return_type, function_body) \
  ({ \
    return_type anon_func_name_ function_body \
    anon_func_name_; \
  })

Verwenden Sie so:

int (*max)(int, int) = lambda (int, (int x, int y) { return x > y ? x : y; });

  • Das ist wirklich cool. Es scheint __fn__ ist nur ein willkürlicher Name für die im Block definierte Funktion ({}), nicht irgendeine GCC-Erweiterung oder ein vordefiniertes Makro? Die Namenswahl für __fn__ (sieht sehr nach GCC-Definition aus) hat mich wirklich dazu gebracht, mir den Kopf zu kratzen und die GCC-Dokumentation nach keinem guten Effekt zu durchsuchen.

    – FooF

    15. August 2012 um 5:08 Uhr

  • Leider funktioniert dies in der Praxis nicht: Wenn Sie möchten, dass die Schließung etwas erfasst, ist ein ausführbarer Stack erforderlich, was eine schreckliche Sicherheitspraxis ist. Ich persönlich halte es für überhaupt nicht sinnvoll.

    – Demi

    2. August 2017 um 2:53 Uhr

  • Es ist nicht im Entferntesten nützlich, aber es macht Spaß. Deshalb wird die andere Antwort akzeptiert.

    – Jo D

    2. August 2017 um 10:16 Uhr

  • Es macht Spaß, aber nutzlos

    – Rokstar

    31. Juli 2020 um 16:33 Uhr

Bei der funktionalen Programmierung geht es nicht um Lambdas, sondern um reine Funktionen. Das Folgende fördert also im Großen und Ganzen den funktionalen Stil:

  1. Verwenden Sie nur Funktionsargumente, verwenden Sie keinen globalen Zustand.

  2. Minimieren Sie Nebenwirkungen, dh printf oder beliebige IO. Geben Sie Daten zurück, die IO beschreiben, die ausgeführt werden können, anstatt die Seiteneffekte direkt in allen Funktionen zu verursachen.

Dies kann in einfachem c erreicht werden, keine Notwendigkeit für Magie.

  • Ich denke, Sie haben diese extreme Sichtweise der funktionalen Programmierung bis an den Punkt der Absurdität getrieben. Was nützt eine reine Angabe von z. mapwenn man nicht die Möglichkeiten hat, ihm eine Funktion zu übergeben?

    – Jonathan Leonhard

    18. Mai 2014 um 4:34 Uhr


  • Ich denke, dieser Ansatz ist viel pragmatischer als die Verwendung von Makros, um eine funktionale Sprache in c zu erstellen. Die angemessene Verwendung reiner Funktionen verbessert ein System eher als die Verwendung von map anstelle von for-Schleifen.

    – Andy Till

    18. Mai 2014 um 15:50 Uhr

  • Sicher wissen Sie, dass die Karte nur der Ausgangspunkt ist. Ohne erstklassige Funktionen ist jedes Programm (selbst wenn alle seine Funktionen „rein“) sind (im Sinne der Informationstheorie) von niedrigerer Ordnung als sein Äquivalent mit erstklassigen Funktionen. Dieser deklarative Stil, der nur mit erstklassigen Funktionen möglich ist, ist aus meiner Sicht der Hauptnutzen von FP. Ich denke, Sie tun jemandem einen schlechten Dienst, wenn Sie andeuten, dass die Ausführlichkeit, die sich aus dem Mangel an erstklassigen Funktionen ergibt, alles ist, was FP zu bieten hat. Es sollte beachtet werden, dass der Begriff FP historisch eher erstklassige Funktionen als Reinheit implizierte.

    – Jonathan Leonhard

    18. Mai 2014 um 20:23 Uhr

  • Nennt man das nicht “reine funktionale Programmierung”? Ich würde sagen, bei der funktionalen Programmierung geht es darum, ein Programm als rekursive Zusammensetzung von Funktionen zu betrachten, und die Reinheit ist orthogonal dazu. Purity hilft Ihnen (oder einem Computer), darüber nachzudenken, zum Nachweis der Korrektheit und zur Optimierung. Erstklassige Funktionen sind meiner Meinung nach notwendig, um nützliche Dinge zu tun oder zumindest schön zu machen, aber nicht ausreichend.

    – leewz

    17. Februar 2016 um 0:04 Uhr

  • Andy Tills Antwort zeigt eine sehr pragmatische Sicht der Dinge. In C “rein” zu werden, erfordert nichts Besonderes und bringt einen großen Vorteil. Erstklassige Funktionen, im Vergleich zum „Wohnkomfort“. Es ist bequem, aber die Übernahme eines reinen Programmierstils ist praktisch. Ich frage mich, warum niemand Tail Calls in Bezug auf all dies diskutiert. Wer erstklassige Funktionen will, sollte auch nach Tail Calls fragen.

    – BitTickler

    13. November 2018 um 12:50 Uhr


FFCALL ermöglicht das Erstellen von Closures in C — callback = alloc_callback(&function, data) gibt einen solchen Funktionszeiger zurück callback(arg1, ...) ist gleichbedeutend mit anrufen function(data, arg1, ...). Sie müssen die Garbage Collection jedoch manuell durchführen.

Dementsprechend Blöcke wurden zu Apples Fork von GCC hinzugefügt; Sie sind keine Funktionszeiger, aber sie lassen Sie Lambdas herumreichen, während Sie vermeiden, dass Speicherplatz für erfasste Variablen von Hand erstellt und freigegeben werden muss (effektiv findet ein gewisses Kopieren und Zählen von Referenzen statt, das hinter einigen syntaktischen Zucker- und Laufzeitbibliotheken verborgen ist).

Hartel & Mullers Buch, Funktionelles Cist heute (02.01.2012) zu finden unter: http://eprints.eemcs.utwente.nl/1077/ (Link zur PDF-Version).

Benutzeravatar von Viktor Shepel
Viktor Schäfer

Voraussetzung für funktionalen Programmierstil ist eine erstklassige Funktion. Es könnte in portablem C simuliert werden, wenn Sie als nächstes tolerieren:

  • manuelle Verwaltung von lexikalischen Bereichsbindungen, auch Closures genannt.
  • manuelle Verwaltung der Lebensdauer der Funktionsvariablen.
  • alternative Syntax der Funktion Anwendung/Aufruf.
/* 
 * with constraints desribed above we could have
 * good approximation of FP style in plain C
 */

int increment_int(int x) {
  return x + 1;
}

WRAP_PLAIN_FUNCTION_TO_FIRST_CLASS(increment, increment_int);

map(increment, list(number(0), number(1)); // --> list(1, 2)


/* composition of first class function is also possible */

function_t* computation = compose(
  increment,
  increment,
  increment
);

*(int*) call(computation, number(1)) == 4;

Die Laufzeit für solchen Code könnte so klein wie eine darunter sein

struct list_t {
  void* head;
  struct list_t* tail;
};

struct function_t {
   void* (*thunk)(list_t*);
   struct list_t* arguments;
}

void* apply(struct function_t* fn, struct list_t* arguments) {
  return fn->thunk(concat(fn->arguments, arguments));
}

/* expansion of WRAP_PLAIN_FUNCTION_TO_FIRST_CLASS */
void* increment_thunk(struct list_t* arguments) {
  int x_arg = *(int*) arguments->head;
  int value = increment_int(x_arg);
  int* number = malloc(sizeof *number);

  return number ? (*number = value, number) : NULL;
}

struct function_t* increment = &(struct function_t) {
  increment_thunk,
  NULL
};

/* call(increment, number(1)) expands to */
apply(increment, &(struct list_t) { number(1), NULL });

Im Wesentlichen imitieren wir eine erstklassige Funktion mit Closures, die als Paar Funktion/Argumente plus eine Reihe von Makrosen dargestellt werden. Vollständiger Code konnte gefunden werden hier.

Benutzeravatar von Jason Dagit
Jason Dagit

Da fällt mir vor allem der Einsatz von Codegeneratoren ein. Wären Sie bereit, in einer anderen Sprache zu programmieren, die die funktionale Programmierung bereitstellt, und daraus dann den C-Code zu generieren?

Wenn das keine attraktive Option ist, dann könnten Sie CPP missbrauchen, um einen Teil des Weges dorthin zu gelangen. Das Makrosystem sollte es Ihnen ermöglichen, einige funktionale Programmierideen zu emulieren. Ich habe gehört, dass gcc auf diese Weise implementiert ist, aber ich habe es nie überprüft.

C kann natürlich Funktionen mithilfe von Funktionszeigern herumreichen, die Hauptprobleme sind das Fehlen von Closures und das Typsystem neigt dazu, im Weg zu stehen. Sie könnten leistungsfähigere Makrosysteme als CPP wie M4 erkunden. Ich denke, was ich letztendlich vorschlage, ist, dass echtes C der Aufgabe nicht ohne großen Aufwand gewachsen ist, aber Sie könnten C erweitern, um es der Aufgabe gewachsen zu sein. Diese Erweiterung würde am ehesten wie C aussehen, wenn Sie CPP verwenden, oder Sie könnten ans andere Ende des Spektrums gehen und C-Code aus einer anderen Sprache generieren.

Benutzeravatar von Paul Nathan
Paul Nathan

Wenn Sie Closures implementieren möchten, müssen Sie sich mit Assemblersprache und Stack-Swapping/-Management vertraut machen. Nicht dagegen empfehlen, nur sagen, dass Sie das tun müssen.

Ich bin mir nicht sicher, wie Sie mit anonymen Funktionen in C umgehen werden. Auf einer von Neumann-Maschine könnten Sie jedoch anonyme Funktionen in asm ausführen.

1424830cookie-checkWelche Werkzeuge gibt es für die funktionale Programmierung in C?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy