如何提升函数以在C中获取额外参数?

时间:2015-12-07 16:44:00

标签: c haskell functional-programming clang

(我知道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);
}

3 个答案:

答案 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的能力。