我有以下C#代码:
(无需了解其详细信息,仅用于说明问题)
long VolumeBeforePrice = 0;
long Volume = 0;
var ContractsCount = 0.0;
var VolumeRequested = Candle.ConvertVolumes(MinVolume);
// go through all entries
foreach (var B in Entries)
{
// can we add the whole block?
if (Volume + B.VolumeUSD <= VolumeRequested)
{
// yes, add the block and calculate the number of contracts
Volume += B.VolumeUSD;
ContractsCount += B.VolumeUSD / B.PriceUSD;
}
else
{
// no, we need to do a partial count
var Difference = VolumeRequested - Volume;
ContractsCount += Difference / B.PriceUSD;
Volume = VolumeRequested; // we reached the max
}
VolumeBeforePrice += B.VolumeUSD;
if (Volume >= VolumeRequested) break;
}
它遍历交易订单簿的条目并计算可用于特定美元金额的合同数量。
逻辑很简单:对于每个条目,都有一个给定价格的合同块,因此它要么添加整个块,要么在请求中不包含部分块。
由于我是该语言的新手,因此我尝试将其移至F#,并且遇到一些问题:
这是部分实现:
let mutable volume = 0L
let mutable volumeBeforePrice = 0L
let mutable contractsCount = 0.0
entries |> List.iter (fun e ->
if volume + e.VolumeUSD <= volumeRequested then
volume <- volume + e.VolumeUSD;
contractsCount <- contractsCount + float(e.VolumeUSD) / e.PriceUSD
else
let difference = volumeToTrade - volume
contractsCount <- contractsCount + difference / B.PriceUSD
volume = volumeRequested // this is supposed to trigger an exit on the test below, in C#
)
我停在那里,因为它看起来不像是一种F#的方式:)
所以,我的问题是:如何构造List.iter以便:
- I can use counters from one iteration to the next? like sums and average passed to the next iteration
- I can exit the loop when I reached a specific condition and skip the last elements?
答案 0 :(得分:5)
我会避免使用mutable
并使用纯函数。例如,您可以为结果定义一条记录,例如Totals
(您可能会有一个更有意义的名称):
type Totals =
{ VolumeBeforePrice : int64
Volume : int64
ContractsCount : float }
然后您可以创建一个函数,该函数将当前总计和一个条目作为输入,并返回一个新的总计作为其结果。为了清楚起见,我在下面的函数中标注了类型,但可以将其删除,因为可以推断出它们:
let addEntry (volumeRequested:int64) (totals:Totals) (entry:Entry) : Totals =
if totals.Volume >= volumeRequested then
totals
elif totals.Volume + entry.VolumeUSD <= volumeRequested then
{ Volume = totals.Volume + entry.VolumeUSD
ContractsCount = totals.ContractsCount + float entry.VolumeUSD / entry.PriceUSD
VolumeBeforePrice = totals.VolumeBeforePrice + entry.VolumeUSD }
else
let diff = volumeRequested - totals.Volume
{ Volume = volumeRequested
ContractsCount = totals.ContractsCount + float diff / entry.PriceUSD
VolumeBeforePrice = totals.VolumeBeforePrice + entry.VolumeUSD }
现在,您可以遍历最后一次总计的列表。幸运的是,有一个内置函数List.fold
可以做到这一点。您可以在F# for fun and profit上阅读有关折叠的更多信息。
let volumeRequested = Candle.ConvertVolumes(minVolume)
let zero =
{ VolumeBeforePrice = 0L
Volume = 0L
ContractsCount = 0. }
let result = entries |> List.fold (addEntry volumeRequested) zero
请注意,这将为您提供正确的结果,但始终会迭代所有条目。这是否可以接受可能取决于entries
列表的大小。如果要避免这种情况,则需要使用递归。像这样:
let rec calculateTotals (volumeRequested:int64) (totals:Totals) (entries:Entry list) : Totals =
if totals.Volume >= volumeRequested then
totals
else
match entries with
| [] -> totals
| entry::remaining ->
let newTotals =
if totals.Volume + entry.VolumeUSD <= volumeRequested then
{ Volume = totals.Volume + entry.VolumeUSD
ContractsCount = totals.ContractsCount + float entry.VolumeUSD / entry.PriceUSD
VolumeBeforePrice = totals.VolumeBeforePrice + entry.VolumeUSD }
else
let diff = volumeRequested - totals.Volume
{ Volume = volumeRequested
ContractsCount = totals.ContractsCount + float diff / entry.PriceUSD
VolumeBeforePrice = totals.VolumeBeforePrice + entry.VolumeUSD }
calculateTotals volumeRequested newTotals remaining
let result = calculateTotals volumeRequested zero entries