简体   繁体   中英

Haskell function sort input list then do stuff with sorted list

Short version:

I want to sort a list, and then perform operations on that sorted list that filter/extract data to form a new list, all in one function.

Long version:

I am teaching myself Haskell using these lessons . I'm currently on Homework 2 Exercise 5.

I am required to write a function whatWentWrong that takes an unsorted list of LogMessages and returns a list of Strings. The strings are the String portion of LogMessages that were constructed with Error in which the Error code is > 50. They are supposed to be sorted by the TimeStamp portion of LogMessage.

I have a function written for whatWentWrong that works, but it's really really slow (you'll see why).

whatWentWrong :: [LogMessage] -> [String]
whatWentWrong [] = []
whatWentWrong ys@((LogMessage (Error code) _ msg):xs)
    | ys /= inOrder (build ys)
      = whatWentWrong (inOrder (build ys))
    | code > 50
      = [msg] ++ whatWentWrong xs
    | otherwise
      = whatWentWrong xs
whatWentWrong (_:xs) = [] ++ whatWentWrong xs

The functions inOrder (build x) will return a sorted version of x (where x is a list of LogMessages). Obviously I have to either sort the list before I begin processing it with whatWentWrong , or I have to filter out all non relevant messages (Messages which are not Errors or which don't have Error codes above 50), sort, and then grab the strings from each one.

If I wasn't following this example, I would just define another function or something, or just send whatWentWrong an already sorted list. But I imagine there's some reason to do it this way (which I can't figure out).

Anyways, what I've done, and why the program is so slow is this: The line ys /= inOrder (build ys) is checking that the LogMessage list is sorted every single time it encounters a LogMessage that matches the Error pattern , even though, after the first time that check fails, the list is sorted for good.

That's the only way I could think to do it. Really, what I want to do it sort it once, but I have no idea how to make the function sort the list using my sorting functions and then not do that step ever again. I'm obviously not thinking about this correctly and any help is appreciated. Thanks.

You really just need a one-line list comprehension:

whatWentWrong xs = [ msg | (LogMessage (Error code) _ msg) <- inOrder (build xs), code > 50]

If you are sorting the list to see if the list is sorted, you may as well just work directly on the sorted list. Once you've done that, the list comprehension will automatically filter out the elements that don't pattern match, and the code > 50 filters the rest.


If you want to fix your current code as an exercise, you just need to define a helper function that assumes its input is sorted.

whatWentWrong :: [LogMessage] -> [String]
whatWentWrong ys = www (inOrder (build ys))
                   where www [] = []
                         www ((LogMessage (Error code) _ msg):xs) | code > 50 = msg : www xs
                                                                  | otherwise = www xs
                         www (_:xs) = www xs

However, you should recognize that www is the combination of a map and a filter .

whatWentWrong ys = map f $ filter p (inOrder (build ys))
                   where p (LogMessage (Error code) _ _) = code > 50
                         p _ = False
                         f (LogMessage _ _ msg) = msg

or, in point-free style

whatWentWrong = map f . filter p . inOrder . build
                where p (LogMessage (Error code) _ _) = code > 50
                      p _ = False
                      f (LogMessage _ _ msg) = msg

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM