如何改进这个F#函数

如何改进这个F#函数,f#,functional-programming,F#,Functional Programming,我对C语言很有经验,但对F语言和函数式编程还不熟悉。现在我正试图在F#中实现一个类库。以下是其中一个功能: 它需要一个整数列表=9,那么当前值应该是previous+1 if(当前==9&&previous>=9) { 项目[i]=以前的+1; } } } 现在是我的F#tail递归。它不是按索引循环列表,而是递归地将项从初始列表移动到已处理列表,直到初始列表中的所有内容都消失: let releaseCap items = let rec loop processed remaini

我对C语言很有经验,但对F语言和函数式编程还不熟悉。现在我正试图在F#中实现一个类库。以下是其中一个功能: 它需要一个整数列表=9,那么当前值应该是previous+1 if(当前==9&&previous>=9) { 项目[i]=以前的+1; } } } 现在是我的F#tail递归。它不是按索引循环列表,而是递归地将项从初始列表移动到已处理列表,直到初始列表中的所有内容都消失:

let releaseCap items =
    let rec loop processed remaining = //tail recursion
        match remaining with
        | [] -> processed //if nothing left, the job is done.
        | current :: rest when current = 9 -> //if current item =9
            match processed with
            // previous value >= 9, then append previous+1 to the processed list
            | previous :: _ when previous >= 9 -> loop (previous+1 :: processed) rest 
            //if previous < 9, the current one should be just 9
            | _ -> loop (current :: processed) rest 
        //otherwise, just put the current value to the processed list
        | current :: rest -> loop (current :: processed) rest 
    loop [] items |> List.rev
let releaseCap项目=
让rec循环处理剩余=//尾部递归
与剩余的匹配
|[]->已处理//如果没有剩余,则作业已完成。
|当前::当前项=9时静止->//如果当前项=9
匹配处理
//previous值>=9,然后将previous+1追加到已处理列表
|previous::u当previous>=9->loop(previous+1::processed)rest
//如果先前值小于9,则当前值应仅为9
|循环(当前::已处理)rest
//否则,只需将当前值放入已处理列表
|当前::rest->循环(当前::已处理)rest
循环[]项|>List.rev

虽然C版本简单直观,但F代码冗长而不直观。F#代码是否有任何部分可以改进以使其更加优雅?

您可以重用现有函数以简化代码。 通常,当您更改列表中的项目时,您会想到一个
映射
,但在这种情况下,您需要记住以前的计算中应该为每个项目传递的内容,因此您应该将目标放在
折叠
相关函数上

这里有一个:
List.scan

let releaseCap items =
    items
        |> List.scan (fun previous current -> 
            if current = 9 && previous >= 9 then previous + 1
            else current) 0  
        |> List.tail
FP不仅仅是使用递归而不是循环。递归通常用于基本的和可重用的函数,然后通过组合这些函数,您可以解决复杂的问题

注意:您正在比较C#解决方案和F#解决方案,但是您是否注意到,除了语言之外,这两种解决方案之间还有一个重要的区别?您的C#解决方案使用可变性。

在您的C#代码中,您正确地假设列表的第一个元素永远不会更改,并从第二个元素开始

如果在F代码中包含此项,则可以跳过累加器上的匹配。 结果会简单一些

   let reduceCap l = 
    let rec reduceCapRec acc l =
        let previous = List.head acc
        match l with
        | x::xs -> 
            if x = 9 && previous >= 9 
            then reduceCapRec ((previous+1) :: acc) xs
            else reduceCapRec (x :: acc) xs
        | [] -> acc

    reduceCapRec [List.head l] (List.tail l)
    |> List.rev

(尽管Gustavo的解决方案仍然要好得多——我也是FP新手)

我认为折叠是重要的一个!:-)

因此:


对于OP:文档将非常有用,因为它可以帮助您了解哪些预先存在的东西用于操作本机集合。谢谢。我不知道有扫描功能,可以做一个地图和记住以前的状态
let list = [9;9;9;1;4;0;1;9;9;9;9] 

let incr acc e =
    match acc, e with
    | h::t, e when e = 9 && h >= 9 -> (h + 1)::acc 
    | _ -> e::acc 


list 
|> List.fold incr []
|> List.rev

//val it : int list = [9; 10; 11; 1; 4; 0; 1; 9; 10; 11; 12]