2016-11-28 4 views
1

Использование функции от Generate all sequences of bits within Hamming distance t:Бросить рекурсивную функцию, когда динамическое условие выполняется

void magic(char* str, int i, int changesLeft) { 
     if (changesLeft == 0) { 
       printf("%s\n", str); 
       return; 
     } 
     if (i < 0) return; 
     // flip current bit 
     str[i] = str[i] == '0' ? '1' : '0'; 
     magic(str, i-1, changesLeft-1); 
     // or don't flip it (flip it again to undo) 
     str[i] = str[i] == '0' ? '1' : '0'; 
     magic(str, i-1, changesLeft); 
} 

Я хотел бы выйти из рекурсивной функции и вернуться к функции вызывающего абонента, когда возникает определенное условие (если он делает). Так что, как и моя рекурсивная функция, слышу голоса, которые могут сказать ей уйти!

Это происходит только после того, как str печатается здесь:

if (changesLeft == 0) { 
    printf("%s\n", str); 
    int quit_now = voices(str); 
    return; 
} 

Как это сделать (остановить разворачивание рекурсию и вернуться к функции вызывающей)?


Попытка:

if (i < 0 || quit_now == 1) return; 

только кажется, чтобы блокировать выполнение и никогда не закончится!

PS - Меня интересует даже в старые методологии.

+0

Вы попробовали goto: вместо возврата? – hbagdi

+0

@hbagdi черная овца? : O Я никогда не касался его, но если вы опубликуете ответ, который докажет, что он может пригодиться, я бы хотел ... Мне уже 21 год! =) // не получилось, что это троллинг:/ – gsamaras

+1

Не надо. Они троллируют вас. – paddy

ответ

1

Выражаясь в простейшей форме, вы могли бы сделать что-то вроде этого:

void foo(bool & ret) { 
    // doStuff... 
    if (ret) return; 
    foo(ret); 
    // doStuff... 
    if (ret) return; 
    foo(ret); 
} 

Затем вы начинаете рекурсию:

bool ret = false; 
foo(ret); 

В вашем случае вы можете ПРЕРЫВАЙТЕ рекурсию на

if (!changesLeft) { 
    printf("%s\n", str); 
    ret = true; 
    return; 
} 

Установка в true приведет к выходу из всего дерева вызовов.

Вы также можете сделать это на C, просто используйте указатель, а не ссылку.

+1

Это на самом деле похоже на то, как я делаю «обработку исключений» на C, - это тяжело, но если вы хотите, чтобы чистый и правильный раскручивание стека, это путь. longjmp прогонит все, что должно произойти вдоль стековых кадров, и у вас будет плохое время. Это приемлемо только в том случае, если вам не с чем справиться - никаких скидок, деинициализации и т. Д. – dtech

+0

Остерегайтесь проблем с упорядочением памяти и условий гонки, получающих истинное значение 'ret' между потоками, и правильное решение может включать использование атома. – paddy

+0

@paddy это действительно не похоже на многопоточный сценарий. И защита состояния гонки в многопоточных сценариях само собой разумеется. – dtech

3

Простое решение, учитывая, что ваша функция в настоящее время не имеет возвращаемого значения, заключается в использовании его для указания того, было ли выполнено это условие завершения. Затем вы можете использовать его для немедленного выхода из всех рекурсивных вызовов, если результат станет истинным.

Не уверен, что если я захватывая планируемую логику правильно здесь, но интуитивный подход будет что-то вроде этого:

int magic(char* str, int i, int changesLeft) { 
    int result; 
    if (changesLeft == 0) { 
      printf("%s\n", str); 
      return voices(str); 
    } 
    if (i < 0) return 0; 

    // flip current bit 
    str[i] = str[i] == '0' ? '1' : '0'; 
    result = magic(str, i-1, changesLeft-1); 

    if(!result) { 
     // or don't flip it (flip it again to undo) 
     str[i] = str[i] == '0' ? '1' : '0'; 
     result = magic(str, i-1, changesLeft); 
    } 

    return result; 
} 
+0

И 'result' должен получить свое значение ** right ** после' printf ("% s \ n", str); ', как я бы хотел, правильно? – gsamaras

+0

Да, в каждом вызывающем, потому что вы возвращаете 1 после этого 'printf'. И каждый вызывающий элемент в стеке перестает делать рекурсивные вызовы, когда получает ненулевой результат. Все они возвращают этот результат, чтобы пройти весь путь назад. Еще один бонус заключается в том, что первый вызывающий абонент узнает, выполнила ли операция его условие завершения или нет. И поэтому самой функции не нужно иметь 'printf'. Вы можете сказать: 'if (magic (str, 0, x)) printf (" OK:% s \ n ", str); else printf («Нет ответа \ n»); ' – paddy

+0

О, извините, я только что заметил, что я не обрабатывал вещи« голосов ». Копирование/вставка и ленивое чтение .... Я обновил. – paddy

1

magic функция вызывает себя рекурсивно в двух местах. Поэтому в каждом из этих мест вам нужно проверить свое условие выхода. Ответ, который дает падди, подробно описывает это.

Альтернативой немедленного разматывания стека является использование setjmp и longjmp, который может функционировать как нелокальный goto.

jmp_buf magic_buf; 

void magic_int(char* str, int i, int changesLeft) { 
     if (changesLeft == 0) { 
       printf("%s\n", str); 
       if (voices(str)) { 
        longjmp(magic_buf, 1); 
       } 
       return; 
     } 
     if (i < 0) return; 
     // flip current bit 
     str[i] = str[i] == '0' ? '1' : '0'; 
     magic_int(str, i-1, changesLeft-1); 
     // or don't flip it (flip it again to undo) 
     str[i] = str[i] == '0' ? '1' : '0'; 
     magic_int(str, i-1, changesLeft); 
} 

void magic(char* str, int i, int changesLeft) { 
    if (!setjmp(magic_buf)) { 
     magic(str, i, changesLeft); 
    } 
} 

setjmp функция возвращает 0, когда вызывается непосредственно. Когда вызывается longjmp, это фактически возвращается функция setjmp, а возвращаемое значение является вторым параметром, заданным longjmp.

Здесь у нас есть функция обертки, которая вызывает setjmp. Это устанавливает точку перехода, когда вызывается longjmp. Тогда вызывается рекурсивная функция. Позже, когда рекурсивная функция «слышит голоса», говоря ей, чтобы выйти сейчас, она вызывает longjmp, которая сразу же переходит непосредственно к соответствующему вызову setjmp.

Эти функции указаны на C99, а также в POSIX, поэтому система, соответствующая POSIX (то есть Linux), должна по-прежнему иметь доступную в режиме C89.

Если вы должны были сделать это на C++, предпочтительным методом было бы выбросить исключение в рекурсивную функцию и уловить его в функции обертки.

struct magic_voices { 
    int rval; 
}; 

void magic_int(char* str, int i, int changesLeft) { 
     if (changesLeft == 0) { 
       printf("%s\n", str); 
       int rval = voices(str); 
       if (rval) { 
        struct magic_voices ex; 
        ex.rval = rval; 
        throw ex; 
       } 
       return; 
     } 
     if (i < 0) return; 
     // flip current bit 
     str[i] = str[i] == '0' ? '1' : '0'; 
     magic_int(str, i-1, changesLeft-1); 
     // or don't flip it (flip it again to undo) 
     str[i] = str[i] == '0' ? '1' : '0'; 
     magic_int(str, i-1, changesLeft); 
} 

void magic(char* str, int i, int changesLeft) { 
    try { 
     magic(str, i, changesLeft); 
    } catch (struct magic_voices ex) { 
     printf("rval=%d\n", ex.rval); 
    } 
} 
+0

Являются ли эти дети стандартными C/C++? – gsamaras

+0

@gsamaras Да, см. Мое редактирование. – dbush

+0

C и C++ - это два разных языка. Использование явных переходов в C++ сильно обескуражено. Специально для чего-то простого, как обычное завершение рекурсии. Компилятор может принять решение о прыжке, если он хочет оптимизировать возврат. Программист не должен заниматься этой деталью при обычных обстоятельствах. – paddy

1

Это нерекурсивный вариант. В основном, он генерирует все возрастающие последовательности 0 <= a[0] < ... < a[dist-1] < strlen(num) и возвращает биты с соответствующими индексами.

void print(const char* num, const vector<int>& a) { 
    size_t k = 0, n = strlen(num); 
    for (size_t i = 0; i < n; ++i) 
     if (k < a.size() && a[k] == i) { 
      cout << (num[i] == '0') ? '1' : '0'; 
      ++k; 
     } 
     else 
      cout << num[i]; 
    cout << endl; 
} 

void hamming(const char* num, size_t dist) { 
    assert(dist > 0); 
    vector<int> a(dist); 
    size_t k = 0, n = strlen(num); 
    a[k] = -1; 
    while (true) 
     if (++a[k] > n - dist + k) 
      if (k == 0) 
       return; 
      else { 
       --k; 
       continue; 
      } 
     else 
      if (k == dist - 1) { 
       print(num, a); 
       // conditional return here 
      } 
      else { 
       a[k+1] = a[k]; 
       ++k; 
      } 
} 

который может быть использован, как это:

int main() 
{ 
    hamming("0000", 2); 
    /* output: 
     1100 
     1010 
     1001 
     0110 
     0101 
     0011 
    */ 
} 

P.S. Благодаря @ruakh для упоминания недостающей оптимизации в состоянии while - if.

+0

Alex, извините за комментарий после такого количества времени, но не будет этого 'cout << (num [i] == '0')? '1': '0'; 'более естественно записываться как' cout << (num [i] == '0')? '0': '1'; ', или это должно быть так, как вы его написали? О, это должно быть так, как вы его написали, но почему? =) – gsamaras

+0

@gsamaras Вектор 'a' должен содержать индексы, для которых бит должен быть инвертирован. Поэтому, если 'a' содержит текущий индекс' i', мы печатаем '1' вместо' 0' и наоборот. В противном случае мы будем печатать бит как есть (см. 'Else'-part). – AlexD

+0

Спасибо AlexD, я очень заинтригован алгоритмом и пытаюсь проанализировать его временную сложность, как показано [здесь] (http://stackoverflow.com/questions/41086928/time-complexity-of-an-iterative-algorithm) , – gsamaras

Смежные вопросы