(我知道C不是以功能性的方式使用。但是,在学习函数式编程之后,我的思维方式有所不同。)
考虑到这些限制:
我们能想到一种提升函数f以获取额外参数以适合原型的方法,例如当执行f时这个新参数会被忽略吗?
以下是我想要做的事情:
我想要拟合一个函数f,其类型为:
f :: void f(char *s)
在函数g中,它将函数作为参数(称之为arg),其类型为:
arg :: void f(unsigned int i, char *s)
因此,g的类型是:
g :: void g(void (*f) (unsigned int, char))
haskell中的解决方案如下:
g (const f)
这是否可能,也许是某种宏观魔法?
编辑:为了更好地理解,这是实际的代码。需要完成ft_striter的主体。目的是:将函数f应用于字符串的每个字符,使用或不使用其索引i。
void ft_striter(char *s, void (*f) (char *s))
{
ft_striteri(?);
}
static void ft_striteri_helper(char *s, unsigned int i, void (*f)(unsigned int, char*))
{
if (*s)
{
f(i, s);
ft_striteri_helper(s + 1, i + 1, f);
}
}
void ft_striteri(char *s, void (*f)(unsigned int, char*))
{
ft_striteri_helper(s, 0, f);
}
答案 0 :(得分:4)
您无法在标准便携式C中实现此功能。问题是函数g
。设计错了。它应该有类型
void g(void (*f) (void *, unsigned int, char), void *ctxt)
它应该将ctxt
参数作为它所做的任何f
调用的第一个参数传递。
现在,您可以使用
等代码实现所需的功能struct const_ctxt {
void (*fun)(char *);
}
void const(void *ctxt, unsigned int i, char *s)
{
((struct const_ctxt *)ctxt)->fun(s);
}
void call_g_using_const_f(void (*f)(char *))
{
struct const_ctxt *ctxt = malloc(sizeof (struct const_ctxt));
ctxt->fun = f;
g(const, (void *)ctxt);
free(ctxt);
}
警告:如果g
的参数可能会超出g
的动态范围,那么您需要找到另一种策略来管理ctxt
的分配。
g
采用一对代码指针和数据“context”/“environment”指针的模式是高级语言通常实现闭包的方式。
答案 1 :(得分:2)
由于您只想接受并忽略额外参数,因此可以通过创建具有所需签名的包装函数来完成此操作,该函数只需委托给原始函数:
void f2(unsigned int i, char *s) {
f(s);
}
在范围内正确声明该功能后,您只需调用
即可g(f2);
如果您愿意,可以将函数f2()
声明为静态,以便在定义它的文件之外的代码中看不到它。它不需要嵌套。
答案 2 :(得分:-1)
我们考虑一些选择。
首先,C没有lambda表达式,尽管最新的C ++标准确实如此。如果这对您来说是一个主要问题,那么考虑C ++而不是C可能是值得的。
C可以定义并传递function pointers。所以 将函数作为参数传递给另一个函数是完全合法的。
您还可以定义functions to take a variable number of arguments,这对您也很有用。
C宏也很有用。例如,你可以这样做:
#define f1( x ) g( (x), 1 )
#define f2( x ) g( (x), 2 )
int g( int x, int y )
{
return ( x + y ) ;
}
你甚至可以用宏来做聪明的事情以允许macro overloading。这在编码风格方面并不完美,但它是可能的。
因此,可能会有一些工具可以满足您的需求。
OP在我编造我的帖子时添加了他的帖子的更新,所以如果我理解他的目标是一个粗略的建议。
看起来OP希望所选择的函数能够访问可以在别处设置的索引变量,他可以尝试使用struct作为参数。也许是这样的事情:
typedef struct mystring_s {
char *s ;
int i ;
} mystring_t ;
void f1( mystring_t *strp )
{
/* can access strp->s and strp->i */
}
/* and similar for other functions f2, f2, etc. */
/* And to use this you can call any function using
*/
void g( void (*fn)( mystring_t * ), mystring_t *strp )
{
(*fn)( strp ) ;
}
void set_i( mystring_t *strp, int v )
{
strp->i = v ;
}
/* for example */
mystring_t s ;
/* set up s */
set_i( &s, 11 ) ;
g( &f1, &s ) ;
set_i( &s, 31 ) ;
g( &f2, &s ) ;
当然他也可以尝试使用全局值(他显然不想或不认为是不可能的),他甚至可以存储带有数据的函数指针。
很难确定这是否是OP的一个可行的想法,因为他真的想做一些C不适合的事情。我认为问题在于他头脑中有一个设计理念,对于C来说这是一个糟糕的选择。看看这些要求是如何产生的,并将整体设计修改为更友好的设计可能会更好,而不是尝试并让C做一些事情来取代Haskell的能力。