When I try to caculate sinh−1(x) using functions:
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);
}
it seem works. but when I try to use block and Y-Combinator to do it:
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;
}
but it works strangely in the output(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
it seem like that in the block, at some time,when I pass buf=0.479167, next time when I print it, it is still 0.500000. I wanna to find why it works like this, maybe I wrote some wrong code at somewhere...
The problem is that your Y combinator is only made to work with an underlying function that takes one void *
parameter and returns a void *
. You can see that in the line:
return f(^ (void * x) { return ((YCBlock)h(h))(x); });
The block in there that takes x
(one argument) and passed the x
to another thing as one argument. For it to work with a recursive function of multiple arguments, this function must take those multiple arguments and pass them all on (of course, the types all need to be right too, because different types have different sizes, and the ABI for passing and returning things of different types is different). So you will need a different Y combinator for each function signature.
You have a recursive function that takes three parameters (two double
s and an unsigned long
) and returns a double
. You can (minimally) make it work by changing the relevant block in the Y combinator and coercing it from the wrong type to the right type:
return f(^ (double buf, double increment, unsigned long item_count) {
return ((RECUR_BLK_TYPE)((YCBlock)h(h)))(buf, increment, item_count);
});
But to really make it clean with correct type safety without this unsafe casting would require you to carefully set up the types. Something like this:
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);
});
}
);
};