【问题标题】:How to call a function inside itself?如何在自身内部调用函数?
【发布时间】:2010-12-09 09:23:48
【问题描述】:

我有一个函数可以生成一个每次都必须唯一的 4 个字符的键。为此,该函数首先生成一个键,然后检查数据库表以查看它是否正在被其他人使用。

如果它没有被使用,它会返回键,否则,它会再次调用自己,但这会导致函数执行无限循环,这是一个禁忌。这是整个函数:

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

【问题讨论】:

  • 另外,$this 不是 CodeIgniter 独有的。
  • 我认为他只是在解释 $this 的来源以及为什么它没有在代码 sn-p 中定义。
  • $this 用于对象/类范围。只要您的函数在一个类中并且您正在调用它,那么您必须使用 $this->functionName().. 或 self::functionName() 如果它是一个静态函数。等等等等

标签: php function codeigniter recursion


【解决方案1】:

我不会在重试场景中使用递归函数(因为你不重用函数的结果,所以使用递归毫无意义)......它增加了很多不必要的开销。做这样的事情:

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

return $key;

如果您接近最大键数,这将导致非常长的循环时间,因此您可能需要设置某种最大限制。

哦,如果这同时发生在多个线程上并且您正在检查一个数据库,您应该实现表写入锁定,这样同一个键就不能被插入两次。检查密钥是否可用的函数最好在同一事务中锁定检查和如果可用写入以避免任何冲突。

【讨论】:

  • 你是对的。这似乎是最好的(也是最简单的)解决方案。谢谢!
  • 在许多情况下,理想的解决方案只是小心设置一些机制以防止循环过长。
【解决方案2】:

需要返回自调用的结果,否则一旦递归就不会返回有效的key。

return $this->key_generator($length);

【讨论】:

  • 好点 =) 我不建议他在这种情况下使用递归。
【解决方案3】:

但这会导致函数执行无限循环,

如果你绝对想保留你的递归策略,你必须定义一个结束情况。例如,您可以定义一个计数器,如下所示:

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));
    }
}

但是也可以迭代地编写代码...

【讨论】:

    【解决方案4】:

    如果您在您的密钥生成例程中包含足够的唯一性,那么您或许可以从一开始就避免这种情况。例如。让例程考虑当前时间戳和本地主机名和/或 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;
        }
    

    【讨论】:

      【解决方案5】:

      您为什么不直接扫描键值空间以查找第一个未使用的键?除了四个字符长且唯一之外,还需要满足其他限制的密钥吗?

      您可以记住最后返回的键,以便在后续调用中从那里继续扫描。

      如果您希望后续调用不返回相似的密钥,您可以先洗牌您的密钥数据库。这意味着您需要在某处保存 456976、1679616、7311616 或 14776336 元素数组(取决于所使用的字母是单大写字符还是双大写字符,带或不带数字)。

      【讨论】:

        【解决方案6】:

        您可以将代码放入循环中并迭代地确定键,而不是 递归地

        示例:

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

        循环本身不会阻止无限循环,但与函数调用不同的是,它不会占用堆栈空间,因此不会冒堆栈溢出的风险。

        它还稍微简化了一些事情。根据密钥的类型,您还可以调整密钥生成方法,例如使用带编号的密钥,您可以在每次迭代中呈指数增长。

        备注:如果可能,请使用数据库的自动增量功能,而不是滚动您自己的密钥生成功能。

        还要确保保护您的代码免受并发访问。如果此函数的两个实例尝试生成一个密钥并且它们都确定相同怎么办?使用关键部分或事务来确保不会发生任何不好的事情。

        【讨论】:

          【解决方案7】:

          在自身内部使用函数

          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
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-11-14
            • 2015-05-18
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多