在 Scala 中调试功能代码
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4272797/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Debugging functional code in Scala
提问by Adrian
Debugging functional code is definitely more tricky than debugging imperative code. See discussions here, hereand here. "Functional" debugging should support inspecting the return value of functions/closures/monads. Do any debuggers/IDEs have (plan to have) the ability to inspect intermediate return values?
调试函数式代码肯定比调试命令式代码更棘手。请参阅此处、此处和此处的讨论。“函数式”调试应该支持检查函数/闭包/单子的返回值。是否有任何调试器/IDE 具有(计划具有)检查中间返回值的能力?
For example, to debug this line in Scala, I should be able to step through 4 function invocations and inspect the returned value at each step before returning r
例如,要在 Scala 中调试这一行,我应该能够单步执行 4 次函数调用并在返回之前检查每一步的返回值 r
val r=(ls filter (_>1) sort (_<_) zipWithIndex) filter {v=>(v._2)%2==0} map{_._1}
回答by Adam Rabung
I think everybody's advice to break this thing down to more manageable chunks is the best approach. One trick for debugging smaller expressions is to steal Ruby's tapfunction, as described here. "tap" allows you to stick an expression in the middle of a chain like this, and perhaps print out some debug values, like so:
我认为每个人的建议是将这件事分解为更易于管理的块是最好的方法。用于调试较小的表情一招是窃取Ruby的龙头作用,如所描述这里。“tap”允许您像这样在链的中间粘贴一个表达式,并且可能会打印出一些调试值,如下所示:
val ls = List(1,2,3).map(_ * 2)
.tap(soFar => println("So far: " + soFar))
.map(_ * 2)
println(ls)
This will print out:
这将打印出:
So far: List(2, 4, 6)
List(4, 8, 12)
到目前为止: List(2, 4, 6)
List(4, 8, 12)
It helps me every once in a while.
它时不时地帮助我。
回答by Apocalisp
In a purely functional setting, stepping through is not nearly as useful as you might think. Since everything is composed of pure functions, you can just test those pieces individually using a process of elimination. In a lazy evaluation setting, stepping through code is even less useful.
在纯粹的功能设置中,单步执行并不像您想象的那么有用。由于一切都由纯函数组成,因此您可以使用消除过程单独测试这些部分。在惰性求值设置中,单步执行代码的用处更小。
Debugging programs in Haskell, for example, you would not at all be interested in tracing function calls. What you are interested in is a trace of intermediate function return values. It would be a very useful feature in any functional language to be able to give such a trace for any expression.
例如,在 Haskell 中调试程序,您根本不会对跟踪函数调用感兴趣。您感兴趣的是中间函数返回值的踪迹。在任何函数式语言中,能够为任何表达式提供这样的跟踪将是一个非常有用的特性。
回答by Synesso
I know being concise is very nice, and I agree with you that IDEs should help with the debugging in these situations. But for the time being I have changed my coding style to assist with debugging. In my personal style I would have implemented your example as:
我知道简洁非常好,我同意您的观点,IDE 应该有助于在这些情况下进行调试。但目前我已经改变了我的编码风格来帮助调试。以我的个人风格,我会将您的示例实现为:
val noZeroLs = ls.filter(_>1)
val sortedLs = noZeroLs.sort(_<_)
val indexedNoZeroLs = sortedLs.zipWithIndex
val everySecondIndexedL = indexedNoZeroLs.filter(v => (v._2) % 2 == 0)
val everySecondL = everySecondIndexedL.map(_._1)
Coming up with meaningful names is difficult/laborious, but it does help you identify silly bugs; may help others to understand what is going on; and definitely helps with debugging.
想出有意义的名称很困难/费力,但它确实可以帮助您识别愚蠢的错误;可以帮助他人了解正在发生的事情;并且绝对有助于调试。
回答by fedesilva
My approach to this problem is to break the expression down into parts binding the results to vals in the REPL. When I am satisfied I may even write a test case that does the same I did in the REPL so that I am sure things stay as I want and so that I or somebody else can come back later and see a more explicit version.
我解决这个问题的方法是将表达式分解为将结果绑定到 REPL 中的值的部分。当我感到满意时,我什至可以编写一个测试用例,它的功能与我在 REPL 中所做的相同,这样我就可以确定事情会按我的意愿进行,这样我或其他人可以稍后回来查看更明确的版本。
The ability to use the repl to explore coupled with nice and easy to use testing toolkits has made debuggers all but obsolete for me.
使用 repl 进行探索的能力加上漂亮且易于使用的测试工具包,这使得调试器对我来说几乎过时了。
Of course YMMV.
当然是YMMV。
回答by Grigory Kislin
It is helpful when you try to debug your own code, but during debugging scala lang or other libs you cannot change code:(
当您尝试调试自己的代码时很有帮助,但在调试 scala lang 或其他库时,您无法更改代码:(
回答by Michael Lafayette
If you don't have an IDE, you can still use this tool I wrote:
如果你没有IDE,你仍然可以使用我写的这个工具:
https://github.com/JohnReedLOL/scala-trace-debug
https://github.com/JohnReedLOL/scala-trace-debug
To print the intermediary values, you can take this example:
要打印中间值,您可以使用以下示例:
val result=(lists.filter(_>1).sort(_<_).zipWithIndex).filter{v=>(v._2)%2==0}.map{_._1}
And add traces to it:
并添加痕迹:
import scala.trace.implicitlyTraceable
val result=(lists.filter(_>1).out.sort(_<_).println.zipWithIndex).filter{v=>(v._2)%2==0}.out.map{_._1}.out
An implicit conversion allows you to print.
隐式转换允许您打印。
回答by Xavier Guihot
Starting Scala 2.13, the chaining operation tap, as mentioned in Adam Rabung's answer, has been included in the standard library, and can be used for debugging by printing intermediate versions of a pipeline:
开始Scala 2.13,tap如Adam Rabung 的回答中提到的链接操作已包含在标准库中,并且可以通过打印管道的中间版本用于调试:
import scala.util.chaining._
scala> val ls = List(1,2,3).map(_ * 2).tap(println).map(_ * 2).tap(println)
List(2, 4, 6)
List(4, 8, 12)
ls: List[Int] = List(4, 8, 12)
The tapchaining operation applies a side effect (in this case println) on a value (in this case a List) while returning the original value:
的tap链接操作适用的副作用(在这种情况下println上的值)(在这种情况下List),而返回原始值:
def tap[U](f: (A) => U): A
def tap[U](f: (A) => U): A

