我在学习一本使用F#进行机器学习的书。我正在将F#代码转换为C#,并按照教程进行操作,因为我对F#的理解不是很深。我在代码的一部分上真的卡住了,想知道是否有人可以告诉我如何用C#编写这段代码。即使只是解释F#中的代码是做什么的也很有帮助。我卡住的代码是
let tokenScore (group:DocsGroup) (token:Token) = if group.TokenFrequencies.ContainsKey token then log group.TokenFrequencies.[token] else 0.0let score (document:TokenizedDoc) (group:DocsGroup) = let scoreToken = tokenScore group log group.Proportion + (document |> Seq.sumBy scoreToken)
我真的在以下部分卡住了
let scoreToken = tokenScore group log group.Proportion + (document |> Seq.sumBy scoreToken)
这是《.Net开发者机器学习项目》一书的第2章教程。如果你能帮我,那真是太好了。提前感谢
回答:
F#函数是柯里化的。这意味着如果一个函数声明了两个参数,而你只用一个参数调用它,结果将是另一个只接受一个参数的函数。以下是一个例子:
let add x y = x + ylet n = add 2 3 // 现在n的值为5let addFive = add 5 // 这是一个接受一个参数的*函数*let altAddFive y = add 5 y // 另一种定义相同内容的方式let result = addFive 3 // 现在result的值为8let altResult = altAddFive 3 // 这个也值为8
所以在score
函数中,名称scoreToken
现在指的是一个接受一个参数的函数,然后用两个参数调用tokenScore
:group
和刚刚传递的值。换句话说,scoreToken
的定义可以写成这样:
let scoreToken token = tokenScore group token
那将是相同的意思。
现在来看下一行。首先,log
是自然对数函数,通常在数学符号中写成ln
。它接受一个浮点数,并返回一个浮点数。F#中的运算符优先级规则是函数调用的优先级高于运算符,因此log 3.0 + 5.0
将被解释为(log 3.0) + 5.0
,这与log 8.0
的值不同。因此,以下行:
log group.Proportion +(document |> Seq.sumBy scoreToken)
相当于这样:
let score1 = log group.Proportionlet score2 = document |> Seq.sumBy scoreTokenscore1 + score2
最后,document |> Seq.sumBy scoreToken
这一行也可能让你感到困惑。它有两个部分。首先,Seq.sumBy
相当于LINQ Sum
方法的两个参数版本,但F#中的参数顺序与C#中的相反。F#的Seq.sumBy
函数接受两个参数;首先是应应用于每个元素以产生值的函数,其次是元素序列。即,如果你不使用|>
运算符调用它,它将看起来像这样:
Seq.sumBy scoreToken document
这将遍历document
序列(类型为TokenizedDoc
,根据其使用方式,显然可以被视为一个标记序列)。对于文档中的每个标记,将调用scoreToken
函数(如果你还记得,调用scoreToken t
,其中t
是某个标记,等同于调用tokenScore group t
)。该调用将产生一个浮点值。最后,所有浮点值将被加在一起以产生最终得分。
然而,在F#中,当对序列求和或进行类似操作时,传统上使用“管道”运算符|>
:document |> Seq.sumBy scoreToken
完全等同于Seq.sumBy scoreToken document
。|>
运算符在另一个SO问题的答案中得到了很好的解释,所以我在这里不会重复那个答案。
所以这个函数所做的就是取group.Proportion
值的自然对数,并将其与文档中每个标记的得分相加。这个加法的结果是函数中的最后一个表达式,因此这就是函数的结果:在F#中,与C#不同,你不需要在函数末尾输入return
。(F#中的return
关键字有不同的含义,我现在不会深入讨论,因为那是一个完全不同的主题)。