.net F#-将简单的for循环转换为更具功能的构造
我有一个简单的“投资”类型和“终身”财产:.net F#-将简单的for循环转换为更具功能的构造,.net,f#,functional-programming,.net,F#,Functional Programming,我有一个简单的“投资”类型和“终身”财产: type Investment = { PurchasePayment: float; Lifetime: float; TaxWriteoff: float; ResidualValue: float; } let CoffeMachine = { PurchasePayment = 13000.0; Lifetime = 4.0; TaxWriteoff = 2000.0; ResidualValue = 500.
type Investment = {
PurchasePayment: float;
Lifetime: float;
TaxWriteoff: float;
ResidualValue: float;
}
let CoffeMachine = {
PurchasePayment = 13000.0;
Lifetime = 4.0;
TaxWriteoff = 2000.0;
ResidualValue = 500.0;
}
我想重复多年的投资,并每年进行一些计算:
for i = 1 to int CoffeMachine.Lifetime do
printf "%i" i
// doSomething
有没有一种方法可以避免使用for循环,并以更实用的风格编写它
干杯
Wojtek我很快找到了答案:
[1..int CoffeMachine.Lifetime] |> List.iter (printf "%i")
对列表中的每个项目执行计算的转到方法称为
map
。具体来说,对于您的情况,您可以创建一个从1到生存期的数字列表,然后使用list.map
对每个数字执行计算:
let calculation year = year * 2 // substitute your calculation here
let calcResults = [1..int CoffeMachine.Lifetime] |> List.map calculation
也就是说,我认为你把“功能性”和“难以理解”(或者,也许是“炫耀”)混淆了。“函数式编程”的要点并不是所有的数学问题,也不是外行都无法理解的。“函数编程”的要点是“用函数编程”。这有一些实际的含义,比如“不可变数据”和“无副作用”,只要你的程序满足这些要求,你可以认为它是“功能性的”,不管它看起来多么简单。事实上,我认为它看起来越简单越好。软件可维护性是一个非常有价值的目标
特别是,如果您只想打印年份,那么您的代码很好:打印本身就是一种“副作用”,因此,只要这是一项要求,就没有办法让它更“实用”。但是,如果您的目标是执行一些计算(如上面的示例所示),那么可以使用列表理解来表达:
let calcResults = [for year in 1..int CoffeMachine.Lifetime -> calculation year]
与其他答案不同的方法是使用尾部递归
例如,尾部递归的好处是什么
[1..int CoffeMachine.Lifetime] |> List.iter (printf "%i")
或:
从性能和内存的角度来看,List.iter
比for loop
更糟糕,因为第一个创建一个单链表(F#immutable List是引擎盖下的单链表)并在其上迭代。在许多情况下,CPU和内存使用率的增加是不相关的,但在其他情况下是相关的
像Seq
这样的惰性集合可以缓解这一问题,但不幸的是Seq
目前在F#中效率不高。Nessos Streams将是一个更好的选择
F#中的for循环
的问题是不能过早地将其中断(中断
或继续
在F#中不存在)
此外,在聚合结果时,for循环
模式常常迫使我们进入可变的变量模式
尾部递归允许我们在不依赖可变变量的情况下聚合结果,并支持中止。此外,尾部递归循环可以返回for loop
无法返回的值(表达式结果总是unit
)
尾部递归在F#中也很有效,因为F#检测尾部递归函数并将其展开到引擎盖下的循环中
这就是上面代码的尾部递归循环的样子:
let rec loop i l = if i <= l then printf "%i" i; loop (i + 1) l
loop 1 (int CoffeMachine.Lifetime)
下面是几个简单的递归方法示例。通常,您只需要使用.iter或.map
let rec map f = function
| [] -> []
| h::t -> f h::map f t
map (fun x->x+1) [1;2;3]
let rec iter f = function
| [] -> ()
| h::t ->
f h
iter f t
签名:
for iter, f:('a -> unit)
for map, f:('a -> 'b)
您也可以使用标准循环语法:
for i in [0 .. 4] do
printfn "%i" i
for i = 0 to 4 do
printfn "%i" i
要以函数方式使用for循环,可以使用以下语法:
[for i in [0..10]->i]
[for i = 0 to 10 do yield i]
注意
List.map f[0..10]相当于[0..10]>i]这取决于如何在生产代码中生成生存期,但使用int
函数可能不是一个好主意。从数据库进行计算或检索,或其他意外情况,可能会导致3.9。。。而不是4.0,使用int
后将有3年而不是4年。也许你应该改用round
。
for i in [0 .. 4] do
printfn "%i" i
for i = 0 to 4 do
printfn "%i" i
[for i in [0..10]->i]
[for i = 0 to 10 do yield i]