C中函数指针的复合谓词

Composite Predicate of function pointers in C

我正在尝试使用函数指针构建复合谓词。 不知道可不可以。

我的谓词是这样的形式:int (*predicate)(int) 那些工作正常。

我想创建一个函数,它接受 2 个 int (*predicate)(int) 类型的参数,return 是一个 int (*predicate)(int) 类型的函数,这样新函数 returns a(x) && b(x) 的值。

我想做这样的东西:

int ( *composite(int (*a)(int), int (*b)(int)) )(int x) {
    return a(x) && b(x);
}

或:

int ( *negate(int (*a)(int)) )(int x) {
    return !a(x);
}

我知道我的尝试是 return 一个值,而不是一个函数,但是如果我尝试为它们创建另一个函数 return,我最终会遇到完全相同的问题。

如果我这样做:

int composed(int (*a)(int), int (*b)(int), int x ) {
    return a(x) && b(x);
}

它可以编译,但它不再是 int (*predicate)(int) 类型,所以我无法按我想要的方式使用它。

我应该怎么做?

附上完整示例代码供参考:

#include <stdio.h>

unsigned int count(const int* xs, unsigned int len, int (*predicate)(int)) {
  int c = 0;
  for(int i = 0; i < len; i++) {
    if(predicate(xs[i])) c++;
  }
  return c;
}

int isEven(int x) { return x % 2 == 0; }
int isOdd(int x) { return !isEven(x); }
int isPos(int x) { return x > 0; }
int isNeg(int x) { return x < 0; }

// int composed(int (*a)(int), int (*b)(int), int x ) {
//     return a(x) && b(x);
// }
// 
// int ( *composite(int (*a)(int), int (*b)(int)) )(int x) {
//     return &composed(a,b)(x)
// }

int main() {
  int xs[] = {-5,-4,-3,-2,-1,0,1,2,3,4,5};
  const int len = 11;

  printf("Even: %d\n", count(xs, len, &isEven));
  printf(" Odd: %d\n", count(xs, len, &isOdd));
  printf(" Pos: %d\n", count(xs, len, &isPos));
  printf(" Neg: %d\n", count(xs, len, &isNeg));

  // int (*compositePtr)(int) = composite(&isNeg, &isOdd);
  // printf("Odd & Neg: %d", count(xs, len, compositePtr));

}

我曾经为了演示目的写过如下代码:

#include <stdio.h>

/* one int for the implicit parameter n,
 * one int as return type,
 * one int as parameter.
 */
typedef struct int_int_int_closure {
    int (*call)(const struct int_int_int_closure *, int);
    int n;
} int_int_int_closure;

static int
adderfn(const int_int_int_closure *cl, int n) {
    return cl->n + n;
}

int
main(void)
{
    int_int_int_closure a3;

    a3.call = adderfn;
    a3.n = 3;

    printf("%d\n", a3.call(&a3, 2));
    return 0;
}

您可以概括此代码,使闭包由函数指针、指向其数据的指针和数据大小组成。

您不能在 C 中动态创建闭包或函数,因此您需要做一些其他事情。最简单的方法是使用额外的间接级别。如果您将谓词更改为

int (**predicate)(void *, int)

你称之为 (**predicate)(predicate, x),那么你可以很容易地创建新的动态谓词:

typedef int (*predicate_t)(void *, int);

struct compose_fn_data {
    predicate_t  fn, *a, *b;
};

int and_fn(void *data_, int x) {
    struct compose_fn_data *data = data_;
    return (**data->a)(data->a, x) && (**data->b)(data->b, x);
}

int or_fn(void *data_, int x) {
    struct compose_fn_data *data = data_;
    return (**data->a)(data->a, x) || (**data->b)(data->b, x);
}

predicate_t *compose(predicate_t fn, predicate_t *a, predicate_t *b) {
    struct compose_fn_data *rv = malloc(sizeof *rv);
    rv->fn = fn;
    rv->a = a;
    rv->b = b;
    return &rv->fn;
}

predicate_t *and(predicate_t *a, predicate_t *b) { return compose(and_fn, a, b); }
predicate_t *or(predicate_t *a, predicate_t *b) { return compose(or_fn, a, b); }

关于这个的小烦恼是对于简单的函数,您需要定义一个额外的单字数据结构来保存函数指针,只是为了间接通过它。您通常使用函数定义旁边的全局变量来执行此操作:

int isEven_fn(void *data, int x) { (void)data; return x % 2 == 0; }
predicate_t isEven = isEven_fn;
int isOdd_fn(void *data, int x) { return !isEven_fn(data, x); }
predicate_t isOdd = isOdd_fn;
int isPos_fn(void *data, int x) { (void)data; return x > 0; }
predicate_t isPos = isPos_fn;
int isNeg_fn(void *data, int x) { (void)data; return x < 0; }
predicate_t isNeg = isNeg_fn;

现在您可以执行以下操作:

printf("Even: %d\n", count(xs, len, &isEven));
printf("Odd & Neg: %d", count(xs, len, and(&isNeg, &isOdd)));
printf("%d", count(xs, len, or(and(&isNeg, &isOdd), &isEven)));

尽管后者确实会泄漏内存。

当发明传递函数指针to/from函数的语法时,我怀疑抽了很多没说的东西。

给定函数指针 int (*)(int),函数 returning 这样的函数指针声明为:

int (*func(void))(int);
1          2     3

其中1=return函数指针的类型,2是函数的参数列表,3=函数指针的参数列表。

现在如果你想创建一个函数 returning 一个这样的指针,并将两个这样的指针作为参数,它是 trivial:

