Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/facebook/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
.net F#-将简单的for循环转换为更具功能的构造_.net_F#_Functional Programming - Fatal编程技术网

.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]