2015-06-16 19 views
5

Tôi đã viết một chương trình để chuyển đổi kích thước tập tin từ byte sang một định dạng có thể đọc được của con người trong F #:Có tham số mặc định trong F # không?

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 dụ:

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

Câu hỏi:

  1. Nó sẽ thật tuyệt nếu tôi có thể đặt i=0 làm giá trị mặc định trong sizeFmt funciton. Tôi đã kiểm tra tài liệu F #, chỉ thấy rằng không có tham số mặc định. Vì vậy, tôi phải viết một hàm wrapper humanReadable. Có cách nào tốt hơn?

  2. Để xử lý cả kiểu nhập int và kiểu float như humanReadable 123;;humanReadable 123433.33;;, tôi phải thêm float n vào chức năng trình bao bọc. Vấn đề rõ ràng là: nó rất dễ dàng để vượt quá kích thước tối đa int là 2.147.483.647. Tôi đoán có thể có một cách tốt hơn, phải không?

+1

Bạn có cần, tại một số thời điểm, độ sâu * ép * không? Ví dụ: '42.000.000' -> '" 41,025,625 KiB "'? Hay nó luôn được coi là "40.0543 MiB"? – bytebuster

+0

Khuôn khổ báo cáo kích thước tệp dưới dạng int64; đó là một "cách tốt hơn". – phoog

+0

@bytebuster Nó được cho là: không quá ba chữ số trước dấu thập phân + đơn vị thích hợp lớn nhất. Ví dụ: '40.1 MiB',' 438.0 KiB', '249.8 GiB'. (Tôi đã sử dụng '% 3.1f' để định dạng nó.) – Nick

Trả lời

4

Quy ước một F # có thể giúp đặt thông số chính ở cuối danh sách tham số và thông số thứ nhất trước - ngược lại với quy ước trong ngôn ngữ OO. Điều này cho phép bạn đặt đối số chính vào hàm của bạn, ví dụ:

let rec sizeFmt i num = 
    ... 

123.0 |> sizeFmt 0 

Nó cũng cho phép bạn dễ dàng tạo ra các chức năng một phần với các thông số tùy chọn điền vào:

let humanReadable = sizeFmt 0 

Trong câu trả lời cho 2, không có không có cách nào tốt hơn, trừ khi bạn thực sizeFmt generic và vượt qua trong đánh máy giá trị của 1024.0 nhưng điều này có thể sẽ không làm cho nó đơn giản hơn.

0

Mặc dù tôi biết đó không phải là những gì đang được hỏi về, bạn có biết về F # 's 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 

Điều này cung cấp cho bạn cách an toàn để phân biệt byte từ kilobyte và ngăn bạn vô tình gán giá trị kilobyte cho hàm dự kiến ​​byte.

Dưới đây là một số chuyển đổi ví dụ:

> 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 

Nếu bạn chỉ cần chức năng như trên như một cách nhanh chóng để tạo ra một giá trị byte lớn con người có thể đọc được, điều này chắc chắn là quá mức cần thiết, nhưng nếu bạn cần để quản lý giá trị byte trên nhiều thang đo, điều này có thể phù hợp.

1

Cách duy nhất để có tham số tùy chọn trong F # là sử dụng phương thức thay vì hàm. Để xác định rằng tham số là tùy chọn, hãy đặt ? trước đó. Từ các tài liệu 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

Nếu sizeFmt chỉ được sử dụng bởi humanReadable, nó làm cho tinh thần để làm cho nó một chức năng bên trong. Điều đó tránh được vấn đề 'mặc định tham số'.

Ngoài ra, đánh dấu chức năng bên ngoài inline khiến nó chấp nhận bất kỳ loại nào n hỗ trợ chuyển đổi rõ ràng thành 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

Câu trả lời hiện có đã giải thích rằng việc giữ chức năng bao bọc là một ý tưởng hay, vì điều này cho phép mã càng nhiều mô-đun càng tốt. Điều này sẽ không rõ ràng trong một ví dụ đơn giản, nhưng trong các dự án thực tế, nó sẽ là một lợi thế lớn để có thể mở rộng sizeFmt tại một thời điểm nào đó bằng cách phơi bày nhiều tham số hơn - xem xét đôi khi bạn cần "Hertz" thay vì "Bytes" (và chia 1000 thay vì 1024) hoặc mẫu định dạng sting (năm chữ số thập phân) hoặc danh sách nhân rộng có thể bản địa hóa, v.v.


Đối với câu hỏi thứ hai, chuyển sang float, giải pháp rất đơn giản: làm value một statically-resolved type:

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

Điều này sẽ làm humanReadable để có các loại hạn chế sau đây:

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

Cách sử dụng:

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

Sử dụng float trong hàm bên trong có vẻ ổn: mọi lỗi vòng sẽ bị xóa bởi một loạt các bộ phận.

Các vấn đề liên quan