Хочется странного
Jul. 20th, 2016 04:28 pmВнезапно захотелось странного: возможности рекурсивно вызвать вызвавшую функцию с заменой некоторых ее аргументов согласно определенному правилу. Например, у меня есть
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;
...
}Какой-нибудь язык сейчас так может?
no subject
Date: 2016-07-21 12:27 am (UTC)no subject
Date: 2016-07-21 12:37 am (UTC)no subject
Date: 2016-07-21 01:45 am (UTC)no subject
Date: 2016-07-21 03:50 am (UTC)no subject
Date: 2016-07-21 02:43 am (UTC)bool complexCondition(...); T3 complexCalculation(...); void foo(T1 arg1, T2 arg2, T3 arg3, T4 arg4) { ... if (complexCondition(params)) { foo(arg1, arg2, complexCalculation(...), arg4); return; } ... }Возможно, тут можно замутить какую-нибудь супер-пупер сверхмонаду на тригенных куаторах, но простой код будет потом проще отлаживать и проще поддерживать.
no subject
Date: 2016-07-21 03:54 am (UTC)Maybe T3 argToRecurse(...) { }
И внутри функций
Maybe T3 val = argToRecurse(params);
if (val != None) рекурсивный вызов
Вопрос чисто теоретический. Если есть https://en.wikipedia.org/wiki/J_operator#Generalized_first-class_return
то должна быть и генерализованная первоклассная рекурсия.
no subject
Date: 2016-07-21 03:51 am (UTC)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__ в строчном контексте.
no subject
Date: 2016-07-21 03:56 am (UTC)Вопрос чисто теоретический, по аналогии с https://en.wikipedia.org/wiki/J_operator#Generalized_first-class_return
no subject
Date: 2016-07-21 04:35 am (UTC)Во-вторых, если проблема с рекурсией, то поможет trampolining.
Скала, грубо говоря, все это предоставляет (в реале есть определенные сложности, но не сильно).
no subject
Date: 2016-07-21 04:45 am (UTC)no subject
Date: 2016-07-21 08:02 am (UTC)no subject
Date: 2016-07-21 01:34 pm (UTC)no subject
Date: 2016-07-21 11:51 am (UTC)http://jsbin.com/yimizuf/1/edit?js,console,output
no subject
Date: 2016-07-21 01:38 pm (UTC)И J operator в JS тоже делается?
no subject
Date: 2016-07-21 02:19 pm (UTC)Так как в JS нет поддержки абстрактных выражений как структур первого класса, в чистом виде этого, кажется, добиться нельзя (свободные переменные замыкаются в функцию при её объявлении). Я попробовал с такими мерзопакостными вещами, как with и new Function, но не вышло.
Однако нашлись какие-то papers от умных людей, сейчас почитаем.
no subject
Date: 2016-07-21 02:29 pm (UTC)Так что кажется, что всё-таки нельзя.