在 F# 中制作一个可变结构,与 class
making a mutable struct, in F#, vs. a class
让我们想象一下这个递归函数:
let rec doSomething dataList b c d e f g h i =
// go through dataList and recurse with updated parameters
所以我们可以让它更干净:
type Context = { b:..; c:..; ... }
let rec doSomething dataList context =
// recurse with an updated context object
实际上,这会比较慢,因为每次都必须重新创建上下文对象,并且使用非常紧凑的循环和简单的计算,开销是可见的。
我有几个这样的数学循环,但现在我有一个新的场景:
我需要将它们用于通过使用相同的逻辑流入的数据,但是随着数据的到来逐步进行。
所以我的函数应该是这样的:
let doOneStepOfSomething oneData context : result * Context =
process one piece of data and returns the context object for the next iteration
但是我也遇到了重新创建对象的问题,我需要保留不同的上下文。
对于每个实例都可以将其上下文变量设为可变的 class,这是一个有效的用例吗?或者为上下文创建一个带有可变字段的可变类型是否有意义?
对于任何与性能相关的问题,唯一的答案是您必须对其进行测试才能看到。我使用一个简单的函数和 #time
特性测试了四种不同的方法。
不可变功能记录
type C = { a:int; b:int; c:int; d:int }
let rec foo c =
if c.a > 100000000 then c
else foo { a = c.a + 1; b = c.a; c = c.a; d = c.a }
foo { a=0; b=0; c=0; d=0 } // ~1.1 sec
带内联参数的函数
let rec bar a b c d =
if a > 100000000 then (a,b,c,d)
else bar (a+1) a a a
bar 0 0 0 0 // ~200ms
使用不可变结构替换记录
[<Struct>]
type S(a:int,b:int,c:int,d:int) =
member x.A = a
member x.B = b
member x.C = c
member x.D = d
let rec goo (c:S) =
if c.A > 100000000 then c
else goo (S(c.A+1, c.A, c.A, c.A))
goo (S(0,0,0,0)) // ~1.6sec
使用可变记录
type M = { mutable a:int; mutable b:int; mutable c:int; mutable d:int }
let rec zoo c =
if c.a > 100000000 then c
else
c.a <- c.a + 1
c.b <- c.a
c.c <- c.a
c.d <- c.a
zoo c
zoo { a=0; b=0; c=0; d=0 } // ~1 sec
从这四个测试来看,内联参数似乎比其他任何东西都更有效(几乎都是一样的)。所以我只使用不可变记录,它使代码清晰,只有当它在你的系统中被证明是一个更普遍的问题时才优化它。
让我们想象一下这个递归函数:
let rec doSomething dataList b c d e f g h i =
// go through dataList and recurse with updated parameters
所以我们可以让它更干净:
type Context = { b:..; c:..; ... }
let rec doSomething dataList context =
// recurse with an updated context object
实际上,这会比较慢,因为每次都必须重新创建上下文对象,并且使用非常紧凑的循环和简单的计算,开销是可见的。
我有几个这样的数学循环,但现在我有一个新的场景:
我需要将它们用于通过使用相同的逻辑流入的数据,但是随着数据的到来逐步进行。
所以我的函数应该是这样的:
let doOneStepOfSomething oneData context : result * Context =
process one piece of data and returns the context object for the next iteration
但是我也遇到了重新创建对象的问题,我需要保留不同的上下文。
对于每个实例都可以将其上下文变量设为可变的 class,这是一个有效的用例吗?或者为上下文创建一个带有可变字段的可变类型是否有意义?
对于任何与性能相关的问题,唯一的答案是您必须对其进行测试才能看到。我使用一个简单的函数和 #time
特性测试了四种不同的方法。
不可变功能记录
type C = { a:int; b:int; c:int; d:int }
let rec foo c =
if c.a > 100000000 then c
else foo { a = c.a + 1; b = c.a; c = c.a; d = c.a }
foo { a=0; b=0; c=0; d=0 } // ~1.1 sec
带内联参数的函数
let rec bar a b c d =
if a > 100000000 then (a,b,c,d)
else bar (a+1) a a a
bar 0 0 0 0 // ~200ms
使用不可变结构替换记录
[<Struct>]
type S(a:int,b:int,c:int,d:int) =
member x.A = a
member x.B = b
member x.C = c
member x.D = d
let rec goo (c:S) =
if c.A > 100000000 then c
else goo (S(c.A+1, c.A, c.A, c.A))
goo (S(0,0,0,0)) // ~1.6sec
使用可变记录
type M = { mutable a:int; mutable b:int; mutable c:int; mutable d:int }
let rec zoo c =
if c.a > 100000000 then c
else
c.a <- c.a + 1
c.b <- c.a
c.c <- c.a
c.d <- c.a
zoo c
zoo { a=0; b=0; c=0; d=0 } // ~1 sec
从这四个测试来看,内联参数似乎比其他任何东西都更有效(几乎都是一样的)。所以我只使用不可变记录,它使代码清晰,只有当它在你的系统中被证明是一个更普遍的问题时才优化它。