通过重构简化代码

时间:2011-08-26 20:42:08

标签: java c refactoring

是否存在可以简化此类冗余代码的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;
}

9 个答案:

答案 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;
}

第1步:持续传播

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

第2步:切片

$ 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)

为什么不使用优化编译器编译代码。然后反编译代码。这只是我的想法,我没有尝试过。