当我在 C 中使用 Y-Combinator 和 block 时,我在参数值中遇到了一个奇怪的事情

When I use Y-Combinator and block in C, I meet a strange thing in parameter value

当我尝试使用函数计算 sinh−1(x) 时:

double asinh_recursion(double  buf, double increment, double input_var, unsigned long item_count) {
    if (fabs(increment) < 1E-5) {
        return buf;
    }
    return asinh_recursion(buf + increment, increment * (-1) * (2 * item_count - 1) * (2 * item_count -1) / (2 * item_count + 1) / 2 / item_count * input_var, input_var, item_count + 1);
}
double asinh(double x) {
    if (!(fabs(x) < 1.0)) {
        printf("error asinh():wrong param x(fabs(x) > 1.0)");
        return -1.0;
    }
    return asinh_recursion(0.0, x, x * x, 1);
}

它似乎有效。 但是当我尝试使用 block 和 Y-Combinator 来做到这一点时:

typedef void * (^YCBlock)(void *);
YCBlock Y;
double asinh_with_block(double x) {
    if (!(fabs(x) < 1.0)) {
        printf("error asinh():wrong param x(fabs(x) > 1.0)");
        return -1.0;
    }


    Y= (YCBlock) ^ (YCBlock f) {
        return (YCBlock) ^ (YCBlock g) {
        return g(g);
        }(
        (YCBlock) ^ (YCBlock h) {
            return f(^ (void * x) { return ((YCBlock)h(h))(x); });
        }
        );
    };

    typedef double (^ RECUR_BLK_TYPE)(double, double, unsigned long);
    RECUR_BLK_TYPE recur_block = Y(^(RECUR_BLK_TYPE recur_block){
        return Block_copy(^ double (double buf, double increment, unsigned long item_count){
            if (item_count < 4) {
                printf("param:%lf,%lf,%lu\n", buf, increment, item_count);
            }

            if (fabs(increment) < 1E-5) {
                return buf;
            }
            buf = buf + increment;
            increment = increment * (-1) * (2 * item_count - 1) * (2 * item_count -1) / (2 * item_count + 1) / 2 / item_count * (x * x);
            ++item_count;
            if (item_count < 4) {
                printf("\tbuf:%lf\n", buf);
            }
            return recur_block(buf, increment, item_count);
            });
          });
    double ret = recur_block(0, x, 1);
    Block_release(recur_block);
    Block_release(Y);
    return ret;
}

但它在输出中运行异常(x=0.5):

param:0.000000,0.500000,1
    buf:0.500000
param:0.500000,-0.020833,2
    buf:0.479167
param:0.500000,0.002344,3
...
asinh_with_block(0.500000):0.500000

block里好像是这样的,有段时间我传buf=0.479167,下次打印的时候还是0.500000。 我想找出为什么它会这样工作,也许我在某个地方写了一些错误的代码...

问题在于,您的 Y 组合器只能与采用一个 void * 参数和 returns 一个 void * 的基础函数一起使用。您可以在以下行中看到:

return f(^ (void * x) { return ((YCBlock)h(h))(x); });

那里的块接受 x (一个参数)并将 x 作为一个参数传递给另一个东西。为了让它与多个参数的递归函数一起工作,这个函数必须接受这些多个参数并将它们全部传递(当然,类型也需要正确,因为不同的类型有不同的大小,以及用于传递和传递的 ABI返回不同类型的东西是不同的)。因此,每个函数签名都需要一个不同的 Y 组合器。

你有一个递归函数,它接受三个参数(两个 double 和一个 unsigned long)和 returns 一个 double。您可以(至少)通过更改 Y 组合器中的相关块并将其从错误类型强制转换为正确类型来使其工作:

return f(^ (double buf, double increment, unsigned long item_count) {
    return ((RECUR_BLK_TYPE)((YCBlock)h(h)))(buf, increment, item_count);
});

但是要真正在没有这种不安全转换的情况下以正确的类型安全使其干净,需要您仔细设置类型。像这样:

typedef double (^Func)(double, double, unsigned long);
typedef Func (^FuncFunc)(Func);
typedef Func (^RecursiveFunc)(void *);
typedef Func (^YCBlock)(FuncFunc);

Y = ^(FuncFunc f) {
    return ^(RecursiveFunc g) {
        return g(g);
    }(
        ^(void *temp) {
            RecursiveFunc h = temp; // trick to hide the recursive typing
            return f(^(double buf, double increment, unsigned long item_count) {
                return h(h)(buf, increment, item_count);
            });
        }
    );
};