批评这个深夜,noob Haskell代码

Cha*_*ers 7 sorting haskell functional-programming

我正在通过Real World Haskell工作,目前正在进行第3章末尾的练习.

我采取了一种不同寻常的方法:尽管我知道有些语言功能尚未涵盖,但这对我有帮助,我只是尝试使用他们明确涵盖的内容进行这些练习.为什么?真的很有趣.感觉它强迫我给我的大脑一些额外的递归练习.

所以我刚刚完成了如下所示的练习:"创建一个函数,根据每个子列表的长度对列表列表进行排序.(您可能需要查看Data.List模块中的sortBy函数.)"

现在,他们提出了关于Data.List模块的提示.但是他们没有说出可以找到参考文档的地方,关于如何导入东西等等.所以我决定推出自己的排序,看看我是否可以做到.我使用冒泡排序,因为它是最简单的算法.

结果如下.我想让你的Haskell专家对它进行批评......但是请注意以下几点: 如果你建议改进,请将它们建立在真实世界Haskell第3章所涵盖的语言特性之上(或者你猜测那些特性)可能没有找到它的麻烦).我知道有很多令人敬畏的语言功能等着我,这将让我更好地编写代码,但是现在具体的挑战是使用到目前为止涵盖的"原始"功能来实现.

我确定有些情况下我会绕到我的肩膀以划伤我的肘部,当递归和模式匹配可以为我做更多事情时,我使用显式控制流的情况等等.我确信代码可以是更短,更可读.我打赌有一些我不知道的好习惯,可以用于我限制自己的原始语言功能.这些都是我喜欢的提示.

这可能是我用任何语言引以为傲的最丑陋的代码(至少,我记得).我用函数式语言首次尝试了"Hello,world"类型的东西.现在你要打败它了:).请温柔,但我期待着一些有趣的见解.谢谢.

areListsEqual :: (Eq a) => [a] -> [a] -> Bool

areListsEqual [] [] = True
areListsEqual [] _  = False
areListsEqual _ []  = False

areListsEqual xs ys = (head xs == head ys)  && (areListsEqual (tail xs) (tail ys))

charlieSort :: (Eq a) => [[a]] -> [[a]]

charlieSort [] = []
charlieSort (x:xs) | null xs = [x]
charlieSort xs | (length xs) >= 2 = if(not (areListsEqual xs wip))
                    then charlieSort wip 
                    else wip
                    where
                      first = head xs
                      second = head (tail xs)
                      theRest = drop 2 xs
                      swapPairIfNeeded a b = if(length a >= length b) 
                          then [second, first]
                          else [first, second]
                      modifiedPair = swapPairIfNeeded first second
                      wip = (take 1 modifiedPair) ++ charlieSort ( (drop 1 modifiedPair) ++ theRest)
Run Code Online (Sandbox Code Playgroud)

小智 6

我首先要开始使用模式匹配.

areListsEqual :: Eq a => [a] -> [a] -> Bool
areListsEqual [    ] [    ] = True
areListsEqual [    ] _      = False
areListsEqual _      [    ] = False
areListsEqual (x:xs) (y:ys) = x == y && areListsEqual xs ys
Run Code Online (Sandbox Code Playgroud)

注意这是多么可读,何时headtail避免.

charlieSort :: Eq a => [[a]] -> [[a]]
charlieSort    [                    ] = []
charlieSort    [x                   ] = [x]
charlieSort xs@(first:second:theRest)
  | areListsEqual xs wip              = wip
  | otherwise                         = charlieSort wip
  where
  swapPairIfNeeded a b
    | length a >= length b = [second,first]
    | otherwise            = [first,second]
  modifiedPair = swapPairIfNeeded first second
  wip = take 1 modifiedPair ++ charlieSort (drop 1 modifiedPair ++ theRest)
Run Code Online (Sandbox Code Playgroud)

我改变了if- then- else到后卫略有改善可读性(因人而异).取而代之的检查列表有一个电话至少两个元素,length我们使用模式匹配,这也让我们的名字 first,second,theRest直接.该name @ pattern模式既对输入匹配pattern 名称整个输入作为name.

现在,我想避免使用takedrop提取两个元素modifiedPair,所以最后两行被改为

  [shorter,longer] = swapPairIfNeeded first second
  wip = [shorter] ++ charlieSort ([longer] ++ theRest)
Run Code Online (Sandbox Code Playgroud)

你可以写最后一行的地方

  wip = shorter : charlieSort (longer : theRest)
Run Code Online (Sandbox Code Playgroud)

如果你愿意的话.但是,为什么要swapPairIfNeeded返回shorterlongerfirstsecond列表的列表?为什么不使用像这样的一对

  swapPairIfNeeded a b
    | length a >= length b = (second,first)
    | otherwise            = (first,second)
  (shorter,longer) = swapPairIfNeeded first second
Run Code Online (Sandbox Code Playgroud)

?在大多数情况下,最好将元组用于固定数量的值(可能是不同类型),并将列表用于可变数量的值(必须具有相同类型).但似乎奇怪的是, swapPairIfNeeded相比较它的参数ab,但随后返回 firstsecond无妨.在这种情况下,我将完全删除而不是让它返回ab成对出现swapPairIfNeeded:

  (shorter,longer)
    | length first >= length second = (second,first)
    | otherwise                     = (first,second)
Run Code Online (Sandbox Code Playgroud)

"展开"的身体swapPairIfNeeded进入了定义 (shorter,longer).

所以现在的代码charlieSort看起来像

charlieSort :: Eq a => [[a]] -> [[a]]
charlieSort    [                    ] = []
charlieSort    [x                   ] = [x]
charlieSort xs@(first:second:theRest)
  | areListsEqual xs wip              = wip
  | otherwise                         = charlieSort wip
  where
  (shorter,longer)
    | length first >= length second = (second,first)
    | otherwise                     = (first,second)
  wip = shorter : charlieSort (longer : theRest)
Run Code Online (Sandbox Code Playgroud)

最后,我应该注意到它charlieSort并没有真正实现冒泡排序,因为递归调用charlieSort不仅会使列表中的一个"冒泡"传递,而且还会对列表进行完全排序 longer : theRest,以便所有必须完成的操作在这个递归调用之后(在返回一个"升级"之前)可能会渗透shorter到其合法的位置.