F#的主张之一是它允许交互式脚本编写和数据操纵/探索。我一直在玩F#,试图了解它与Matlab和R进行数据分析工作的比较。显然,F#并没有这些生态系统的所有实用功能,但是我对基础语言的一般优点/缺点更感兴趣。

对我来说,即使是在功能样式上,最大的变化是F#是静态键入的。这有一定的吸引力,但通常也感觉像是一件直线外套。例如,我还没有找到一种处理异类矩形数据的便捷方法-想想R中的数据框。假设我正在读取一个名称(字符串)和权重(浮点数)的CSV文件。通常,我加载数据,执行一些转换,添加变量等,然后运行分析。在R中,第一部分可能看起来像:

df <- read.csv('weights.csv')
df$logweight <- log(df$weight)

在F#中,尚不清楚应使用哪种结构来执行此操作。据我所知,我有两个选择:1)我可以首先定义一个强类型的类(Expert F#9.10)或2)我可以使用一个异构容器,例如ArrayList。静态类型的类似乎不可行,因为我需要在加载数据后以即席方式(对数权重)添加变量。异构容器也很不方便,因为每次访问变量时,都需要将其拆箱。在F#中:
let df = readCsv("weights.csv")
df.["logweight"] = log(double df.["weight"])

如果这是一次或两次,可能没问题,但是每次使用变量时都指定类型似乎并不合理。我经常处理带有100个变量的调查,这些变量被添加/删除,划分为新的子集并与其他数据框合并。

我是否缺少一些明显的第三选择?是否存在一些有趣且轻松的方式来交互和处理异构数据?如果我需要在.Net上进行数据分析,则目前的感觉是我应该将IronPython用于所有数据探索/转换/交互工作,而仅将F#/C#用于数字密集型零件。 F#本质上是错误的工具,无法快速而脏乱地处理异构数据吗?

最佳答案

我认为还有其他一些选择。
(?)运算符

如Brian所述,您可以使用(?)运算符:

type dict<'a,'b> = System.Collections.Generic.Dictionary<'a,'b>

let (?) (d:dict<_,_>) key = unbox d.[key]
let (?<-) (d:dict<_,_>) key value = d.[key] <- box value

let df = new dict<string,obj>()
df?weight <- 50.
df?logWeight <- log(df?weight)

这确实在每次访问时都使用装箱/拆箱,有时您可能需要添加类型注释:
(* need annotation here, since we could try to unbox at any type *)
let fltVal = (df?logWeight : float)

顶级标识符

另一种可能性是,您可以仅使用顶级标识符,而不是动态定义现有对象的属性(F#对此不特别支持)。
let dfLogWeight = log(dfWeight)

这样做的好处是,尽管它可能会使您的顶级 namespace 困惑,但几乎不需要指定类型。

属性对象

最后一个需要更多类型输入和更丑陋语法的选项是创建强类型“属性对象”:
type 'a property = System.Collections.Generic.Dictionary<obj,'a>

let createProp() : property<'a> = new property<'a>()
let getProp o (prop:property<'a>) : 'a = prop.[o]
let setProp o (prop:property<'a>) (value:'a) = prop.[o] <- value

let df = new obj()
let (weight : property<double>) = createProp()
let (logWeight : property<double>) = createProp()

setProp df weight 50.
setProp df logWeight (getProp df weight)
let fltVal = getProp df logWeight

这要求显式创建每个属性(并且此时需要类型注释),但是此后不需要任何类型注释。我发现这比其他选项的可读性差得多,尽管也许定义一个运算符来替换getProp会有所缓解。

10-07 14:22
查看更多