bubblesort2 :: (Ord a, Show a) => [a] -> [a]
bubblesort2 [] = []
bubblesort2 [x] = [x]
bubblesort2 (x:y:rest) =
bubblesort2 (init bubbled) ++ [last bubbled]
where
(first,second) = if x > y then (y,x) else (x,y)
bubbled = first : bubblesort2(second:rest)
我正在尝试理解上面的haskell代码。我试图在intellij,jetbrains haskell插件中调试代码,但由于某种原因它会抛出调试执行错误。有什么好方法可以通过ide进行调试。通过gchi正常调试似乎太复杂了。
答案 0 :(得分:11)
FWIW,似乎是一种常见的体验,如果你来自面向对象的背景,你会发现你需要一个较少的调试器来进行函数式编程。我不知道这是不是你的背景,但那是我的旅程。在我写过Haskell代码的几年内,我从未研究过如何调试它。
我有时必须调试F#代码,但只有当它与.NET的面向对象部分交互时才会发生。
调试器允许您通过计算检查各个阶段的变量的内部状态。当代码涉及可变状态时,这是有道理的,但当所有内容都是不可变的,并且当表达式是引用透明时,它变得不那么重要。
当我不理解一段Haskell代码时,我通常会做的就是我开始分解它并使用GHCi中的各种子表达式。在这个特定示例中,您可以执行以下操作。
首先,它希望清楚当输入为[]
或[x]
时会发生什么:
Prelude> bubblesort2 []
[]
Prelude> bubblesort2 [42]
[42]
我假设你想要理解的部分代码是bubblesort2 (x:y:rest)
。那么,我要做的就是从[]
和[42]
继续前进到下一个最简单的案例,在这种情况下你只有两个值:
Prelude> bubblesort2 [1337,42]
[42,1337]
这对应于bubblesort2 (x:y:rest)
个案,其中:
Prelude> x = 1337
Prelude> y = 42
Prelude> rest = []
请注意,我只是将值绑定到GHCi中的符号x
,y
和rest
。这使您可以评估函数中where
块中的第一个表达式:
Prelude> (first,second) = if x > y then (y,x) else (x,y)
Prelude> first
42
Prelude> second
1337
您可以做的下一件事是运行bubblesort2(second:rest)
子表达式:
Prelude> bubblesort2(second:rest)
[1337]
如果您需要提醒一下结果的原因,您甚至可以检查second
,rest
和second:rest
:
Prelude> second
1337
Prelude> rest
[]
Prelude> second:rest
[1337]
迟早,您可能会意识到这是bubblesort2 [x]
案例,这就是bubblesort2(second:rest)
返回[1337]
的原因。您现在应该知道bubbled
是什么,但除此之外,您也可以评估它:
Prelude> bubbled = first : bubblesort2(second:rest)
Prelude> bubbled
[42,1337]
继续,您现在可以开始分解bubblesort2
的主体。首先,例如:
Prelude> [last bubbled]
[1337]
和
Prelude> init bubbled
[42]
所以,bubblesort2 (init bubbled)
再次匹配bubblesort2 [x]
案例,以便获得:
Prelude> bubblesort2 (init bubbled)
[42]
最后:
Prelude> bubblesort2 (init bubbled) ++ [last bubbled]
[42,1337]
通过这些步骤可以让您了解列表中恰好有两个元素的情况。一旦这对您有用,您可以继续将GHCi中的值重新绑定到例如完成案例[1337, 42, 12345]
:
Prelude> x = 1337
Prelude> y = 42
Prelude> rest = [12345]
Prelude> (x:y:rest)
[1337,42,12345]
我不打算引导您完成此案例,但我希望您能够以与上述相同的方式清楚地了解它。
我想我知道你会说什么:
每次调试时都必须这样做吗?
根据我的经验,当你开始使用Haskell或其他函数式编程语言时,有很多你不了解的内容,而且你觉得需要达到经常调试。
那只是一个阶段,它会过去。
在REPL中使用代码是一种更惯用的函数式编程方法,一旦你习惯了它,你将总是打开一个REPL。对于Haskell和F#来说,这对我来说都是如此,我听过其他功能程序员也这么说。对于Clojure程序员来说似乎也是如此。
要清楚,现在,我很少觉得需要在上面概述的详细级别上逐步介绍Haskell代码。通常情况下,只有一两个表达式我觉得很难理解,然后我就把它隔离开来并在GHCi中使用它,直到我理解了它的作用。
我认为GHCi中的工作将使您对Haskell有一个更好的长期理解,而不是试图让调试器工作。