表示器中的正規化


為了讓只有非必要差異的 solution 能有相同的 representation,Representer 應該套用正規化。一般來說,建立正規化 representation 的流程如下:

  • 將 solution 的程式碼解析成抽象語法樹(AST)
  • 對 AST 套用正規化
  • 將正規化後的 AST 轉換成字串
  • 將正規化後的 AST 字串寫入名為representation.txt的檔案(請參閱介面)

不過要注意,representation 不一定要是 AST,也可以是普通的(正規化過的)程式碼,端看哪一種對你的 track 最好。

為了幫助你上手,我們接著介紹一些常見的正規化策略。

注意 1:這些正規化範例彼此並不互相累加,每個範例只示範一種特定的正規化。實際的 Representer 會想依序套用它們。

注意 2:這些準則所用的程式碼會使用 C#,但準則本身與語言無關。

正規化識別字

為了讓 representation 不受命名影響,使用者自訂的名稱(例如變數、函式等)可以替換成佔位符。這種情況下,應該產生一份mapping.json(請參閱介面)。

請務必注意,所有相同名稱都必須替換成同一個佔位符,不論其作用域為何。

原始碼

public static class Fake
{
    public static int Test(int input)
    {
        var test = input + 2;
        return test;
    }
}

Representation

public static class PLACEHOLDER_1
{
    public static int PLACEHOLDER_2(int PLACEHOLDER_3)
    {
        var PLACEHOLDER_4 = PLACEHOLDER_3 + 2;
        return PLACEHOLDER_4;
    }
}

正規化空白

空白不一致的情況非常常見,因此將它正規化是常見的一個步驟。換行符號也應該一併正規化。

原始碼

using   System;

     public static    class Fake
{
    public     static   DateTime Add    (DateTime    birthDate)
    {
        return birthDate.Add( TimeSpan.FromSeconds   (   10  ) )   ;
    }
}

Representation

public static class Fake
{
    public static DateTime Add(DateTime birthDate)
    {
        return birthDate.Add(TimeSpan.FromSeconds(10));
    }
}

正規化區塊

在許多語言中,使用者對於如何定義區塊(或作用域)擁有一定的自由。例如在多數類似 C 的語言中,作用域是宣告在大括號之間的。把大括號放在同一行或下一行通常沒有差別,因此可以把這點正規化。

原始碼

public static class Fake {
    public static int Test() {
        if (1 > 2) {
            return 0;
        }

        return 1;
    }
}

Representation

public static class Fake
{
    public static int Test()
    {
        if (1 > 2)
        {
            return 0;
        }

        return 1;
    }
}

移除不重要的程式碼

並非每一小段程式碼對 Representer 都有意義,這些部分可以被移除。舉例來說,在多數語言中,註解並不重要,可以安全地移除。

原始碼

/*
   These are some very nice
   comments spanning multiple lines
*/
public static class Fake
{
    // Nice method
    public static string Test()
    {
        return "Test"; // This is very nice
    }
}

Representation

public static class Fake
{
    public static string Test()
    {
        return "Test";
    }
}

順序不重要時進行正規化

在某些情況下,程式碼的順序並不重要。為了避免相同的程式碼因為順序不同而產生不同的 representation,將它排序可能很有用。通常需要排序的項目是函式或宣告。要找出排序依據的指標可能不太容易,實作起來也可能很棘手。其中一個指標可以是該節點包含多少個 AST 子節點,另一個則可以是第一個子節點型別的名稱。

這個範例依某種函式長度來排序:

原始碼

public static class Fake
{
    public static string Test2()
    {
        int a = "Test2";
        return a;
    }

    public static string Test()
    {
        return "Test";
    }
}

Representation

public static class Fake
{
    public static string Test()
    {
        return "Test";
    }

    public static string Test2()
    {
        int a = "Test2";
        return a;
    }
}