F# - 将简单的循环转换为更多功能构造

时间:2017-06-04 17:58:50

标签: .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.0;
}

我想重复多年的投资并每年进行一些计算:

for i = 1 to int CoffeMachine.Lifetime do
    printf "%i" i
    // doSomething

有没有办法避免使用for循环并以更实用的方式编写?

干杯,

Wojtek

4 个答案:

答案 0 :(得分:5)

对列表中的每个项目执行计算的首选方法称为cd /opt/PetaLinux sudo chmod 777 PetaLinux$petalinux-v2017.1-finalinstaller.run ./Petalinux-v2017.1-finalinstaller.run 。具体而言,对于您的情况,您可以创建一个从1到map的数字列表,然后使用Lifetime对每个数字执行计算:

List.map

那就是说,我认为你把“功能性”与“难以理解”(或者,也许是“炫耀”)混为一谈。 “函数式编程”的要点不是所有的数学,也不是无法接受的。 “函数式编程”的意思是“用函数编程”。这有一些实际意义,例如“不可变数据”和“无副作用”,只要您的程序满足这些要求,您就可以将其视为“功能性”,无论它看起来多么简单。事实上,我认为它看起来越简单越好。软件可维护性是一个非常有价值的目标。

特别是,如果你只想打印多年,那么你的代码就可以了:打印出来本身是一种“副作用”,所以只要这是一个要求,就没有办法让它更具“功能性” ”。但是如果你的目标是进行一些计算(如上面的例子中那样),那么可以用列表理解来表达清晰度:

let calculation year = year * 2 // substitute your calculation here

let calcResults = [1..int CoffeMachine.Lifetime] |> List.map calculation

答案 1 :(得分:2)

我很快找到答案:

[1..int CoffeMachine.Lifetime] |> List.iter (printf "%i") 

答案 2 :(得分:1)

与其他答案不同的方法是使用尾递归。

例如尾递归有什么好处?

[1..int CoffeMachine.Lifetime] |> List.iter (printf "%i") 

或者:

for i = 1 to int CoffeMachine.Lifetime do
    printf "%i" i

从性能和内存的角度来看,List.iterfor loop更糟糕,因为第一个创建一个链表(F#不可变列表是引擎盖下的单链表)并迭代它。在许多情况下,增加的CPU和内存使用量并不相关,但在其他情况下则是如此。

Seq这样的惰性集合会缓解这种情况,但不幸的是Seq目前在F#中效率不高。 Nessos Streams将是更好的选择。

F#中for loop的问题在于它不能过早地中断(F#中不存在breakcontinue。)

此外,for loop模式通常会在聚合结果时强制我们进入可变变量模式。

尾递归允许我们在不依赖可变变量的情况下聚合结果并支持中止。另外,尾递归循环可以返回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)

使用ILSpy看到这被编译成一个while循环:

internal static void loop@3(int i, int l)
{
  while (i <= l)
  {
    PrintfFormat<FSharpFunc<int, Unit>, TextWriter, Unit, Unit> format = new PrintfFormat<FSharpFunc<int, Unit>, TextWriter, Unit, Unit, int>("%i");
    PrintfModule.PrintFormatToTextWriter<FSharpFunc<int, Unit>>(Console.Out, format).Invoke(i);
    int arg_28_0 = i + 1;
    l = l;
    i = arg_28_0;
  }
}

答案 3 :(得分:0)

一些递归方法的简短示例。通常,您只需使用.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]相当于[for i in [0..10] - &gt; i]