Haskell函数排序输入列表然后用排序列表做东西

时间:2016-09-27 19:56:49

标签: list sorting haskell

简短版本:

我想对列表进行排序,然后对该排序列表执行操作,过滤/提取数据以形成新列表,所有操作都在一个函数中。

长版:

我正在使用these lessons教自己Haskell。我目前正在Homework 2练习5。

我需要编写一个函数whatWentWrong,它接受​​一个未排序的 LogMessages列表并返回一个字符串列表。字符串是LogMessages的String部分,它是用Error构造的,其中Error代码是> 50.它们应该按LogMessage的TimeStamp部分排序。

我为whatWentWrong编写了一个可行的函数,但它确实很慢(你会明白为什么)。

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

函数inOrder (build x)将返回x的排序版本(其中x是LogMessages列表)。显然我必须在开始使用whatWentWrong处理列表之前对列表进行排序,或者我必须过滤掉所有不相关的消息(非错误的消息或者错误代码不超过50的消息),排序,然后从每个字符串中抓取字符串。

如果我没有关注这个例子,我只想定义另一个函数或者其他东西,或者只发送whatWentWrong已排序的列表。但我想有一些理由这样做(我无法弄清楚)。

无论如何,我做了什么,为什么程序这么慢是这样的: 第ys /= inOrder (build ys)行检查LogMessage列表每次遇到与错误模式匹配的LogMessage时都会按排序,即使在第一次检查失败后,列表也已排序好的。

这是我能想到的唯一方法。真的,我想要做的就是对它进行一次排序,但是我不知道如何使用我的排序函数对该列表进行排序,然后又不再执行该步骤。我显然没有正确地考虑这一点,任何帮助都表示赞赏。感谢。

1 个答案:

答案 0 :(得分:3)

你真的需要一个单行列表理解:

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

如果要对列表进行排序以查看列表是否已排序,您也可以直接在排序列表上进行操作。完成后,列表推导将自动过滤掉未匹配模式的元素,code > 50过滤其余元素。

如果您想将当前代码修复为练习,您只需要定义一个辅助函数,该函数假定其输入已排序。

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

但是,您应该认识到www是组合 mapfilter

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

或者,无点样式

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