如何重构代码以使其具有功能性样式? [英] How to refactor code to make it functional style?

查看:107
本文介绍了如何重构代码以使其具有功能性样式?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用F#,我试图以一种更实用的方式来思考代码.我工作的很大一部分恰好是数字性质的,所以我在考虑这种再教育是否有意义.编写数字代码的功能是否像尝试将方形钉插入圆孔中一样,还是仅仅是陡峭的学习曲线问题,而不管其用途如何?

Playing with F#, I'm trying to think of code in a more functional way. A large part of my work happens to be numerical in nature so I'm thinking whether this re-education makes sense. Is writing numerical code in a functional way like trying to fit a square peg in a round hole or is it simply a matter of a steep learning curve irrespective of the application?

例如,让我们通过一个片段演示大数定律:

For example, lets take a snippet which demonstrates the weak law of large numbers:

open System
open System.IO
open System.Windows.Forms
open System.Windows.Forms.DataVisualization
open FSharp.Data
open FSharp.Charting
open FSharp.Core.Operators
open MathNet.Numerics
open MathNet.Numerics.LinearAlgebra
open MathNet.Numerics.Random
open MathNet.Numerics.Distributions
open MathNet.Numerics.Statistics


let T = 1000

let arr1 = Array.init T (fun i -> float i*0.)
for i in 0 .. T-1 do
    arr1.[i] <- [|for j in 1..i do yield Exponential.Sample(0.1)|] |> Statistics.Mean

let arr2 = Array.init T (fun i -> float i*0.)
for i in 0 .. T-1 do
    arr2.[i] <- arr1.[1 .. i] |> Statistics.Mean

arr2 |> Chart.Line |> Chart.Show

是否有表达以上内容的简洁功能方法?可以将多少功能范式纳入这样的工作中?

Is there a succinct functional way of expressing the above? How much of a functional paradigm can be incorporated into work like this?

推荐答案

我首先不会将对Array.init的调用和设置初始值分开.您可以使用@ s952163在其答案中使用的形式,也可以根据您的代码使用

I'd first not separate the call to Array.init and setting the initial values. You can use the form that @s952163 use in their answer, or based on your code:

let arr1 = Array.init T (fun i ->
    [|for j in 1..i do yield Exponential.Sample 0.1 |] |> Statistics.Mean
)

这是因为您要分配中间数组,这很昂贵-而且无论如何,您都必须在计算均值之后立即丢弃它们.替代方法:

Issue with this is that you are allocating intermediate arrays, which is costly - and you anyway discard them right after computing the mean. Alternative:

let arr1 = Array.init T (fun i ->
    Exponential.Samples 0.1 |> Seq.take (i+1) |> Seq.average
)

现在第二部分:您正在重复计算元素1..i的平均值,这将成为O(n ^ 2)运算.您可以通过使用元素1..i的总和是元素1 ... {i-1}加上第i.th个元素的事实,在O(n)中解决该问题.

Now for the second part: You are computing the mean of elements 1..i repeatedly, which becomes an O(n^2) operation. You can solve it in O(n) by using the fact that the sum of elements 1..i is the sum of elements 1..{i-1} plus the i.th element.

let sums, _ =
    arr1
    |> Array.mapFold (fun sumSoFar xi ->
        let s = sumSoFar + xi
        s, s
    ) 0.0
let arr2 = 
    sums
    |> Array.mapi (fun i sumi -> sumi / (float (i + 1)))

当然,您都可以在单个管道中编写该代码.

Of course you can all write that in a single pipe.

或者,使用库函数Array.scan计算累积总和,在这种情况下,您将得到长度为T+1的结果,然后从该结果中删除第一个元素:

Alternatively, use the library function Array.scan to compute the cumulative sums, which would in this case give you a result of length T+1, from which you'd then drop the first element:

let arr2 =
    Array.sub (Array.scan (+) 0.0 arr1) 1 T
    |> Array.mapi (fun i sumi -> sumi / (float (i + 1)))

或避免使用中间数组:

Seq.scan (+) 0.0 arr1
|> Seq.skip 1
|> Seq.mapi (fun i sumi -> sumi / (float (i + 1)))
|> Seq.toArray

这篇关于如何重构代码以使其具有功能性样式?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