2015-06-16 2 views
5

Я написал программу для преобразования размера файла из байтов в читаемом формате в F #:Есть ли параметр по умолчанию в F #?

let rec sizeFmt num i = 
    let suffix="B" 
    let unit = ["";"Ki";"Mi";"Gi";"Ti";"Pi";"Ei";"Zi"] 
    match abs num with 
     | x when x < 1024.0 -> printfn "%3.1f %s%s" num unit.[i] suffix 
     | _ -> sizeFmt (num/1024.0) (i+1) 

let humanReadable n = 
    sizeFmt (float n) 0 

Run например:

> humanReadable 33;; 
33.0 B 
val it : unit =() 
> humanReadable 323379443;; 
308.4 MiB 
val it : unit =() 
> 

Вопрос:

  1. Это было быть хорошим, если я могу установить i=0 в качестве значения по умолчанию в sizeFmt funciton. Я проверил документацию F #, нашел только, что нет параметра по умолчанию. Поэтому я должен написать функцию обертки humanReadable. Есть ли способ лучше?

  2. Для обработки как ввода типа int, так и типа float, как humanReadable 123;; и humanReadable 123433.33;;, я должен добавить float n в функцию обертки. Очевидная проблема: очень легко превысить максимальный размер int, который составляет 2,147,483,647. Думаю, что может быть лучший способ, не так ли?

+1

Вам нужно, в какой-то момент, * форсированную глубину? Например, '42,000,000' ->' "41,025.625 KiB" '? Или это всегда должно быть «40.0543 MiB»? – bytebuster

+0

Рамки сообщают размеры файлов как int64; это один «лучший способ». – phoog

+0

@bytebuster Он должен быть: не более трех цифр до десятичной точки + самой большой соответствующей единицы. Например: '40.1 MiB',' 438.0 KiB', '249.8 GiB'. (Я использовал '% 3.1f' для его форматирования.) – Nick

ответ

4

Одно соглашение F #, которое может помочь включить первичные параметры в конце списка параметров и вторичные параметры в первую очередь - противоположность конвенции на языках OO. Это позволяет вам передать основной аргумент вашей функции, например.

let rec sizeFmt i num = 
    ... 

123.0 |> sizeFmt 0 

Это также позволяет легко не создавать частичные функции с дополнительными параметрами заполнены:

let humanReadable = sizeFmt 0 

В ответ на 2, нет нет лучшего способа, если вы сделаете sizeFmt родовое и передать в машинке значение 1024.0, но это, вероятно, не упростит.

0

Хотя я знаю, что это не то, о чем вас спрашивают, знаете ли вы о F # Units of Measure feature?

[<Measure>] type B 
[<Measure>] type kB 

let bPerKB = 1024.M<B/kB> 

let bytesToKiloBytes (bytes : decimal<B>) = bytes/bPerKB 
let kiloBytesToBytes (kiloBytes : decimal<kB>) = kiloBytes * bPerKB 

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

Вот несколько примеров преобразования:

> 1024.M<B> |> bytesToKiloBytes;; 
val it : decimal<kB> = 1M 
> 1145.M<B> |> bytesToKiloBytes;; 
val it : decimal<kB> = 1.1181640625M 
> 1.M<kB> |> kiloBytesToBytes;; 
val it : decimal<B> = 1024M 

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

1

Единственный способ иметь необязательный параметр в F # - использовать метод вместо функции. Чтобы указать, что параметр является необязательным, поставьте перед ним ?. Из документации here:

type DuplexType = 
    | Full 
    | Half 

type Connection(?rate0 : int, ?duplex0 : DuplexType, ?parity0 : bool) = 
    let duplex = defaultArg duplex0 Full 
    let parity = defaultArg parity0 false 
    let mutable rate = match rate0 with 
         | Some rate1 -> rate1 
         | None -> match duplex with 
            | Full -> 9600 
            | Half -> 4800 
    do printfn "Baud Rate: %d Duplex: %A Parity: %b" rate duplex parity 

let conn1 = Connection(duplex0 = Full) 
let conn2 = Connection(duplex0 = Half) 
let conn3 = Connection(300, Half, true) 
5

Если sizeFmt только используется humanReadable, то имеет смысл сделать его внутренняя функция. Это позволяет избежать проблемы с параметром по умолчанию.

Кроме того, маркировка внешней функции inline заставляет принять любой тип n, который поддерживает явное преобразование в float.

let inline humanReadable n = 
    let rec sizeFmt num i = 
     let suffix="B" 
     let unit = ["";"Ki";"Mi";"Gi";"Ti";"Pi";"Ei";"Zi"] 
     match abs num with 
      | x when x < 1024.0 -> printfn "%3.1f %s%s" num unit.[i] suffix 
      | _ -> sizeFmt (num/1024.0) (i+1) 
    sizeFmt (float n) 0 

humanReadable 123 //works 
humanReadable 123433.33 //also works 
0

Существующих ответы уже объясняют, что сохранение функции-оболочки является хорошей идеей, так как это позволяет коду быть максимально модульным, насколько это возможно. Это было бы не очень очевидно на простом примере, но в реальных проектах было бы большим преимуществом, чтобы в какой-то момент расширить sizeFmt, выставив больше параметров - считайте, что вам иногда может понадобиться "Hertz" вместо "Bytes" (и деление на 1000 вместо 1024), или шаблон формата sting (пять десятичных цифр), или локализуемый список множителей и т. д.


Что касается второго вопроса, то преобразование в float, решение очень простое: сделать valuestatically-resolved type:

let inline humanReadable (value:^T) = 
    sizeFmt (float value) 0 

Это сделает humanReadable иметь следующий тип ограничение:

val inline humanReadable : 
    value: ^T -> unit when ^T : (static member op_Explicit : ^T -> float) 

Использование:

humanReadable 42424242.42      // float 
humanReadable 4242        // int 
humanReadable 42424242424242424242I   // Numerics.BigInteger 
humanReadable (424242424242424242422424N/5N) // BigRational 

Использование float в внутренней функции, кажется, хорошо: любые ошибки округления будут устранены ряд подразделений.

Смежные вопросы