首頁 >後端開發 >Golang >Code n Golang 的出現:做或不做正規表示式

Code n Golang 的出現:做或不做正規表示式

Susan Sarandon
Susan Sarandon原創
2024-12-08 06:42:18816瀏覽

受到 Shradha Agarwal 在 Byte Size Go 上的帖子的啟發:這裡:我決定寫下我的方法,它是不同的,並且想分享它。那篇文章寫得很好,解決方案緊湊而簡單,我建議也先閱讀該文章。

這是一個 blogvent 系列,我也很想參加 blogvent,但不確定我會不會完成這個。

介紹

嗯,現在是 Code 2024 出現的第三天,我一直在直播。我落後了兩天,但正在一一解決。到目前為止,我已經在 Go 中學到了很多。讓我們開始第三天的生活。

第 1 部分

任何 AOC 問題的第一部分似乎都很簡單,但是一旦第二部分被揭示,真正的實現就開始令人費解(如果你不樂觀或不深思熟慮的話)

今天的第 1 部分是解析一個包含 mul(X,Y) 表達式的字串,其中 X 可以是任何 3 位數字。因此,字串中可能有多個這樣的表達式,目的是將單一表達式中的 X 和 Y 相乘並將它們相加。

Advent of Code n Golang: Do or Don

xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5))

在上面的例子中,有 4 個這樣的表達式,如果我們將它們相乘,我們得到的答案是 161。

方法

它看起來像正規表示式模式,在字串中尋找類似表達式的模式。因此,方法是使用正規表示式模式找到此類表達式,並將數字解析為整數,然後將它們相乘,簡單地。

您可以繼續編寫解析器來迭代字串中的每個字元並解析標記,然後評估表達式。這是一種有效的方法,但我選擇這樣做是因為我不知道如何誠實地編寫解析器,我也想在最後嘗試解決方案。

但對於第一部分,快速正規表示式似乎是個好主意。

建構正規表示式

第一件事是編寫 mul(X,Y) 部分的正規表示式,這是第一部分中唯一具有挑戰性的部分。剩下的只是簡單的數學。

所以,我們需要找到 mul,然後找到 a(然後找到任意 1 到 3 位數字長的數字,然後再找到 1 到 3 位數字長的數字,最後以 a 結尾)

譯為:

mul\((\d{1,3}),(\d{1,3})\) 

讓我們細分一下:

  • mul 用來捕捉字面字 mul

  • (這是表達式 mul() 中的第一個括號,如果我們想匹配它,我們需要在正則表達式中轉義括號,所以我們在它之前使用。

  • 然後我們有一個配對組 (d{1,3}) ,這將是 mul(X,Y) 中的 X:

    • 匹配組就像正規表示式中的一組匹配,基本上,如果你想捕獲整個匹配中的特定部分,那麼我們使用() 將它們單獨分組,這不是必需的,但有助於獲得正確的事情,沒有開銷
    • 在本例中,我們使用匹配組來捕獲 1 到 3 位數字的數字。
    • 另一種寫法是([0-9]{1,3}) ,它也會做同樣的事情,(注意: [0-9] 和d 有一些區別,但那就是非常微妙,不會影響這個謎題,如果你仍然好奇,我更喜歡閱讀這個StackOverflow 問題)
  • 然後我們使用 , 作為 mul(X,Y) 表達式中 X 和 Y 操作數的分隔符號

  • 然後我們以類似的方式將 mul(X,Y) 中的 Y 與 (d{1,3}) 配對組進行配對

  • 最後我們用 ) 來結束正規表示式

編碼它

這非常簡單,我們將行作為字符串獲取,並使用regexp.MustCompile 函數,它為我們提供了一個Regexp 對象,該對象又具有一些與之關聯的方法來查找、匹配、替換和其他可以執行的操作使用字串上的正規表示式來完成。

一旦我們有了 mulRegex ,我們就可以使用與 regexp 套件中的 Regexp 結構關聯的 FindAllStringSubmatch 方法。此函數接受一個字串來執行正規表示式,以及要傳回的最大符合數。我們想要所有結果,因此我們將它們傳入 -1 以獲得所有匹配項。

現在,此方法傳回字串切片的切片,每個切片都是一個匹配項,每個切片內都有一個字串切片,其中包含匹配的字串以及字串中的後續匹配組(如果有)。

xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5))

所以,上面的函數將會回傳類似這樣的內容

mul\((\d{1,3}),(\d{1,3})\) 

這是一個字串列表,這些看起來像數字,但它們是 Golang 中的字串類型。

現在我們有了這個,我們可以創建獲取結果的實際邏輯部分,即解析這些表達式,將 X 和 Y 相乘並將每個表達式的結果相加。

