5 min to read
CIS 194 02 Homework
Source: CIS 194: Homework 2
發生了嚴重錯誤!
- 您需要的文件:
Log.hs
,error.log
,sample.log
- 您應提交的文件:
LogAnalysis.hs
▌Log file parsing 日誌文件解析
我們確實不確定發生了什麼,但我們確實設法恢復了日誌文件 error.log
。 它似乎在每一行上都包含一個不同的日誌消息。 每行以一個字符開頭,指示其表示的日誌消息的類型:
I
信息,W
警告,E
錯誤
錯誤消息行中的整數將指示錯誤的嚴重性, 1 是您可能在明年夏天某個時候遇到的那種錯誤,而 100 是史詩般的災難性故障。 然後,所有類型的日誌消息都有一個整數時間戳,其後是文本內容,該文本內容一直行到行尾。 這是日誌文件的片段,包括信息和 2 級錯誤消息:
I 147 mice in the air, I’m afraid, but you might catch a bat, and
E 2 148 #56k istereadeat lo d200ff] BOOTMEM
一切都很混亂。顯然,我們需要一個程序來解決這一混亂局面。 我們提出了一些數據類型來捕獲此日誌文件格式的結構:
data MessageType = Info
| Warning
| Error Int
deriving (Show, Eq)
type TimeStamp = Int
data LogMessage = LogMessage MessageType TimeStamp String
| Unknown String
deriving (Show, Eq)
請注意,LogMessage
具有兩個構造函數:一個用於表示標準格式的日誌消息,另一個用於表示任何其他不適合正確格式的消息
我們為您提供了一個 Log.hs
模塊,其中包含這些數據類型聲明以及其他一些有用的功能。 下載 Log.hs
,並將其放在要放置作業的同一文件夾中。 請命名您的作業作業 LogAnalysis.hs
(如果要使其成為 literate Haskell 文檔,則命名為.lhs)。 LogAnalysis.hs
的前幾行應如下所示:
{-# OPTIONS_GHC -Wall #-}
module LogAnalysis where
import Log
它將文件設置為名為 LogAnalysis
的模塊,並從 Log.hs
導入該模塊,以便您可以使用其提供的類型和功能
▌練習 1
第一步是弄清楚如何解析單個消息。 定義函數
parseMessage :: String -> LogMessage
它從日誌文件中分析一行。 例如
parseMessage "E 2 562 help help"
== LogMessage (Error 2) 562 "help help"
parseMessage "I 29 la la la"
== LogMessage Info 29 "la la la"
parseMessage "This is not in the right format"
== Unknown "This is not in the right format"
一旦我們可以解析一條日誌消息,就可以解析整個日誌文件。 定義函數
parse :: String -> [LogMessage]
它立即解析整個日誌文件,並以 LogMessages
列表的形式返回其內容
要測試您的函數,請使用 Log
模塊中提供的 testParse
函數,將其作為參數提供您的 parse
函數,要解析的行數以及要從中解析的日誌文件(該文件也應與您的分配位於同一文件夾中) 。 例如,將您的作業加載到 GHCi 中後,在提示符下鍵入以下內容:
testParse parse 10 "error.log"
不要重新發明輪子! (上周是這樣。)使用 Prelude
函數可以使您的解決方案盡可能簡潔,高級和實用。 例如,要將 "562"
之類的字符串轉換為 Int
,可以使用 read
函數。 其他對您可能有用(或可能不有用)的功能包括 line, word, unwords, take, drop and (.)
▌將日誌按順序排列 / 整理日誌
不幸的是,由於錯誤消息是由遍布全球的多個位置的多台服務器生成的,包括雷電風暴,磁盤故障以及無聊而又無能的程序員,因此日誌消息嚴重混亂。 在我們進行一些整理之前,將無法弄清出什麼問題了! 我們設計了一種應該有用的數據結構 —— LogMessages
的二進制搜索樹:
data MessageTree = Leaf
| Node MessageTree LogMessage MessageTree
請注意,MessageTree
是一種遞歸數據類型: Node
構造函數本身以兩個子代作為參數,分別代表左右子樹以及一個 LogMessage
。 在此,Leaf
代表空樹
MessageTree
應該按時間戳排序:即,任何 Node
中 LogMessage
的時間戳都應大於左子樹中任何 LogMessage
的所有時間戳,而小於右子節點中任何 LogMessage
的所有時間戳
未知消息不應存儲在 MessageTree
中,因為它們沒有時間戳記
▌練習 2
定義函數
insert :: LogMessage -> MessageTree -> MessageTree
它將新的 LogMessage
插入現有的 MessageTree
中,從而產生一個新的 MessageTree
。 insert
可以假定已給它排序的 MessageTree
,並且除了原始 MessageTree
的內容之外,還必須產生一個包含新 LogMessage
的新排序的 MessageTree
但是,請注意,如果為 insert
提供了一個 Unknown
的 LogMessage
,則它應返回 MessageTree
不變
▌練習 3
一旦我們可以將單個 LogMessage
插入到 MessageTree
中,就可以從消息列表中構建完整的 MessageTree
。 具體來說,定義一個函數
build :: [LogMessage] -> MessageTree
通過依次將消息插入到 MessageTree
(從 Leaf
開頭)中,構建了一個包含列表中消息的 MessageTree
。
▌練習 4
最後,定義函數
inOrder :: MessageTree -> [LogMessage]
它接受一個已排序的 MessageTree
並生成其包含的所有 LogMessage
的列表,並按時間戳從最小到最大的順序進行排序。 (這被稱為 MessageTree
的有序遍歷。)
有了這些功能,我們現在可以刪除未知消息,並使用以下表達式對格式正確的消息進行排序:
inOrder (build tree)
【注意:有更好的方式對列表進行排序; 這只是讓您使用遞歸數據結構的一種練習!】
▌日誌文件驗屍
▌練習 5
現在我們可以對日誌消息進行排序,剩下要做的就是提取相關信息。 我們認為“相關”是指“嚴重程度至少為 50 的錯誤”。
寫一個函數
whatWentWrong :: [LogMessage] -> [String]
它採用 LogMessages
的未排序列表,並返回與嚴重性為 50 或更高的任何錯誤相對應的消息列表,並按時間戳排序。 (當然,您可以使用前面練習中的函數來進行排序。)
例如,假設我們的日誌文件如下所示:
I 6 Completed armadillo processing
I 1 Nothing to report
E 99 10 Flange failed!
I 4 Everything normal
I 11 Initiating self-destruct sequence
E 70 3 Way too many pickles
E 65 8 Bad pickle-flange interaction detected
W 5 Flange is due for a check-up
I 7 Out for lunch, back in two time steps
E 20 2 Too many pickles
I 9 Back from lunch
該文件作為 sample.log
提供。 有四個錯誤,其中三個嚴重程度大於 50。 sample.log
上 whatWentWrong
的輸出應為
[ "Way too many pickles"
, "Bad pickle-flange interaction detected"
, "Flange failed!"
]
您可以使用 log
模塊提供的 testWhatWentWrong
測試 whatWentWrong
函數。 您應該為您的解析函數,您的 whatWentWrong
函數以及要解析的日誌文件的名稱提供 testWhatWentWrong
。
▌Miscellaneous 雜項
- 我們將在我們給您的日誌文件之外的其他日誌文件上測試您的解決方案,因此無需進行硬編碼!
- 只要您輸入自己的解決方案,您就可以自由地(實際上是鼓勵)與您的任何同學討論作業。
▌Epilogue 結語 / 尾聲
▌練習6(可選)
由於各種原因,我們開始懷疑最近的混亂是由一個自私的黑客造成的。 你能弄清楚是誰做的嗎?