int (*func(int(*a)(int), int(*b)(int)))(int)

不,这不是微不足道的……这太疯狂了!如果您真的可以编写和理解这样的代码,那么这意味着您只是为了自己的利益而使用 C 太多了。

你绝对应该做的是使用 typedef.

typedef int (predicate_t)(int);
...

predicate_t* func (predicate_t* a, predicate_t* b);

但是,我怀疑您实际上只需要一个 return 是整数而不是函数指针的函数。

这是您创建谓词函数组合的 block-based 实现:

//
//  ViewController.m
//  PredicateFunctionsExercise
//
//  Created by James Alan Bush on 3/12/22.
//

#import "ViewController.h"
@import simd;

typedef int (^boolean_expression)(void);
typedef int (^guarded_boolean_expression)(void);
typedef int (^ const (*guarded_boolean_expression_t))(void);
typedef int (^(^conditional_boolean_expression)(guarded_boolean_expression_t))(void);
typedef int (^ const (^ const (*conditional_boolean_expression_t))(guarded_boolean_expression_t))(void);
typedef int (^predicate)(void);
typedef int (^ const (*predicate_t))(void);

static int c = 0;
static int flag = (1 << 0);
guarded_boolean_expression flag_conditional = ^ int { return flag; };


conditional_boolean_expression isEven = ^ (guarded_boolean_expression_t _Nullable x) { return ^ int { int result = (*x)() % 2 == 0; printf("%d %s (%d)\t", flag, (result) ? "is even"     : "is not even",     result); return result; }; };
conditional_boolean_expression isOdd  = ^ (guarded_boolean_expression_t _Nullable x) { return ^ int { int result = (*x)() % 2 == 1; printf("%d %s (%d)\t", flag, (result) ? "is odd"      : "is not odd",      result); return result; }; };
conditional_boolean_expression isPos  = ^ (guarded_boolean_expression_t _Nullable x) { return ^ int { int result = (*x)() > 0;      printf("%d %s (%d)\t", flag, (result) ? "is positive" : "is not positive", result); return result; }; };
conditional_boolean_expression isNeg  = ^ (guarded_boolean_expression_t _Nullable x) { return ^ int { int result = (*x)() < 0;      printf("%d %s (%d)\t", flag, (result) ? "is negative" : "is not negative", result); return result; }; };

static void (^evaluate_predicate)(predicate_t) = ^ (predicate_t p) {
    printf("\nresult = %d (%d invocations)\n", (*p)(), c);
};

static int (^(^(^g)(__strong conditional_boolean_expression, __strong conditional_boolean_expression, guarded_boolean_expression_t))(int (^__strong)(conditional_boolean_expression_t, conditional_boolean_expression_t, guarded_boolean_expression_t)))(void) =
^ (conditional_boolean_expression boolean_conditional_a, conditional_boolean_expression boolean_conditional_b, guarded_boolean_expression_t guarded_conditional) {
    return ^ (int (^bitwise_operation)(conditional_boolean_expression_t, conditional_boolean_expression_t, guarded_boolean_expression_t)) {
        return ^ (int boolean_expression) {
            return ^ int {
                ++c;
                return boolean_expression;
            };
        }(bitwise_operation((conditional_boolean_expression_t)&boolean_conditional_a, (conditional_boolean_expression_t)&boolean_conditional_b, guarded_conditional));
    };
};


@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // gbe_a is set to a precalculated and stored result
    // Invoking gbe_a() merely returns the results without recalculating it -- perfect for use inside the domain of discourse
    guarded_boolean_expression gbe_a = g(isEven, isOdd, &flag_conditional)(^ int (conditional_boolean_expression_t boolean_conditional_a, conditional_boolean_expression_t boolean_conditional_b, guarded_boolean_expression_t guarded_conditional) {
        return ((*boolean_conditional_a)(guarded_conditional)() & (*guarded_conditional)()) && (*boolean_conditional_b)(guarded_conditional);
    });
    
    guarded_boolean_expression gbe_b = g(isPos, isNeg, &flag_conditional)(^ int (conditional_boolean_expression_t boolean_conditional_a, conditional_boolean_expression_t boolean_conditional_b, guarded_boolean_expression_t guarded_conditional) {
        return ((*boolean_conditional_a)(guarded_conditional)() & (*guarded_conditional)()) && (*boolean_conditional_b)(guarded_conditional);
    });
    
    // Returning a variety of bitwise operations with randomly chosen operands and operators
    // to demonstrate that simd vectors can be used combining multiple bitwise operations into a single operation
    simd_uint2 bit_vector = simd_make_uint2((gbe_a() << 0), (gbe_b() << 0));
    simd_uint2 bit_mask   = simd_make_uint2(flag, flag);
    simd_uint2 results    = simd_make_uint2(bit_vector & bit_mask);
    printf("\n%d & %d == %d\t\t\t", bit_vector[0], bit_mask[0], results[0]);
    printf("%d & %d == %d\n", bit_vector[1], bit_mask[1], results[1]);
    results    = simd_make_uint2(bit_vector | bit_mask);
    printf("%d | %d == %d\t\t\t", bit_vector[0], bit_mask[0], results[0]);
    printf("%d | %d == %d\n", bit_vector[1], bit_mask[1], results[1]);
    results    = simd_make_uint2(bit_vector ^ bit_mask);
    printf("%d ^ %d == %d\t\t\t", bit_vector[0], bit_mask[0], results[0]);
    printf("%d ^ %d == %d\n", bit_vector[1], bit_mask[1], results[1]);
}


@end

控制台输出: