【问题标题】:Why should we use return statement when using a recursive call?为什么在使用递归调用时要使用return语句?
【发布时间】:2020-01-08 23:32:12
【问题描述】:

在下面的整数类型的二分查找函数返回值中,当递归调用时,为什么要在else if语句中都使用返回值呢?如果我,代码将不起作用 直接使用递归调用而不是使用它作为回报。

int binarysearch(int l,int h,int key)
{
  int mid;
  mid=(l+h)/2;
  if(l<=h)                             
  {
    if(key==a[mid])
      return mid;
    else if(key>a[mid])
      return(binarysearch(mid+1,h,key));
    else if(key<a[mid])
      return(binarysearch(l,mid-1,key));
  }
  else
    return -1;
}

【问题讨论】:

  • 相关:这个函数需要重写,以便只有一个递归调用。它应该以return binarysearch(low, high, key); 之类的结尾。否则你只会产生一个非常慢的膨胀函数,它比仅仅编写一个循环要糟糕得多——因为编译器不能尾调用优化它。这反过来又表明在这种情况下绝对不需要使用递归。首先问自己“为什么我在这里使用递归”?看看你是否能想出另一个答案,而不是“因为我正在学习递归”。
  • 如果我使用低=中+1。然后,如果我按照您所说的将其放在函数内部 binarysearch(low,high,key) 。有什么区别
  • 不同之处在于,如果您有多个带有递归调用的“分支”(if else 场景),则参数计算将发生在这些分支中,您无法确定编译器可以优化掉递归调用。 (我为 x86 尝试了 gcc 和 clang,结果他们可以优化这个特定的代码,但我不会指望它。)
  • @Lundin 该功能不能非常慢。它的运行时复杂度仍然是 O(log n),对于所有实际用途来说已经足够快了。
  • @Lundin 我们是在真空中讨论的,没有衡量性能问题,也不知道整个具体程序的瓶颈。首先,我不会递归地编写该函数。但是,我也不会为优化具有 O(log n) 最坏情况复杂度的函数而烦恼,除非它是一个瓶颈。对我来说,过早意味着“生命周期过早”。

标签: c sorting


【解决方案1】:

虽然它的返回值是int 类型,但该函数必须返回一个值。它返回的每个地方都必须返回一个值。当当前函数调用的递归调用返回时,它会给你答案;您必须将该答案返回给调用上下文——递归链的下一级或原始调用者。

如果您不返回值,则调用undefined behaviour,这是一个坏事!™

【讨论】:

  • 如果您愿意,您可以将答案保存在外部变量中,并使用指向它的指针,并为函数提供 void 返回类型。在我的公司,我们的编码标准不允许在返回中包含这种类型的构造,因为它很难测试。
  • @rallen911 — 变得线程不安全且更难使用。但是你也修改了函数返回void(无),如果它什么都不返回,那么显然你在return语句中没有返回值。
  • @rallen911 递归函数可能比在return 中调用函数更难测试
  • 不从声明为返回值的函数返回值(通过到达右括号)不会导致未定义的行为。调用者使用未返回值的函数的返回值会导致 C 标准未定义该行为。
  • @EricPostpischil — 是的,但我试图让我的回答让一个 tyro 能够理解。差异通常可以忽略不计,但并非总是如此。特别是,这种差异不适用于递归搜索代码。
【解决方案2】:

如果你的函数原型定义了它的返回类型是一个非空数据,那么在该函数的主体中必须返回一个原型中指定的类型的值。 请注意,在您的函数原型中明确表示必须返回“int”类型的值:

int binarysearch(int l, int h, int key);

在它的函数体内有一些条件。这些条件导致不同的执行指令。您需要验证,在每条语句的最后,函数返回的相同类型的值实际上会被返回。

如果你不想使用return,你的函数应该有如下原型:

void binarysearch(int l, int h, int key, int *mid);

在这种情况下,您将使用指针从该函数的内部上下文中检索“mid”的值。

【讨论】:

  • 第一句话不对。 C 标准允许声明返回值的函数不返回值(通过到达其右大括号),前提是调用者不尝试使用返回值。
  • 使用“return”子句破坏 void 函数是一种不好的编程习惯。任何 void 功能都可以完美地执行,而不会用“return;”破坏它。我的解决方案解释了如何使用最佳实践进行编程!让它发挥作用并不意味着它是正确的!
  • 我的评论不是关于“void 函数”,我认为您的意思是返回类型为 void 的函数。它是关于一个声明为返回void 以外的类型的函数。这个答案的第一句话是错误的:C 标准不要求返回类型为非void 的函数返回值,这样做本身并不会呈现标准未定义的行为。跨度>
【解决方案3】:

不清楚你在问什么。我认为你想要一个返回语句,这可以通过你的函数的这个编辑来完成:

extern int a[];
int binarysearch(int l,int h,int key)
{
  int mid;
  mid=(l+h)/2;
  if(l<=h) {
    if(key>a[mid]) {
      mid = binarysearch(mid+1,h,key);
    } else if(key<a[mid]) {
      mid = binarysearch(l,mid-1,key);
    }
  } else {
    mid = -1;
  }
  return mid;
}

您的原始函数对我造成了编译器诊断:

bb.c:17:1: 警告:控制可能到达非空函数的末尾 [-Wreturn-type] }

这似乎是一个虚假的编译器警告,因为它看到了以下构造:

if (a == b) {
    return ...;
} else if (a < b) {
    return ...;
} else if (a > b) {
    return ...;
}

还有其他的可能。我补充说:

return -42;

这似乎满足了它。

【讨论】:

  • “虚假警告”是因为 if / else if / else if 链没有 else。如果 else 曾经被使用过,那么该函数将不会返回值。需要(大量)更多分析才能意识到最后一个else if 中的条件是多余的并且是重复的。从else if (a &gt; b) 中删除if (a &gt; b) 条件或添加else return -1;(或-42——这就是生命、宇宙和一切的答案)。如果您放弃显式条件以记录对人类而言明显正确但对编译器不正确的内容,则可以将 assert(a &gt; b); 添加到 else 子句中。
  • 我想我会重组你的if 链——if(l&lt;=h) { if(key&gt;a[mid]) { mid = binarysearch(mid+1,h,key); } else if(key&lt;a[mid]) { mid = binarysearch(l,mid-1,key); } } else { mid = -1; } 以避免额外的缩进:if (l &gt; h) { mid = -1; } else if (key &gt; a[mid]) { mid = binarysearch(mid + 1, h, key); } else if (key &lt; a[mid]) { mid = binarysearch(l, mid-1 , key); } /* else found it at entry mid */。我没有从评论中删除该文本以验证它是否正确括起来,尽管我认为它是正确的。基本思想是“在你测试某事之后,做某事——不要再进行测试”。
  • "if the else was used" -- 因为除了 >,
猜你喜欢
  • 2014-05-26
  • 1970-01-01
  • 1970-01-01
  • 2020-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多