2009-10-23 3 views
7

У меня есть функция, которая генерирует ключ из 4 символов, который должен быть уникальным для каждого времени. Для этого функция сначала генерирует ключ, а затем проверяет таблицу базы данных, чтобы узнать, используется ли она кем-то другим.Как вызвать функцию внутри себя?

Если он не используется, он возвращает ключ, иначе он снова вызывает себя, но это заставляет функцию выполнять бесконечный цикл, который является no-no. Вот и вся функция:

function key_generator($length = 4) 
{ 
    // I've subsequently left out the generating code, 
    // which is not necesarry in this case 

    $key = 'xxxx'; 

    if ($this->user_model->valid_key($key) == true) 
    { 
     return $key; 
    } 
    else 
    { 
     $this->key_generator(4); 
    } 
} 

Каков правильный способ вызова функции еще раз?

Кстати, я использую CodeIgniter, следовательно $this.

+2

Это называется рекурсией – Makach

+1

Кроме того, $ это не только для CodeIgniter. –

+1

Я думаю, он просто объяснял, откуда это взялось и почему он не определен в фрагменте кода. – JAL

ответ

25

Я бы не использовать рекурсивные функции повторов-сценарии (так как вы не» t повторно использовать результат функции, бессмысленно использовать рекурсию) ... Это добавляет много лишних накладных расходов. Сделайте что-то вроде этого:

do { 
    $key = ...; // Generate your key here... 
} while (!$this->user_model->valid_key($key)); 

return $key; 

Если вы рядом с максимальным количеством клавиша, это приведет к очень долгому времени цикла, так что вы можете захотеть поставить какое-то максимальный предел.

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

+0

Вы правы. Это похоже на лучшее (и самое простое) решение. Благодаря! –

+0

Идеальное решение во многих случаях просто будьте осторожны, чтобы создать механизм для предотвращения чрезмерно длинной петли. – Stuart

1

Вы можете поместить свой код в петлю и определить ключ итеративно вместо рекурсивно.

Пример:

function key_generator($length = 4) 
{ 
    do { 
    $key = 'xxxx'; //TODO 
    if (timeOutReached()) return InvalidKey; 
    } while (!$this->user_model->valid_key($key)) 

    return $key; 
} 

петля сам не предотвращает петлю infinte, но в отличие от вызова функции, это не съедает пространство стека, так что вы не рискуете переполнения стека.

Также это немного упрощает работу. В зависимости от типа ключа вы также можете адаптировать метод генерации ключей, например, с пронумерованными клавишами вы можете увеличивать экспоненциально с каждой итерацией.

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

Также убедитесь, что вы защищаете свой код от одновременного доступа. Что делать, если два экземпляра этой функции пытаются сгенерировать ключ, и оба они определяют то же самое? Используйте критические разделы или транзакции, чтобы убедиться, что ничего плохого не происходит.

2

, но это вызывает функцию, чтобы сделать бесконечный цикл,

Если вы абсолютно хотите сохранить рекурсивную стратегию вы должны определить конечный случай. Например, вы можете определить счетчик, например:

function key_generator($length = 4, $limit=5) 
{ 
    if($limit === 0) { 
     throw new YourException(); 
    } 

    // I've subsequently left out the generating code, 
    // which is not necesarry in this case 

    $key = 'xxxx'; 

    if ($this->user_model->valid_key($key) == true) 
    { 
     return $key; 
    } 
    else 
    { 
     return $this->key_generator(4, ($limit-1)); 
    } 
} 

Однако также можно сделать свой код итеративен ...

5

Вам необходимо вернуть результат самовыражения, иначе действительный ключ не будет возвращен после его повторения.

return $this->key_generator($length); 
+0

Хорошая точка =) Я бы не рекомендовал его использовать рекурсию в этом случае. – Blixt

2

Если включить достаточно уникальность в вашей ключевой рутина поколения, вы могли бы избежать этой ситуации в первую очередь. Например. иметь подпрограмму учитывать текущую временную метку и локальное имя хоста и/или PID.

Цитирование в такой недетерминированной форме, как правило, является доказательством того, что какая-то часть слишком наивна. Это не хорошо. :-)


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

function key_generator($length = 4) 
    { 
     /* The $attempts_left clearly depends on how much trust 
      you give your key generation code combined with the key space size. */ 
     $attempts_left = pow(16, $length) * 2; 
     /* ... just guessing, in case your key base is 16, i.e. [0-9a-z] for example */ 

     do { 
      // ... key generation goes here ... 
      $key = 'xxxx'; 
     } while ($this->user_model->valid_key($key) == false && $attempts_left-- > 0); 

     if($attempts_left < 1) 
      return false; 
     else 
      return $key; 
    } 
1

Почему не вы просто просматриваете пространство значений ключа для первого неиспользуемого ключа? Нужен ли ключ для выполнения дополнительных ограничений на четырехкратных и уникальных символах?

Вы можете вспомнить последний возвращенный ключ для возобновления сканирования оттуда при последующих вызовах.

Если вы хотите, чтобы последующие вызовы не возвращали похожие ключи, вы можете сначала перетасовать свою базу данных ключей. Это означало бы, что вам нужно держать где-то элемент массива элементов размером 456976, 1679616, 7311616 или 14776336 (в зависимости от того, используются ли алфавиты с символами с одним или двумя символами, с цифрами или без них).

0

Использование функции внутри себя

function test($val) { 
    /*initialize return value by using the conditions*/ 
    if($val>=5){ 
     /*do something with return statement*/ 
     return $val+10; 
    } else { 
     /*set the return default value for avoid the error throwing*/ 
     return "default value"; 
    } 
    /*return the function used for check the condition*/ 
    return test($val); 
} 

echo test(4); // output "default value"; 
echo test(6); //output 16 
Смежные вопросы