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进行的正常调试似乎太复杂了。
发布于 2018-01-12 08:40:47
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 = []注意,我只是将值绑定到x、y和GHCi中的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,而不是尝试让调试器工作。
https://stackoverflow.com/questions/48208827
复制相似问题