因此,我有这4个例子,其中3个是从这个youtube视频得到的。
我刚上了一门关于函数式编程的课程(在Racket中),如果我对F#的基本理解是正确的话,我就是这样做的。
let data = [1.;2.;3.;4.]
let sqr x = x * x
// Bad, very bad
let sumOfSquareI nums =
let mutable acc = 0.0
for x in nums do
acc <- acc + sqr x
acc
// Better than above, but may cause stack overflow with very long list
let rec sumOfSquareF1 nums =
match nums with
| [] -> 0.0
| h::t -> sqr h + sumOfSquareF1 t
// much much better, uses tail-recursion, no stack overflow
let sumOfSquareF2 nums =
let rec sumOfSquareLocal nums acc =
match nums with
| [] -> acc
| h::t -> sumOfSquareLocal t (acc + sqr h)
sumOfSquareLocal nums 0.0
// seems to be idiomatic F#, but is it better than tail-recursive version?
let sumOfSquare nums =
nums
|> Seq.map sqr
|> Seq.sum
sumOfSquare data
sumOfSquareI data
sumOfSquareF1 data
sumOfSquareF2 data后两个函数之间有什么真正的区别吗?一个比另一个好吗?在用F#编写函数代码时,我应该多久使用一次|>操作符(这对我来说是全新的)?
另一件事是,在F#中创建列表有不同的方法吗?我从视频中提取的东西看上去像是...clunky。
发布于 2016-04-21 15:04:43
是的,与显式递归相比,您应该更喜欢在Seq和List模块中使用高阶函数。您也可以使用sumBy
let sumOfSquare nums = nums |> Seq.sumBy sqr发布于 2016-04-21 15:40:27
关于编程风格或范式的讨论很容易转化为基于意见的推理。让我们来看看现实世界中对每个函数的真正意义。
版本1:命令式
这个花了五行零钱。几乎每个人都能读到。“状态机”是一个单变量,易于验证。它不是通用的,但是LanguagePrimitives.GenericZero而不是0.0可以修复这个问题,尽管代价是要花费一定的时间。
与其他人相比,我不认为这是“坏的,非常坏的”。除非你把编程变成一种邪教,在那里,必须用神圣的功能真理来摧毁强制的离经叛道。不过,这也不是最小的。
版本2:递归计算
这是相当可读的,使用一行较少。同样,不是泛型的,但是泛型零可以解决这个问题。但是,此函数将导致堆栈增长!有人使用它会看到它在第一次测试中运行良好,直到突然,性能直线下降或整个程序崩溃!
我认为这是客观上提出的最糟糕的解决方案!您不希望在生产代码中出现这种情况!
版本3:嵌套尾递归函数
同样,这也可以成为通用的。这个功能很好,但是它有6行,我不认为其中的三行是琐碎的。
这是纯粹的,但老实说,如果我必须在这个和命令变体之间进行选择,我就不太确定了。
版本4:调用高阶函数
简短可读的,通用的,没有使用奇怪的原语!你为什么要怀疑这是四人中的赢家?
额外版本:完全没有
高阶函数在这方面工作得很好,这并不是最后的结果。Seq.sumBy sqr (我看到李在他的回答中击败了我)做了这项工作。因为在生产代码中,这仅仅比sumOfSquares长,所以可能根本没有理由定义额外的函数。
可读性是关键
当您询问何时使用管道操作符等方面的建议时,答案必须针对将要阅读代码的人员进行优化。编译器当然不关心你抛出了多少括号。考虑谁将阅读代码,并为此进行优化。
当主要是为自己编写代码时,无论使用哪种组合,导航和解释所花费的时间都是最少的。管道是可怕的,以避免陷阱,如许多关闭括号,是真的很难计算。
PS:初始化列表有很多方法。在这种情况下,[1. .. 4.]将完成这项工作。
发布于 2016-04-21 15:13:13
函数之间最重要的区别在于它们是多么的通用。让我们来看看他们的类型:
val sumOfSquareI : nums:seq<float> -> float
val sumOfSquareF1 : nums:float list -> float
val sumOfSquareF2 : nums:float list -> float
val sumOfSquare : nums:seq<float> -> float正如您所看到的,sumOfSquare和sumOfSquareI比其他的更通用--它们可以处理任何序列,而不仅仅是列表。
就sumOfSquare和sumOfSquareF2的风格差异而言,我个人更喜欢sumOfSquare。由于管道操作符和递归的缺乏,您可以简单地从上到下读取代码,并且在每一行都知道发生了什么。
正如Lee指出的,您可以使用内置的Seq.sumBy函数:
let sumOfSquareBiref nums = nums |> Seq.sumBy sqr如果稍后在文件中调用此函数(以便可以推断输入参数),甚至可以跳过参数并编写:
let sumOfSquareBirefer = Seq.sumBy sqr此时,您必须扪心自问,是要在代码中定义单独的函数还是简单地内联(Seq.sumBy sqr input)。您应该考虑函数是否可能发生更改,以及在源中使用了多少次。
https://stackoverflow.com/questions/36773379
复制相似问题