Functional programming 如何编写更清晰的函数式代码?

Functional programming 如何编写更清晰的函数式代码?,functional-programming,f#,Functional Programming,F#,我的代码在风格和外观上都越来越具有功能性 这里我有一个函数,我尽量保持它的通用性,传递一个过滤函数和一个计算函数作为参数 let calcError filter (fcalc:'a -> float) (arr:'a array) = arr |> Array.filter filter |> Array.map fcalc |> Array.average 签名为: val calcError : fi

我的代码在风格和外观上都越来越具有功能性

这里我有一个函数,我尽量保持它的通用性,传递一个过滤函数和一个计算函数作为参数

let calcError filter (fcalc:'a -> float) (arr:'a array) =
        arr |> Array.filter filter
            |> Array.map fcalc
            |> Array.average
签名为:

val calcError : filter:('a -> bool) -> fcalc:('a -> float) -> arr:'a array -> float
我相信这是相当标准的,在部分应用程序中使用calcError

然而,Array.average将在数组大小为0或为null时引发异常,这在我的情况下不会发生

我不太喜欢F中的异常,我更喜欢使用浮点输出或结果

然后,我会考虑用这种方式编写代码,但我不确定这是一种在我试图获得的功能性思维中正确的方式。当然,任何其他解决方案都是受欢迎的,我可能能够适应其他类似问题

谢谢大家

我心目中的解决办法是:

let calcError2 filter (fcalc:'a -> float) (arr:'a array) =
    let subarr = arr |> Array.filter filter
    match subarr.Length with
    | 0 -> Result.Error "array length 0"
    | _ -> subarr |> Array.map fcalc
                  |> Array.average
                  |> Result.Ok

这是一种方法:

let tryCalcError filter (fcalc:'a -> float) (arr:'a array) =
    arr |> Array.filter filter
        |> Array.map fcalc
        |> function
        | [||] -> None
        | arr  -> Array.average arr |> Some

它遵循前缀为try的约定,以指示返回值是一个选项。你可以从以下几个方面看到这个惯例。试试看。。。函数,如tryFind、tryHead、tryLast、tryItem、tryPick。

这是一种方法:

let tryCalcError filter (fcalc:'a -> float) (arr:'a array) =
    arr |> Array.filter filter
        |> Array.map fcalc
        |> function
        | [||] -> None
        | arr  -> Array.average arr |> Some

它遵循前缀为try的约定,以指示返回值是一个选项。你可以从以下几个方面看到这个惯例。试试看。。。函数,如tryFind、tryHead、tryLast、tryItem、tryPick。

这里是另一个带有辅助函数的版本

let calcError filter (fcalc:'a -> float) (arr:'a array) =
    let safeAverage ar = if Array.isEmpty ar then None else Some(Array.average ar)
    arr |> Array.filter filter
            |> Array.map fcalc
            |> safeAverage
let nat arr = if Array.isEmpty arr then None else Some(arr)


let calcError filter (fcalc:'a -> float) (arr:'a array) =
        arr |> Array.filter filter
                |> Array.map fcalc
                |> nat
                |> Option.bind (Some << Array.average )
此外,您可以将数组转换为选项,以便将其与任何其他不安全的数组函数一起使用

let calcError filter (fcalc:'a -> float) (arr:'a array) =
    let safeAverage ar = if Array.isEmpty ar then None else Some(Array.average ar)
    arr |> Array.filter filter
            |> Array.map fcalc
            |> safeAverage
let nat arr = if Array.isEmpty arr then None else Some(arr)


let calcError filter (fcalc:'a -> float) (arr:'a array) =
        arr |> Array.filter filter
                |> Array.map fcalc
                |> nat
                |> Option.bind (Some << Array.average )
这是一个使用无点样式的更紧凑、更高效的版本

let calcError filter (fcalc:'a -> float)   =
       Option.bind (Some << (Array.averageBy fcalc)) << nat << Array.filter filter  

我花了一段时间才真正体会到创建许多小功能的价值。希望能有帮助

这里是另一个带有帮助函数的版本

let calcError filter (fcalc:'a -> float) (arr:'a array) =
    let safeAverage ar = if Array.isEmpty ar then None else Some(Array.average ar)
    arr |> Array.filter filter
            |> Array.map fcalc
            |> safeAverage
let nat arr = if Array.isEmpty arr then None else Some(arr)


let calcError filter (fcalc:'a -> float) (arr:'a array) =
        arr |> Array.filter filter
                |> Array.map fcalc
                |> nat
                |> Option.bind (Some << Array.average )
此外,您可以将数组转换为选项,以便将其与任何其他不安全的数组函数一起使用

let calcError filter (fcalc:'a -> float) (arr:'a array) =
    let safeAverage ar = if Array.isEmpty ar then None else Some(Array.average ar)
    arr |> Array.filter filter
            |> Array.map fcalc
            |> safeAverage
let nat arr = if Array.isEmpty arr then None else Some(arr)


let calcError filter (fcalc:'a -> float) (arr:'a array) =
        arr |> Array.filter filter
                |> Array.map fcalc
                |> nat
                |> Option.bind (Some << Array.average )
这是一个使用无点样式的更紧凑、更高效的版本

let calcError filter (fcalc:'a -> float)   =
       Option.bind (Some << (Array.averageBy fcalc)) << nat << Array.filter filter  

我花了一段时间才真正体会到创建许多小功能的价值。希望能有帮助

我觉得你的代码不错。唯一不同的是,我不会使用match来测试数组是否为空-您没有绑定任何变量,并且只有两个案例,因此您可以在这里使用if表达式

另外两个小调整是,我使用的是数组。我想看看数组是否为空,这可能在这里没有效果,但如果使用序列,它将比检查长度更快,我还使用averageBy,而不是map后跟average:


我觉得你的代码不错。唯一不同的是,我不会使用match来测试数组是否为空-您没有绑定任何变量,并且只有两个案例,因此您可以在这里使用if表达式

另外两个小调整是,我使用的是数组。我想看看数组是否为空,这可能在这里没有效果,但如果使用序列,它将比检查长度更快,我还使用averageBy,而不是map后跟average:


我觉得不错。我可能会尝试将错误案例的消息更改为更详细的消息,例如,过滤后数组为空,但我认为很多人不会对您提出的解决方案有异议。请查看:查看@BentTranberg页上的tryCatch函数,我喜欢tryCatch方法。我用这本圣经研究了ROP,但没有想到tryCatch函数。谢谢我觉得不错。我可能会尝试将错误案例的消息更改为更详细的消息,例如,过滤后数组为空,但我认为很多人不会对您提出的解决方案有异议。请查看:查看@BentTranberg页上的tryCatch函数,我喜欢tryCatch方法。我用这本圣经研究了ROP,但没有想到tryCatch函数。谢谢事实上,我去创建了一个额外的函数数组。tryAverage,感谢inputAn更好的解决方案。实际上,我去创建了一个额外的函数数组。tryAverage,感谢inputAn更好的解决方案。我发现这是一个从功能上解决这个问题的优雅方法。我发现这是一个从功能上解决这个问题的优雅方法。