Это лучший (более функциональный способ) написать следующий код fsharp? - PullRequest
1 голос
/ 29 октября 2009

У меня есть такие части кода в проекте, и я понимаю, что это не так написано функционально:

let data = Array.zeroCreate(3 + (int)firmwareVersions.Count * 27)
data.[0] <- 0x09uy                              //drcode
data.[1..2] <- firmwareVersionBytes             //Number of firmware versions



let mutable index = 0
let loops = firmwareVersions.Count - 1
for i = 0 to loops do
    let nameBytes = ASCIIEncoding.ASCII.GetBytes(firmwareVersions.[i].Name)
    let timestampBytes = this.getTimeStampBytes firmwareVersions.[i].Timestamp
    let sizeBytes = BitConverter.GetBytes(firmwareVersions.[i].Size) |> Array.rev

    data.[index + 3 .. index + 10] <- nameBytes
    data.[index + 11 .. index + 24] <- timestampBytes
    data.[index + 25 .. index + 28] <- sizeBytes
    data.[index + 29] <- firmwareVersions.[i].Status
    index <- index + 27  

firmwareVersions - это список, который является частью библиотеки csharp. Он имеет (и не должен) знать, как он будет преобразован в массив байтов. Я понимаю, что приведенный выше код не работает, поэтому я попытался меняя это так:

let headerData = Array.zeroCreate(3)
headerData.[0] <- 0x09uy
headerData.[1..2] <- firmwareVersionBytes

let getFirmwareVersionBytes (firmware : FirmwareVersion) =
    let nameBytes = ASCIIEncoding.ASCII.GetBytes(firmware.Name)
    let timestampBytes = this.getTimeStampBytes firmware.Timestamp
    let sizeBytes = BitConverter.GetBytes(firmware.Size) |> Array.rev
    Array.concat [nameBytes; timestampBytes; sizeBytes]

let data = 
    firmwareVersions.ToArray()
    |> Array.map (fun f -> getFirmwareVersionBytes f)
    |> Array.reduce (fun acc b -> Array.concat [acc; b])

let fullData = Array.concat [headerData;data]

Так что теперь мне интересно, если это лучший (более функциональный) способ написать код. Если так ... почему и какие улучшения я должен сделать, если нет, почему бы и нет, и что мне делать вместо этого?

Предложения, отзывы, замечания?

Спасибо

Обновление

Просто хотел добавить еще немного информации. Это часть некоторой библиотеки, которая обрабатывает данные для двоичного сообщения протокол. Единственный плюс, который я вижу в первой версии кода, заключается в том, что люди, реализующие протокол на другом языке (что имеет место в нашей ситуации также) может получить лучшее представление о том, сколько байтов каждый часть занимает и где именно они находятся в потоке байтов ... просто замечание. (Поскольку не все понимают английский, но все наши партнеры могут читать код)

Ответы [ 3 ]

2 голосов
/ 14 января 2012

Я был бы склонен встроить все, потому что вся программа становится намного короче:

let fullData =
  [|yield! [0x09uy; firmwareVersionBytes; firmwareVersionBytes]
    for firmware in firmwareVersions do
      yield! ASCIIEncoding.ASCII.GetBytes(firmware.Name)
      yield! this.getTimeStampBytes firmware.Timestamp
      yield! BitConverter.GetBytes(firmware.Size) |> Array.rev|]

Если вы хотите передать позиции байтов, я бы поместил их в комментарии в конце каждой строки.

1 голос
/ 29 октября 2009

Мне больше нравится ваша первая версия, потому что индексирование дает лучшую картину смещений, которые являются важной частью проблемы (я полагаю). В императивном коде заметно смещение байтов, что может быть важно, если ваши партнеры не могут / не читают документацию. Функциональный код подчеркивает склеивание структур, что было бы нормально, если бы смещения байтов были недостаточно важны, чтобы их можно было упомянуть в документации.

Индексирование обычно является случайной сложностью, и в этом случае ее следует избегать. Например, цикл вашей первой версии может быть for firmwareVersion in firmwareVersion вместо for i = 0 to loops.

Кроме того, как говорит Брайан, использование констант для смещений сделает императивную версию еще более читабельной.

1 голос
/ 29 октября 2009

Как часто запускается код?

Преимущество «конкатенации массивов» состоит в том, что оно облегчает «просмотр» логических частей. Недостатком является то, что он создает много мусора (выделяя временные массивы) и может также работать медленнее, если используется в узком цикле.

Кроме того, я думаю, что, возможно, ваш "Array.reduce (...)" может быть просто "Array.concat".

В целом, я предпочитаю первый способ (просто создайте один огромный массив), хотя я бы по-другому его учел, чтобы сделать логику более очевидной (например, иметь именованную константу HEADER_SIZE и т.

Пока мы на этом, я бы, вероятно, добавил несколько утверждений, чтобы убедиться, что, например, nameBytes имеет ожидаемую длину.

...