func FindMulExpression(line string) [][]string {
  mulRegex := regexp.MustCompile(`mul\((\d{1,3}),(\d{1,3})\)`)
  return mulRegex.FindAllStringSubmatch(line, len(line))
}

這非常簡單,我們迭代每個匹配項,即一個 mul(X,Y) 表達式,並將 X 和 Y 分別解析為整數並相乘,然後將獲得的結果加到分數中。我們對字串(行)中找到的每個 mul(X,Y) 表達式執行此操作並傳回最終分數。

輸入

現在,範例給了我們一個字串,但我意識到輸入中有六行(單獨輸入),因此我們需要解析每一行並將分數相加。

請記住這一點,因為它在第2 部分中至關重要,我花了一些時間並質疑我的存在,才意識到我們需要組合所有行才能獲得結果(在第1 部分中不是必需的,但在第2 部分中肯定是這樣)。

第2部分

這通常是出問題的地方。至少對我來說是這樣。

我從一個非常幼稚的方法開始,使用永遠循環並找到“做”或“不做”的索引並剝離這些部分,然後循環直到我們沒有剩下“做”和“不做”。這對測試用例有效,但在實際輸入上失敗了。

我最終想出的方法,並透過稍微調整相同的方法來發揮作用。

方法

我想到的是在整個字串中找到 don’() 和 do() 字串的第一個匹配位置,我們將其刪除並刪除 don’t() 之後或 do() 之前的部分。這樣我們就可以將字串修剪為僅有效/啟用的 mul() 表達式。

Advent of Code n Golang: Do or Don

因此,更明確定義的方法將是:

  • 找出 dont() 和 do() 表達式的位置(索引)

  • 我們需要追蹤前一個字串是啟用還是停用,因此將保留一個標誌以將啟用的表達式(字串的一部分)附加到最終結果

  • 如果沒有找到,則按原樣傳回字串(行)

  • 如果我們找到其中任何一個:

    • 如果我們先發現don't(dont()出現在do()之前)

      • 如果啟用標誌為 true → 將字串附加到 dont() 表達式之前
      • 然後將啟用切換為 false 並修剪掉 don't() 部分 (這樣我們就完成了 don't 表達式之前的所有檢查,因此我們從行字串中刪除該部分)
    • 如果我們發現do先出現(do()出現在dont()之前)

      • 如果啟用標誌為 true → 將字串附加到 do() 表達式之前
      • 然後將啟用切換為 true 並修剪掉 do() 部分 (這樣我們就完成了 do 表達式之前的所有檢查,因此我們從行字串中刪除了這部分)
  • 我們這樣做,直到沒有剩餘的線串需要檢查

程式碼

我使用簡單的 Strings.Index 來取得子字串的第一個匹配索引,在本例中,我想要 dont() 和 do() 的第一個匹配索引。一旦我們有了兩個匹配項的索引,我們就可以迭代,直到字串中不再有任何該做或不該做的事情。

如果我們有 do 或 don ,我們會在字串中附加 do not 之前的部分(如果啟用)或在 do 之前的部分(如果啟用),並相應地打開和關閉啟用標誌。在循環結束時,結果字串將僅包含行/字串的啟用部分。

xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5))

我將這個函數傳遞給乘法函數,在其中我取得 mul 表達式的匹配模式並進行數學計算。

strings.Index 方法接受一個字串和一個要在該字串中尋找的子字串,並傳回該子字串第一個出現的實例的索引。這樣我們就可以識別行字串是否包含 do() 或 dont() 表達式,如果不包含,我們只需返回該行,如果存在它們的實例,我們循環並修剪該行之前和之後的字串。表達式取決於該標誌是啟用還是停用。

讓我們舉個例子並演練一下邏輯:

xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5))

在將結果傳遞給 FindMulExpression 函數後,我們使用與第一部分中使用的相同的 Multiply 函數來處理結果,該函數將傳回結果行字串中的所有 mul 表達式。

注意輸入

拼圖的實際輸入我認為是多行,所以我們需要在所有剩餘的行中保留該行的狀態。或者,我們可以更聰明,創建一個大字串並處理它。兩者都是有效的並且會給出相同的結果。我只是不想增加追蹤所有狀態和行的開銷,所以我只是連接所有行並處理該單一字串。

結論

本質上這是一個簡單的問題,但如果您不了解正規表示式,您可能會陷入編寫自己的解析器或有線字串操作的兔子洞(就像我一樣)。

這是第三天,我將在週末甚至下週進行更多的直播解決問題。在 GitHub 上尋找我的 AoC 解決方案的程式碼。

到那時,

快樂編碼:)

以上是Code n Golang 的出現:做或不做正規表示式的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn