8

(我使用的是“工作流程”这个词——不是异步工作流程的意义——而是“git 工作流程”的意义,也就是说,你如何将它用作你的开发的一部分)

在使用 F# 一段时间后,我开始开发我的第一个 F# 应用程序。我来自 c#/vb。在观看了各种演示/演讲(无论对错)后,我开始使用 fsi 作为主要的开发“引擎”并在该领域进行工作。如果我遇到需要调试的问题,我倾向于将有问题的函数分解成更小的部分并检查这些工作以尝试调试问题。

但是,为了使 fsi 中的代码量保持可管理性,一旦我对所做的工作感到满意,我会将其移动到 .fs 中并将 .fs 重新加载到 fsi 中。随着应用程序变得越来越大,这可能会开始变得有点笨拙,因为当我需要重构时,我最终不得不从 fs 文件中带回内容更改它运行的东西以使某些东西再次工作,然后再将代码推回进入 .fs 文件。此外,这种风格并不是真正的测试优先方法,因此我没有从构建一组测试中受益。(我也可能错过设置断点/步进代码的能力,在某些情况下,例如递归,诊断错误比分解函数的某些部分更快 - 尽管这可能在 VS11 中可用并且我没有设置对)..所以我想我'

我想知道其他人是否可以提供他们如何开发应用程序。您主要使用 fsi 还是从 tdd 开始。tdd 方法是否应该是主要的开发工具,而 FSI 是否应该更有选择性地用于帮助实现更复杂的算法、数据探索等

我看过这个问题,显然它有助于指向 F# 的各种 tdd 框架,但我仍然有兴趣了解经验丰富的 F# 开发人员的工作流程。

很多谢谢

小号

4

2 回答 2

12

我认为你在正确的轨道上。

开发过程是一个品味问题。无论如何,我会分享我的方法。

  • 从几个fs文件开始。每个文件代表一个模块,它由一组彼此密切相关的功能组成。它不必从一开始就精确;你经常在模块之间移动东西。
  • 准备好模块骨架后,创建一些fsx文件以进行快速测试。
  • 创建一个测试项目并设置 NuGet 包。我经常一起使用 NUnit 和FsUnit
  • 每当fsx脚本给出正确的结果时,将它们移至测试用例。反复这样做。
  • 将 a 包含Program.fs到主项目中并编译为可执行文件,以便在需要时进行调试。

一般来说,F# REPL 是主要的开发引擎。它为我提供即时反馈并允许增量更改,这对原型设计非常有帮助。在 F# 中,TDD 不太重要,因为错误率远低于其他语言。而且我不会测试所有东西,只关注主要功能并确保高测试覆盖率。使用testdriven.net插件或Visual Studio 2012 Premium 和 Ultimate可以为您提供有关测试覆盖率的有用统计信息。

使用 F# REPL 和 TDD,我几乎不必使用调试。每当出现错误行为时,我都会停下来思考。由于您的代码没有副作用,因此您可以轻松地对其进行推理。在很多时候推理和一些打印命令可以给我正确的答案。

您可以在 F# REPL 中通过UnquoteFsCheck使用 TDD 。前者通过报价提供测试,这令人印象深刻。后者使用随机测试方法,这在处理代码的极端情况时很有吸引力。当您的程序必须满足某些属性时,我发现它非常有用。但是,学习正确使用这些框架可能需要一些时间。

于 2013-04-01T09:16:42.750 回答
3

pad给出了一个很好的答案,对于 F# 的新手来说非常实用和有用。我将给出不同的方法,这样其他人就不会认为 F#'ers 只有一种方法。

注意:如果您对编程非常陌生,请坚持使用pad的答案,这对新程序员来说要好得多。

面向对象的世界中,人们用对象来思考,在这样的语言中,我会从在纸上写下对象开始,并使用各种图表,如用例状态转换序列图等,直到我觉得我已经足够开始了在 C# cs 文件中创建对象,用方法、属性、事件等充实对象。

在函数世界中,我通常从抽象概念开始,并将它们转换为F# fs 文件中的可区分联合(DU),跳过使用REPL,即F# Interactive,然后开始添加一些函数。在我拥有一些功能后,我通过NuGet使用NUnitFsUnit设置了一个测试项目。由于 DU 是抽象的,测试用例通常更难编写,因此我为 DU 创建打印机,然后将它们插入到测试用例中,我在 NUnit 工具中捕获打印机输出的结果,以便剪切并粘贴回测试用例根据需要进行更改。有关为什么我不从头开始编写它们的示例,请参阅这些示例。

一旦我完成了抽象 DU,我就可以转到代码将人/具体形式转换为抽象 DU,并将抽象 DU 转换为人/具体形式。在某些情况下,这些将是解析器漂亮的打印机

我要说明的要点是,我不关注我使用的工具,而是关注问题的抽象概念,并在需要时引入工具。

我会注意到我也在PROLOG中编程,并且我确实从 REPL 开始,并在逻辑工作后将代码移动到存储中。所以我不反对使用 REPL,它只是解决问题的另一种方式。

编辑

根据Ken的请求,举个例子。

请参阅:Discriminate Unions (F#)并查找该部分
Using Discriminated Unions Instead of Object Hierarchies

因此,不是具有继承类型的圆形、等边三角形、正方形和矩形的基本形状类型,而是创建一个可区分的联合,如前所述:

type Shape =
| Circle of float
| EquilateralTriangle of double
| Square of double
| Rectangle of double * double

由于您的问题会提出一个更好的独立问题,并获得比我所能提供的更详细的答案,因此我建议您提出这个问题。

此外,如果您搜索这方面的信息,也可以使用以下替换来搜索受歧视的联合 (DU):

于 2013-04-04T14:05:09.917 回答