F# 交互式开发

3

如果您来自Matlab和R背景,那么开发过程通常是非常交互式的(选择、运行所选内容、修复、选择、运行所选内容、修复等等)。我正在尝试弄清楚F#如何处理这种开发风格,因为这在科学应用程序中似乎非常重要。以下是对于新手来说立即想到的一些事情:

  1. Selecting multiple lines gives different results than one line at a time.

    let add x y = x + y
    add 4.1 2.3
    

    Selecting both lines results in float -> float -> float whereas selecting the first line results in int -> int -> int. More generally, matlab/R users are used to results printing out after each statement, not at the end.

  2. Shadow copying can become burdensome.

    let file = open2GBfile('file.txt')
    process file
    

    If you run this interactively over and over again, the 2GB file is shadow copied and you will quickly run out of memory. Making file mutable doesn't seem like the appropriate solution, since the final run of the program will never change it.

鉴于这些问题,基于 fsi.exe 的系统是否不可能支持类似 matlab/R 的交互式开发?
[编辑:我猜想问题2.如果对象被遮蔽,它们是否会被立即标记为删除?]
2个回答

4
我不认为F#可以直接替代Matlab/R,因为与它们不同,F#是一种通用编程语言。并非您需要的特定类型的所有内容都在标准库中。但这并不意味着您所描述的“交互式开发”是不可能的,只是可能需要一些前期努力来构建所依赖的库函数。
对于#1,正如之前提到的那样,在某些情况下不幸地需要添加类型注释,但是inline关键字和“帽子类型”可以提供鸭子类型
对于#2,我不清楚您的openprocess函数的作用与您想要它们做什么有何区别。例如,open函数可以:
  • 一次性读取整个文件,将数据作为数组/列表等返回,然后关闭文件
  • 返回一个FileStream对象,在其上调用process但忘记关闭。
  • 返回一个序列表达式,以便您可以惰性地迭代文件内容
  • Memoize其中一个上述结果,以便随后的调用只返回缓存的结果
  • 其他无数创建文件访问抽象化的方法之一。

其中有些比其他更适合您的任务。与Matlab和R相比,像F#这样的通用语言给您提供了更多自毁的方式。但这是因为它提供了更多的做事方式。


我的意思是以Matlab/R的方式打开。它会打开一个csv文件,并将其放入一个大的数组/矩阵中。我的担心是,如果我再次选择并运行该代码(这在交互式开发风格中经常发生),那么我将有两个大矩阵,其中一个是影子复制。我是正确的吗? - Tristan
@Tristan:.Net垃圾回收应该会清除你所说的额外“影子副本”。原帖听起来像是你已经尝试过这个方法,但出现了问题。现在,你似乎只是猜测这可能会成为一个问题。你能澄清一下吗? - John Fisher
我刚在fsi.exe中进行了一个简单的实验,分配(非零)数组并观察内存使用情况,Tristan似乎是正确的,如果没有使用mutable或ref-cell,则内存永远不会被释放。但是再次写入额外的mutableref在你的let声明中,然后在后续赋值上使用<-或者:=,在我看来并不是太烦人。 - Gabriel
@Gabiel:如果您确实正在更改x,那么使其可变可能是正确的策略。但是在这里,我考虑在fsi中多次运行相同的代码,以便您调试它。这通常发生在matlab样式的开发中。如果您的对象很小,那么这不会太重要。但是,如果您加载了一个大矩阵五次,事情可能会很快变得混乱。在实际代码中从不对其进行突变,因此使用“mutable”没有意义。 - Tristan
@Tristan:是的,这很有道理,但当你说“实际代码”时,这种区分不是 fsi.exe 理解的。除非修改 fsi 使其不保留对“遮蔽”对象的引用(提供源代码),否则最好重启 fsi 并维护包含“启动”代码的源文件以在每次重启时运行。当在 fsi 中尝试非平凡事物时,我发现自己经常这样做。这往往会导致干净而模块化的代码,并使您更轻松地记录您所做的事情(以及哪些有效),以便稍后重新跟踪您的步骤。希望有所帮助。 - Gabriel
显示剩余3条评论

1

至#1

在FSI中,您需要在每个语句的末尾键入;;并直接获取结果:

> 1 + 2;;
val it : int = 3

一般来说,F# 代码文件应该被视为一个包含多个单独函数的集合,你需要交互式地调用和评估这些函数,而不是一系列生成要显示值的步骤。

至 #2:

这似乎是代码本身的问题:将 file 设为一个函数,这样读取/复制只会在真正需要时和地方进行(否则 let 绑定会在开头被评估)。


问题在于这会改变类型推断。 - Tristan
1
在无法避免的情况下,请明确注释类型。 - Dario
请注意,这是 F# 类型系统的一个缺陷。在交互式 Haskell (GHCi) 中,情况要好得多,因为类型系统可以使用类型类来概括这样的函数。 - Dario

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接