2017-03-27 2 views
1

J'ai récemment trébuché sur cette pensée curieuse tout en manipulant un code C.
J'ai écrit une fonction qui renvoie un double et prend en comme argument le pointeur sur une fonction et un certain nombre de paramètres, à savoirEst-il possible d'écrire une fonction qui retourne un pointeur vers une fonction différente de la fonction dans son argument?

double template_1(double (*)(double),...); 

cette fonction identifie correctement une propriété d'une fonction réelle

double f(double); 

représenté comme un pointeur dans template_1, afin de rendre template_1 valide pour chaque fonction réelle que je pourrais brancher.

Maintenant, je devais écrire une autre fonction, que ce soit:

double derivative(double (*)(double),double); 

double derivative(double (*f)(double),double x){ 

    double epsilon = ...; 

    return (f(x+epsilon)-f(x-epsilon))/(2.0*epsilon); 
} 

à nouveau avec f dans l'argument pour le faire fonctionner pour chaque f. Ma question est: puisque je voudrais utiliser derivative dans template_1 sans le modifier, est-il possible d'écrire une fonction qui prend dérivé et recrache quelque chose qui a la forme de double (*)(double)?

Mon idée était de définir typedef double (*real_function)(double); puis de définir

real_function g(double (*derivative)(double (*)(double),double)) 

que je voudrais à cracher quelque chose comme: double derivative_2(double x); de sorte que je pourrais définir quelque chose comme g(derivative) = double (*h)(double); directement dans template_1 argument malheureusement, je n'ai pas la moindre idée de la façon de faire ce travail, ou même si cela peut fonctionner.

+0

Cela ne serait possible que dans le cas où cette fonction retournée est déjà définie. Non, vous ne pouvez pas "définir" des fonctions dans le runtime. Vous devrez passer à un langage fonctionnel tel que Haskell ou Lisp pour cela. –

+0

Il n'y a pas de moyen (portable) de créer une fonction à l'exécution en C. –

+0

@EugeneSh. la plupart des langues, et pas seulement les langues fonctionnelles, le supportent (au moins sous une forme quelconque) - par exemple C++, java, go. –

Répondre

1

Attention: Je suis un développeur C++ avec peu de connaissances de C pour tout ce qui suit est probablement unidiomatic C.

Comme KerrekSB said, vous devez effectuer un état avec votre fonction. Ce n'est pas possible avec les fonctions brutes mais vous pouvez définir une structure qui porte l'état et ajouter une fonction qui fonctionne avec cette structure.Ceci a évidemment l'inconvénient de perdre la belle syntaxe d'appel de fonction. Je fouetté un exemple:

#include <math.h> 
#include <stdio.h> 
#include <stdlib.h> 

typedef double (*raw_fptr)(double); 
struct real_function; 
typedef double (*evaluate_function)(struct real_function*, double); 

struct real_function { 
     evaluate_function evaluate; 
}; 
typedef struct real_function real_function; 

double evaluate(real_function *f, double x) { 
     if(f) { 
       return f->evaluate(f, x); 
     } 
     return NAN; 
} 

struct raw_real_function { 
     real_function real_function_base; 
     raw_fptr raw_function; 
}; 
typedef struct raw_real_function raw_real_function; 

double evaluate_raw_real_function(real_function *f_base, double x) { 
     if(f_base) { 
       raw_real_function *f = (raw_real_function*)f_base; 
       return f->raw_function(x); 
     } 
     return NAN; 
} 

raw_real_function make_raw_real_function(raw_fptr function) { 
     raw_real_function result; 
     result.raw_function = function; 
     result.real_function_base.evaluate = evaluate_raw_real_function; 
     return result; 
} 

struct derive_real_function { 
     real_function real_function_base; 
     real_function *function_to_derive; 
}; 
typedef struct derive_real_function derive_real_function; 

double derive(real_function *f_base, double x) { 
     derive_real_function *f = (derive_real_function*)f_base; 
     double epsilon = 1e-3; 
     double upper = evaluate(f->function_to_derive, x+epsilon); 
     double lower = evaluate(f->function_to_derive, x-epsilon); 
     double result = (upper - lower)/(2.0*epsilon); 
     return result; 
} 

derive_real_function make_derivative(real_function * function_to_derive) { 
     derive_real_function result; 
     result.real_function_base.evaluate = derive; 
     result.function_to_derive = function_to_derive; 
     return result; 
} 

double x_cubed(double x) { 
     return x * x * x; 
} 

int main(int argc, char **argv) { 
     raw_real_function x_cubed_wrapped = make_raw_real_function(x_cubed); 
     derive_real_function derived = make_derivative(&x_cubed_wrapped.real_function_base); 
     derive_real_function derived_twice = make_derivative(&derived.real_function_base); 
     double x = atof(argv[1]); 
     double derivative = evaluate(&derived.real_function_base, x); 
     double second_derivative = evaluate(&derived_twice.real_function_base, x); 
     printf("derivative of x^3 at %f = %f\n", x, derivative); 
     printf("second derivative of x^3 at %f = %f\n", x, second_derivative); 
     return 0; 
} 

Voir (une légère variaton, en raison des limitations d'entrée) en cours d'exécution here.

Comment ça marche? J'ai truqué un peu d'héritage avec les structures real_function, raw_real_function et derive_real_function pour générer des appels de fonction virtuelle. Le struct real_function sert de conteneur d'une table de fonctions virtuelle composée uniquement de l'entrée evaluate. Ce pointeur de fonction aux "dérivés" struct fonction evaluate pertinente:.

raw_real_function instances indiquent evaluate_raw_real_function (comme initialisé dans make_raw_real_functionderive_real_function instances pointent evaluate à derive (comme initialisé dans make_derivative)

Lorsque vous appelez evaluate. Sur le membre real_function_base, il appellera la fonction d'évaluation associée, qui envoie le real_function* à son pointeur de structure associé et fait ce qui est nécessaire avec cette information.Puisque tout est juste un real_function*, on peut les enchaîner à volonté mais il faut convertir les fonctions "normales" au format real_function, c'est ce que fait make_raw_real_function.

+0

Il m'a fallu du temps pour comprendre la réponse, merci beaucoup. Je suppose que la leçon principale ici est que je vais en apprendre plus sur C++ – Fra

2

Il existe plusieurs façons de faire des fonctions anonymes en C. Comme les commentaires l'ont indiqué, ils ne sont pas portables. Mais selon le cas d'utilisation, vous pouvez trouver ce utile: Anonymous functions using GCC statement expressions

Un couple de personnes ont semblé avoir des problèmes similaires, ne sais pas comment ils sont portables, mais ils peuvent être débrouillards: https://github.com/graphitemaster/lambdapp https://github.com/Leushenko/C99-Lambda

Fondamentalement, S'il existe un moyen d'architecturer votre programme d'une manière qui ne nécessite pas de fonctions anonymes, faites-le de cette façon. Si vous n'avez pas d'autre choix, je donnerais un coup de feu à l'un d'entre eux.

+0

Je vais regarder dedans merci! – Fra

1

Si vous avez une fonction my_fancy_function:

double my_fancy_function (double x) { return sin(x) + cos(x); } 

Ensuite, vous pouvez utiliser une macro d'aide qui crée la fonction dérivée pour vous. Vous transmettez ensuite cette fonction nouvellement définie à votre modèle.

template_1(derivative_of_my_fancy_function, x, y, z); 
+0

c'est une solution géniale, mais pas exactement ce que je cherchais. – Fra