2

当我尝试使用函数计算 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

似乎在块中,有时,当我通过 buf=0.479167 时,下次打印它时,它仍然是 0.500000。我想找出它为什么会这样工作,也许我在某个地方写了一些错误的代码......

4

1 回答 1

0

问题是您的 Y 组合器仅适用于采用一个void *参数并返回一个void *. 您可以在该行中看到:

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

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

您有一个递归函数,它接受三个参数(两个doubles 和 an unsigned long)并返回 a 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);
            });
        }
    );
};
于 2015-06-03T22:40:01.660 回答