Когда я использую Y-Combinator и блокирую в C, я встречаю странную вещь в значении параметра

Когда я пытаюсь вычислить 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);
}

вроде работает. но когда я пытаюсь использовать блок и 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. Я хочу выяснить, почему это работает так, может быть, я где-то написал неправильный код...


person slowcoach    schedule 02.06.2015    source источник
comment
Вау, это довольно сложная штука. Я уверен, что это можно упростить.   -  person Droppy    schedule 02.06.2015
comment
Да, я думаю, вы правы, вы имеете в виду, что это как функция в начале или статический блок?   -  person slowcoach    schedule 02.06.2015
comment
Ну сделайте что-нибудь, чтобы было легче понять.   -  person Droppy    schedule 02.06.2015
comment
Спасибо за ваше предложение, и я думаю, что оно правильное, но это просто попытка сделать рекурсию с блоком... Это просто попытка, и когда я встречаю странную вещь, я хочу узнать, что произошло...   -  person slowcoach    schedule 02.06.2015


Ответы (1)


Проблема в том, что ваш комбинатор Y создан для работы только с базовой функцией, которая принимает один параметр void * и возвращает void *. Вы можете увидеть это в строке:

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

Блок, который принимает x (один аргумент) и передает x другому объекту в качестве одного аргумента. Чтобы работать с рекурсивной функцией с несколькими аргументами, эта функция должна принимать эти несколько аргументов и передавать их все (конечно, все типы тоже должны быть правильными, потому что разные типы имеют разные размеры, а ABI для передачи и передачи). возврат вещей разных типов отличается). Таким образом, вам понадобится другой комбинатор Y для каждой сигнатуры функции.

У вас есть рекурсивная функция, которая принимает три параметра (два double и unsigned long) и возвращает 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);
            });
        }
    );
};
person newacct    schedule 03.06.2015
comment
Спасибо! Это работает, и спасибо за подробное объяснение! Тогда, если я хочу использовать Y-Combinator в Obj-C, должен ли я также настроить тип блока? Я имею в виду, что если я использую id вместо void * и рекурсивный блок имеет несколько аргументов, будет ли он работать нормально или как мой исходный код? - person slowcoach; 04.06.2015
comment
Когда я пытаюсь сделать то, что я сказал в Obj-C, происходит EXC_BAD_ACCESS... Может быть, мне всегда следует настраивать тип блока, когда я делаю Y-Combinator... - person slowcoach; 04.06.2015