我发现我经常进行递归调用以重新排序参数。
例如,这是我对endOther
from codingbat.com的解决方案:
给定两个字符串,如果其中一个字符串出现在另一个字符串的最末端,则返回
true
,忽略大小写差异(换句话说,计算不应该是“区分大小写”)。注意:str.toLowerCase()
返回字符串的小写版本。
public boolean endOther(String a, String b) {
return a.length() < b.length() ? endOther(b, a)
: a.toLowerCase().endsWith(b.toLowerCase());
}
我对递归很满意,但我当然可以理解为什么有些人会反对它。
这种递归技术有两种明显的替代方法:
a
和b
public boolean endOther(String a, String b) {
if (a.length() < b.length()) {
String t = a;
a = b;
b = t;
}
return a.toLowerCase().endsWith(b.toLowerCase());
}
if
语句打破了“流程”public boolean endOther(String a, String b) {
return (a.length() < b.length())
? b.toLowerCase().endsWith(a.toLowerCase())
: a.toLowerCase().endsWith(b.toLowerCase());
}
||
这两个表达所以我的问题是:
为了更多地讨论技术而不是特定的编码问题,这里是另一个例子,我觉得递归比if-else,swap或重复代码要优雅得多。
// sorts 3 values and return as array
static int[] sort3(int a, int b, int c) {
return
(a > b) ? sort3(b, a, c) :
(b > c) ? sort3(a, c, b) :
new int[] { a, b, c };
}
递归和三元运算符并不像困扰某些人那样困扰我;老实说,我相信上面的代码是可以编写的最好的纯Java解决方案。请随意给我看。
答案 0 :(得分:1)
对于这个特殊的例子,我不会使用你建议的任何东西..我会写:
public boolean endOther(String a, String b){ String alower=a.toLowerCase(); String blower=b.toLowerCase(); if ( a.length() < b.length() ){ return blower.endsWith(alower); } else { return alower.endsWith(blower); } }
虽然三元运算符确实有它的位置,但if
语句通常更容易理解,尤其是当操作数相当复杂时。此外,如果在if
语句的不同分支中重复代码,它们将仅在所采用的分支中进行评估(在许多编程语言中,无论选择哪个分支,都会评估三元运算符的两个操作数)。虽然正如你所指出的那样,这在Java中并不是一个问题,但是许多程序员已经使用了各种语言并且可能不记得这种程度的细节,因此最好只使用三元运算符和简单的操作数。
经常听到“递归”与“迭代”/“非递归”实现。我没有听说过您提供的各种选项的任何特定名称。
无论如何,我的建议是尽可能少地在每个陈述中做。您在单个语句中执行的操作越多,对于需要维护代码的其他人来说就越容易混淆。
就你对重复的抱怨而言...如果有几行被重复,那么是时候创建一个“助手”功能来完成这一部分了。功能组合是为了减少重复。交换只是没有任何意义,因为交换更多的努力比简单地重复...而且,如果函数后面的代码使用参数,参数现在意味着与以前不同的东西。
修改的
我对三元运算符的论证不是一个有效的...绝大多数编程语言都使用三元运算符进行惰性求值(我在撰写本文时考虑过Verilog,这是一种硬件描述语言( HDL),其中两个分支并行评估)。也就是说,有正当理由避免在三元运算符中使用复杂的表达式;例如,使用if ... else语句,可以在其中一个条件分支上设置断点,而使用三元运算符,两个分支都是同一语句的一部分,因此大多数调试器不会在它们上拆分
答案 1 :(得分:1)
让我们首先确定代码重复通常是一个坏主意。
所以无论我们采取什么解决方案,方法的逻辑只应该写一次,我们需要一种交换参数的方法,不会干扰逻辑。
我看到了三个一般的解决方案:
if
或条件运算符)。swap
- 在Java中,这是一个问题,但可能适用于其他语言。我不知道哪些解决方案客观上是最好的。然而,我注意到有某些算法(1)通常被认为是惯用的解决方案,例如: Euklid用于计算两个数字的GCD的算法。
我一般反对swap
解决方案(2),因为它增加了一个额外的调用,它实际上并没有与算法有关。现在,技术上这不是问题 - 我怀疑使用任何体面的编译器效率低于(1)或(3)。但它增加了精神上的速度。
解决方案(3)让我觉得过度工程虽然我想不出任何批评,只是它更多的文字要阅读。一般来说,我不喜欢任何以“Impl
”为后缀的方法引入的额外间接。
总之,在大多数情况下我可能更喜欢(1),尽管我实际上在类似的情况下使用过(3)。
答案 2 :(得分:1)
另外+1为“在任何情况下,我的建议是尽可能少地在每个陈述中做。你在一个陈述中做的事情越多,对于那些需要维护你的人来说就越困惑。代码“。
很抱歉,但您的代码:
// sorts 3 values and return as array
static int[] sort3(int a, int b, int c) {
return
(a > b) ? sort3(b, a, c) :
(b > c) ? sort3(a, c, b) :
new int[] { a, b, c };
}
也许对你来说是最好的“纯Java代码”,但对我而言,这是最糟糕的......难以理解的代码,如果我们没有方法或评论,我们一眼就看不到它正在做什么...
只有在需要高性能时才能使用难以读取的代码(但无论如何,许多性能问题都是由于糟糕的架构......)。如果你必须编写这样的代码,你可以做的就是做一个好的javadoc和单元测试...我们开发人员通常不关心这些方法的实现,如果我们只是必须使用它,而不是重做它......但是,由于第一眼看不到我们做了什么,我们必须相信它的工作方式与我们预期的一样,我们可以节省时间......
当它是一个简短的方法时,递归方法是可以的,但我认为如果算法很复杂并且在几乎相同的计算时间内有另一种方法可以避免递归方法...特别是如果其他人会多产在这种方法中工作。
对于你的例子,这是好的,因为这是一个简短的方法,但无论如何,如果你只是不关心表演,你可以使用类似的东西:
// sorts int values
public static int[] sort(Integer... intValues) {
ArrayList list = new ArrayList(
for ( Integer i : intValues ) {
list.add(i);
}
Collections.sort(list);
return list.toArray();
}
一种实现方法的简单方法,易于被所有java&gt; = 1.5开发者读取,适用于1到n个整数... 不是最快但是无论如何只是速度使用c ++或asm:)
答案 3 :(得分:0)
使用其他方法而不是递归
稍微好一些public boolean endOther(String a, String b) {
return a.length() < b.length() ? endOtherImpl(b,a):endOtherImpl(a,b);
}
protected boolean endOtherImpl(String longStr,String shortStr)
{
return longStr.toLowerCase().endsWith(shortStr.toLowerCase());
}