是否存在可以简化此类冗余代码的C或Java重构工具。我相信这称为数据传播。
这基本上是优化编译器的功能。
public int foo() {
int a = 3;
int b = 4;
int c = a + b;
int d = c;
System.out.println(c);
return c;
}
进入
public int foo() {
int c = 7;
System.out.println(c);
return c;
}
答案 0 :(得分:20)
我认为这不是一个好主意。
例如以下代码:
long hours = 5;
long timeInMillis = hours * 60 * 1000;
这比仅仅更清洁和易懂:
long timeInMillis = 300000;
答案 1 :(得分:6)
我可以为C提供解决方案。我的解决方案使用我在另一个答案here中描述的两个工具(反向顺序)。
这是你的程序,翻译成C:
int foo() {
int a = 3;
int b = 4;
int c = a + b;
int d = c;
printf("%d", c);
return c;
}
$ frama-c -semantic-const-folding t.c -lib-entry -main foo
...
/* Generated by Frama-C */
/*@ behavior generated:
assigns \at(\result,Post) \from \nothing; */
extern int ( /* missing proto */ printf)() ;
int foo(void)
{
int a ;
int b ;
int c ;
int d ;
a = 3;
b = 4;
c = 7;
d = 7;
printf("%d",7);
return (c);
}
$ frama-c -slice-calls printf -slice-return foo -slice-print tt.c -lib-entry -main foo
...
/* Generated by Frama-C */
extern int printf() ;
int foo(void)
{
int c ;
c = 7;
printf("%d",7);
return (c);
}
答案 2 :(得分:3)
是的,我见过的最好的重构工具是他们的大脑。
大脑似乎是一个非常好的工具,用于逻辑组织代码以供其他大脑消费。它还可以用于在适当的情况下使用注释增强代码,并通过布局和命名赋予其他含义。
编译器有助于优化代码,使其更接近构成处理器的晶体管的底层。更高代编程语言的一个好处是,它不像机器制作的那样读取。
道歉,如果这看起来有点油腻和无益。我当然使用了各种工具,但我不记得任何处理“数据传播”的工具。
答案 3 :(得分:2)
Eclipse(我确定NetBeans和IntelliJ)几乎可以使用所有这些重构。我将用Eclipse给出具体细节。从:
开始public int foo() {
int a = 3;
int b = 4;
int c = a + b;
int d = c;
System.out.println(c);
return c;
}
首先,d
将显示您有未读局部变量的警告。该行<CTRL>+1
并选择“删除d和所有分配”。然后你有:
public int foo() {
int a = 3;
int b = 4;
int c = a + b;
System.out.println(c);
return c;
}
接下来,突出显示a
中的int c = a + b;
并输入<CTRL>+<ALT>+I
以内联a
。重复b
,您将拥有:
public int foo() {
int c = 3 + 4;
System.out.println(c);
return c;
}
现在你快到了。我不知道将3 + 4转换为7的重构。似乎某人很容易实现,但可能不是常见的用例,因为其他人已经指出,取决于域,3 +4可以比7更具表现力。你可以更进一步内联c
,给你:
public int foo() {
System.out.println(3 + 4);
return 3 + 4;
}
但是在不知道原始代码的“真实”问题的情况下,无法知道这是一种改进还是倒退。
答案 4 :(得分:1)
代码的语义信息可能会丢失。可能的依赖可能会破坏。简而言之:只有程序员知道哪些变量很重要或者可能变得重要,因为只有程序员知道代码的上下文。我担心你必须自己进行重构
答案 5 :(得分:1)
是的,IntelliJ在其社区版本中提供此功能。现在要解决一个更严重的问题,我很确定你正在将编译与重构混在一起。当你编译某些东西时,你会使用比机器代码更高的语言并将其转换为机器代码(基本上)。你想要的是删除在程序文件,.c,.java等高级语言中冗余的声明。很可能编译器已经根据您的建议优化了不太好的代码,有可用的工具来查看它正在做什么。在重构方面,通常更好,但不要牺牲更少的代码行的可维护性。
答案 6 :(得分:1)
一种可能的方法是将它放入符号数学程序(如Mathematica或Maple)中并让它为您进行简化。无论它们是否是常数,它都会这样做。
缺点是您需要将代码转换为其他语言。 (虽然如果语法类似,它可能主要是复制和粘贴。)此外,如果您希望某些整数类型以特定大小溢出,则可能会很危险。符号数学程序不关心,并将根据“数学”进行优化。浮点舍入误差也是如此。
在您的示例中,如果您将其输入Mathematica:
a = 3;
b = 4;
c = a + b;
d = c;
c
将在Mathematica中输出:
7
当然,你不能只是复制和粘贴,因为它是一种不同的语言和不同的语法,但这是我对你的问题最好的想法。在将其投入C / C ++之前,我自己使用Mathematica来简化表达式和其他数学。
对于涉及未知数的更复杂的例子:
原始C代码:
int a = 3 + x*x;
int b = 4 + y*y;
int c = a + b - 7 + 2*x*y;
int d = c;
将其输入Mathematica(仍主要是复制+粘贴):
a = 3 + x*x;
b = 4 + y*y;
c = a + b - 7 + 2*x*y;
d = c;
FullSimplify[c]
输出:
(x + y)^2
将其转换回以下C代码:
d = (x + y)
d = d * d;
这显然比原始代码简单得多。一般来说,符号程序甚至可以处理非平凡的表达式,并且与任何内部编译器一样好(甚至更好)。
最后的缺点是像Mathematica或Maple这样的象征性数学程序不是免费的,而且相当昂贵。 SAGE是一个开源程序,但我听说它不如Mathematica或Maple好。
答案 7 :(得分:0)
如果您正在谈论C,您可以查看已编译的优化汇编代码。然后,您可以将C代码重构为与优化程序集相同的结构。然而,就像阿尔弗雷多所说,这可能导致更加模糊的代码。
答案 8 :(得分:0)
为什么不使用优化编译器编译代码。然后反编译代码。这只是我的想法,我没有尝试过。