Возникают проблемы с преобразованием кода C # в F # при необходимости - PullRequest
1 голос
/ 19 мая 2019

У меня есть следующий код 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? 

1 Ответ

5 голосов
/ 19 мая 2019

Я бы не стал использовать 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 # для удовольствия и прибыли .

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...