Da *_*est 2 f# functional-programming
所以我有这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
Run Code Online (Sandbox Code Playgroud)
最后两个函数之间有什么真正的区别吗?这个比那个好吗?在F#中编写功能代码时,我应该多久使用一次|>运算符(这对我来说是全新的)?
另一件事是在F#中创建列表有不同的方法吗?我从视频中提取的内容似乎......笨重.
是的,你应该更喜欢使用Seq和List模块中的高阶函数而不是显式递归.您还可以使用sumBy:
let sumOfSquare nums = nums |> Seq.sumBy sqr
Run Code Online (Sandbox Code Playgroud)
关于编程风格或范式的讨论很容易变成基于意见的推理。让我们来看看每个函数在现实世界中真正重要的含义。
版本 1:命令式
这一项需要五行琐碎的行。几乎每个人都可以阅读它。“状态机”是单个变量,易于验证。它不是通用的,但LanguagePrimitives.GenericZero不是0.0可以解决这个问题,尽管需要付出一定的代价。
与其他人相比,我不会称之为“糟糕,非常糟糕”。除非你让编程成为一种崇拜,必须通过神圣的功能真理来摧毁命令式的异常。虽然它也不是最小的。
版本 2:递归计算
它相当易读,少用一行。同样,不是通用的,但通用零可以解决这个问题。但是,这个函数会导致栈变大!使用它的人会在第一次测试中看到它运行良好,直到突然,性能下降或整个程序崩溃!
我认为这客观上是所提出的解决方案中最糟糕的!您不想在生产代码中使用它!
版本 3:嵌套尾递归函数
同样,这可以变得通用。这个运行良好并且功能强大,但它有 6 行,我不会再将其中的三行称为微不足道了。
这是纯粹的,但老实说,如果我必须在这个和命令式变体之间做出选择,我不太确定。
版本 4:调用高阶函数
简短易读,通用而不使用奇怪的原语!为什么你会怀疑这是四人中的赢家?
额外版本:根本没有
高阶函数为此效果很好,这还不是结束。Seq.sumBy sqr(我看到李在他的回答中击败了我)完成了这项工作。由于这仅比 长sumOfSquares,在生产代码中,可能根本没有理由定义额外的函数。
可读性是关键
当您就何时使用管道运算符等寻求建议时,答案必须针对将阅读代码的人进行优化。编译器肯定不在乎你扔了多少括号。考虑谁会阅读代码,并为此进行优化。
在主要为自己编写代码时,请使用任何花费最少时间进行导航和解释的组合。管道非常棒,可以避免像很多很难计算的右括号之类的问题。
PS:有很多方法可以初始化列表。在这种情况下,[1. .. 4.]将完成这项工作。
| 归档时间: |
|
| 查看次数: |
290 次 |
| 最近记录: |