如何提升函数以在 C 中获取额外参数?
How to lift a function to take an extra parameter in C?
(我明白C并不是要以函数式的方式使用的。但是,学习了函数式编程后,我很难有不同的想法。)
鉴于这些限制:
- 没有嵌套函数,因为 clang 禁止它(所以没有 lamda 表达式)
- 没有全局变量
我们能不能想办法提升一个函数 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);
}
由于您只想接受并忽略额外的参数,您可以通过创建一个具有所需签名的包装函数来完成此操作,该函数只是委托给原始函数:
void f2(unsigned int i, char *s) {
f(s);
}
在范围内正确声明该函数后,您可以简单地调用
g(f2);
函数 f2()
如果您愿意,可以将其声明为静态的,这样它对于定义它的文件之外的代码是不可见的。不需要嵌套。
好吧,让我们考虑几个选项。
首先,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 在我编写我的内容时向他的 post 添加了更新,所以如果我理解他的目标,这里是一个粗略的建议。
OP 似乎希望所选函数能够访问可以在其他地方设置的索引变量,他可以尝试使用结构作为参数。也许是这样的:
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 更友好的东西,而不是尝试并让 C 做一些事情来取代 Haskell 的能力。
您不能在标准的可移植 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" 指针的模式是高级语言通常如何实现闭包。
(我明白C并不是要以函数式的方式使用的。但是,学习了函数式编程后,我很难有不同的想法。)
鉴于这些限制:
- 没有嵌套函数,因为 clang 禁止它(所以没有 lamda 表达式)
- 没有全局变量
我们能不能想办法提升一个函数 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);
}
由于您只想接受并忽略额外的参数,您可以通过创建一个具有所需签名的包装函数来完成此操作,该函数只是委托给原始函数:
void f2(unsigned int i, char *s) {
f(s);
}
在范围内正确声明该函数后,您可以简单地调用
g(f2);
函数 f2()
如果您愿意,可以将其声明为静态的,这样它对于定义它的文件之外的代码是不可见的。不需要嵌套。
好吧,让我们考虑几个选项。
首先,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 在我编写我的内容时向他的 post 添加了更新,所以如果我理解他的目标,这里是一个粗略的建议。
OP 似乎希望所选函数能够访问可以在其他地方设置的索引变量,他可以尝试使用结构作为参数。也许是这样的:
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 更友好的东西,而不是尝试并让 C 做一些事情来取代 Haskell 的能力。
您不能在标准的可移植 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" 指针的模式是高级语言通常如何实现闭包。