?

Log in

No account? Create an account

Хочется странного - Общество дровосеков Бердичева по изучению Мишны

Jul. 20th, 2016

04:28 pm - Хочется странного

Previous Entry Share Next Entry



Внезапно захотелось странного: возможности рекурсивно вызвать вызвавшую функцию с заменой некоторых ее аргументов согласно определенному правилу. Например, у меня есть

void foo(T1 arg1, T2 arg2, T3 arg3, T4 arg4) {
    ...
    if (сложное условие) {
        T3 newArg3 = сложное вычисление;
        return foo(arg1, arg2, newArg3, arg4);
    }
    ...
}
и
T5 bar(T1A arg1, T2A arg2, T3 arg3) {
    ...
    if (то же самое сложное условие) {
        T3 newArg3 = то же самое сложное вычисление;
        return bar(arg1, arg2, newArg3);
    }
    ...
}

Как прикажете выфакторизовывать сложное условие и сложное вычисление? В идеале (псевдокод) было бы что-то вроде
Maybe T baz(данные для вычисления условия и newArg3) {
// "Maybe void" must be == bool.
    if (сложное условие) {
        return Just recurseParent(map (3 => сложное вычисление));
    }
    return None;
}

void foo(T1 arg1, T2 arg2, T3 arg3, T4 arg4) {
    ...
    if (baz(...)) {
        return;
    ...
}

T5 bar(T1A arg1, T2A arg2, T3 arg3) {
    ...
    Maybe T5 val = baz(...);
    if (val != None)
        return val;
    ...
}


Какой-нибудь язык сейчас так может?

Tags:

Comments:

[User Picture]
From:archaicos
Date:July 21st, 2016 12:27 am (UTC)
(Link)
longjmp/throw?
(Reply) (Thread)
[User Picture]
From:spamsink
Date:July 21st, 2016 12:37 am (UTC)
(Link)
Рекурсию они не дают.
(Reply) (Parent) (Thread)
[User Picture]
From:archaicos
Date:July 21st, 2016 01:45 am (UTC)
(Link)
Тогда я запутался. В первых примерах у тебя вроде и так всё хорошо (кроме расхода стека). То, что я предложил, даёт возможность оборвать исполнение вызванной ф-ции с возвратом, откуда можно сделать повторный вызов с другим аргументом (лишнего расхода стека нет). А чего ещё нужно?
(Reply) (Parent) (Thread)
[User Picture]
From:spamsink
Date:July 21st, 2016 03:50 am (UTC)
(Link)
Нужен оператор "first-class recursion", по аналогии с first-class return
(Reply) (Parent) (Thread)
[User Picture]
From:yatur
Date:July 21st, 2016 02:43 am (UTC)
(Link)
Сдается мне, что ничего проще выделения этих сложных условий с свои собственные функции не получится.

bool complexCondition(...);
T3 complexCalculation(...);

void foo(T1 arg1, T2 arg2, T3 arg3, T4 arg4) {
   ...
    if (complexCondition(params)) {
        foo(arg1, arg2, complexCalculation(...), arg4);
        return;
    }
    ...
}


Возможно, тут можно замутить какую-нибудь супер-пупер сверхмонаду на тригенных куаторах, но простой код будет потом проще отлаживать и проще поддерживать.
(Reply) (Thread)
[User Picture]
From:spamsink
Date:July 21st, 2016 03:54 am (UTC)
(Link)
Конечно, можно их даже объединить вместе в
Maybe T3 argToRecurse(...) { }
И внутри функций
Maybe T3 val = argToRecurse(params);
if (val != None) рекурсивный вызов

Вопрос чисто теоретический. Если есть https://en.wikipedia.org/wiki/J_operator#Generalized_first-class_return
то должна быть и генерализованная первоклассная рекурсия.
(Reply) (Parent) (Thread)
[User Picture]
From:ticklish_frog
Date:July 21st, 2016 03:51 am (UTC)
(Link)
std::optional baz(T1 arg1, T2 arg2) {
return (сложное условие) ? сложное вычисление : std::optional();
}

и потом

void foo(T1 arg1, T2 arg2, T3 arg3, T4 arg4) {
// ...
const auto& maybe = baz(arg1, arg2);
if (maybe) {
return foo(arg1, arg2, maybe.value(), arg4);
}
// ...
}

мне было бы так проще тестировать и поддерживать baz, чем если бы baz кого-то вызывал.

Можно еще поиграть с __func__ / __FUNCTION__.

#define MAYBE_RETURN_MAPPED_SELF(arg, ...) \
do { \
if (condition(arg, ##__VA_ARGS__)) \
return __func__ (mapping(arg), ##__VA_ARGS__) \
} while(false)

Я не знаю, правда, сработает ли это, может, надо обернуть в ##, всегра использовал __func__ в строчном контексте.

(Reply) (Thread)
[User Picture]
From:spamsink
Date:July 21st, 2016 03:56 am (UTC)
(Link)
Конечно, в настоящее время именно так и будет делаться, факторизовав вычисление условие и параметра, но не собственно рекурсию.
Вопрос чисто теоретический, по аналогии с https://en.wikipedia.org/wiki/J_operator#Generalized_first-class_return

(Reply) (Parent) (Thread)
[User Picture]
From:juan_gandhi
Date:July 21st, 2016 04:35 am (UTC)
(Link)
Во-первых, надо каррировать.
Во-вторых, если проблема с рекурсией, то поможет trampolining.
Скала, грубо говоря, все это предоставляет (в реале есть определенные сложности, но не сильно).
(Reply) (Thread)
[User Picture]
From:spamsink
Date:July 21st, 2016 04:45 am (UTC)
(Link)
Реализовать-то это можно по-всякому; интересно, как звать first-class recursion operator, которому не надо говорить, кого рекурсировать.
(Reply) (Parent) (Thread)
[User Picture]
From:juan_gandhi
Date:July 21st, 2016 08:02 am (UTC)
(Link)
Y
(Reply) (Parent) (Thread)
[User Picture]
From:spamsink
Date:July 21st, 2016 01:34 pm (UTC)
(Link)
Нет, он делает другое.
(Reply) (Parent) (Thread)
[User Picture]
From:_guard_
Date:July 21st, 2016 11:51 am (UTC)
(Link)
Ну, в JS есть динамический объект arguments, а также Function.caller

http://jsbin.com/yimizuf/1/edit?js,console,output
(Reply) (Thread)
[User Picture]
From:spamsink
Date:July 21st, 2016 01:38 pm (UTC)
(Link)
Cool! Спасибо, буду знать.
И J operator в JS тоже делается?
(Reply) (Parent) (Thread)
[User Picture]
From:_guard_
Date:July 21st, 2016 02:19 pm (UTC)
(Link)
Я слаб в высоких материях, пришлось почитать в википедии. Насколько я понимаю, речь идет о замыкании выражения с текущим контекстом и получении функции, которая, будучи вызванной, вернет результат вычисления выражения.

Так как в JS нет поддержки абстрактных выражений как структур первого класса, в чистом виде этого, кажется, добиться нельзя (свободные переменные замыкаются в функцию при её объявлении). Я попробовал с такими мерзопакостными вещами, как with и new Function, но не вышло.

Однако нашлись какие-то papers от умных людей, сейчас почитаем.
(Reply) (Parent) (Thread)
[User Picture]
From:_guard_
Date:July 21st, 2016 02:29 pm (UTC)
(Link)
В общем, то, что я нашел, полагается на некую фичу, реализованную только в Rhino, древней (по нынешним меркам) имплементации JS на Java.

Так что кажется, что всё-таки нельзя.
(Reply) (Parent) (Thread)