2010-05-15 7 views
20

Tôi đang cố định nghĩa một hàm, nhân số, sử dụng các ràng buộc kiểu cấu trúc (yêu cầu các thành viên tĩnh Zero, One, +, và /) tương tự như Seq.sum để nó có thể được sử dụng với int , dài, bigint, vv Tôi dường như không thể có được cú pháp đúng, và không thể tìm thấy nhiều tài nguyên về chủ đề này. Đây là những gì tôi có, xin vui lòng giúp đỡ.F # Static Member Type Constraints

let inline factorize (n:^NUM) = 
    ^NUM : (static member get_Zero: unit->(^NUM)) 
    ^NUM : (static member get_One: unit->(^NUM)) 
    let rec factorize (n:^NUM) (j:^NUM) (flist: ^NUM list) = 
     if n = ^NUM.One then flist 
     elif n % j = ^NUM.Zero then factorize (n/j) (^NUM.One + ^NUM.One) (j::flist) 
     else factorize n (j + ^NUM.One) (flist) 
    factorize n (^NUM.One + ^NUM.One) [] 

Trả lời

21

Đây là cách tôi muốn viết nó:

module NumericLiteralG = begin 
    let inline FromZero() = LanguagePrimitives.GenericZero 
    let inline FromOne() = LanguagePrimitives.GenericOne 
end 

let inline factorize n = 
    let rec factorize n j flist = 
    if n = 1G then flist 
    elif n % j = 0G then factorize (n/j) j (j::flist) 
    else factorize n (j + 1G) (flist) 
    factorize n (1G + 1G) [] 

Loại suy ra cho động lực ở đây là cách quá chung chung, nhưng các chức năng sẽ làm việc như bạn mong muốn. Bạn có thể buộc một chữ ký lành mạnh hơn và tập các ràng buộc nếu bạn muốn bằng cách thêm các loại rõ ràng để một số các biểu thức tổng quát:

let inline factorize (n:^a) : ^a list = 
    let (one : ^a) = 1G 
    let (zero : ^a) = 0G 
    let rec factorize n (j:^a) flist = 
    if n = one then flist 
    elif n % j = zero then factorize (n/j) j (j::flist) 
    else factorize n (j + one) (flist) 
    factorize n (one + one) [] 
+0

Thật tuyệt vời. –

+1

Bạn nói đúng - công trình này - khá bất ngờ đối với tôi :-). Tôi không chắc chắn những gì đang xảy ra ở đây, bởi vì 'factorize' được biên dịch như một hàm tổng quát. Nó sử dụng triển khai động 'GetZero' (có thể tương tự như sử dụng' NumericAssociations'), nhưng tôi không chắc cách thức hoạt động (không đăng ký rõ ràng các hoạt động cho kiểu của riêng bạn). Nếu bạn hiểu cách làm việc này, tôi sẽ khá quan tâm đến các chi tiết :-). –

+0

Chỉ cần nhận thấy tối ưu hóa tốt đẹp mà bạn đã thêm vào trong trường hợp elif cho chính thuật toán. –

4

Thứ nhất, đây là một ví dụ nhỏ cho thấy cách mà cú pháp sẽ giống như thế:

let inline zero< ^NUM when ^NUM : (static member get_Zero: unit-> ^NUM)> 
    (n:^NUM) = 
    (^NUM : (static member get_Zero : unit -> ^NUM)()) 

Trong một số trường hợp, bạn không cần phải viết những hạn chế một cách rõ ràng (F # biên dịch sẽ thực sự cảnh báo bạn về điều đó nếu bạn viết ở trên), bởi vì một số thành viên tĩnh được biết đến với trình biên dịch và có các hàm chuẩn để sử dụng chúng. Vì vậy, bạn có thể sử dụng các chức năng và trình biên dịch sẽ suy ra hạn chế:

let inline zero (n:^T) = 
    LanguagePrimitives.GenericZero<^T> 

Thật không may, điều này thực sự không giúp bạn, bởi vì chức năng đệ quy không thể được khai báo là inline (vì lý do rõ ràng - trình biên dịch có thể không inline chức năng tại thời gian biên dịch, bởi vì nó không biết bao nhiêu lần), do đó, các ràng buộc tĩnh có lẽ không đủ mạnh cho vấn đề của bạn.

[EDIT: Đây thực sự là có thể cho một số chức năng (xem câu trả lời kvb của)]

Tôi nghĩ rằng bạn sẽ cần NumericAssociations thay, được alreaday thảo luận in this question (chúng được xử lý trong thời gian chạy, vì vậy chúng chậm hơn - nhưng được sử dụng để thực hiện ví dụ loại ma trận F # - ma trận có thể lưu trữ thông tin thu được động, do đó, nó là hợp lý hiệu quả).

7

Lấy cảm hứng từ câu trả lời bằng NumericLiterals kvb, tôi đã được thúc đẩy để phát triển một phương pháp mà sẽ cho phép chúng tôi để ép buộc các chữ ký kiểu "lành mạnh" mà không cần phải thêm các chú thích kiểu mở rộng.

Đầu tiên chúng ta định nghĩa một số chức năng helper và loại wrapper cho nguyên thủy ngôn ngữ:

let inline zero_of (target:'a) : 'a = LanguagePrimitives.GenericZero<'a> 
let inline one_of (target:'a) : 'a = LanguagePrimitives.GenericOne<'a> 
let inline two_of (target:'a) : 'a = one_of(target) + one_of(target) 
let inline three_of (target:'a) : 'a = two_of(target) + one_of(target) 
let inline negone_of (target:'a) : 'a = zero_of(target) - one_of(target) 

let inline any_of (target:'a) (x:int) : 'a = 
    let one:'a = one_of target 
    let zero:'a = zero_of target 
    let xu = if x > 0 then 1 else -1 
    let gu:'a = if x > 0 then one else zero-one 

    let rec get i g = 
     if i = x then g 
     else get (i+xu) (g+gu) 
    get 0 zero 

type G<'a> = { 
    negone:'a 
    zero:'a 
    one:'a 
    two:'a 
    three:'a 
    any: int -> 'a 
}  

let inline G_of (target:'a) : (G<'a>) = { 
    zero = zero_of target 
    one = one_of target 
    two = two_of target 
    three = three_of target 
    negone = negone_of target 
    any = any_of target 
} 

Sau đó, chúng ta có:

let inline factorizeG n = 
    let g = G_of n 
    let rec factorize n j flist = 
     if n = g.one then flist 
     elif n % j = g.zero then factorize (n/j) j (j::flist) 
     else factorize n (j + g.one) (flist) 
    factorize n g.two [] 

[Sửa: do một lỗi rõ ràng với F # 2.0 /. NET 2.0, factorizen, factorizeL và factorizeI bên dưới chạy chậm hơn đáng kể so với factorizeG khi được biên dịch trong chế độ Release-mode nhưng nếu không chạy nhanh hơn một chút như mong đợi - xem F# performance question: what is the compiler doing?]

Hoặc chúng ta có thể mang nó một vài bước xa hơn (lấy cảm hứng từ Expert F #, p.110):

let inline factorize (g:G<'a>) n = //' 
    let rec factorize n j flist = 
     if n = g.one then flist 
     elif n % j = g.zero then factorize (n/j) j (j::flist) 
     else factorize n (j + g.one) (flist) 
    factorize n g.two [] 

//identical to our earlier factorizeG 
let inline factorizeG n = factorize (G_of n) n 

let gn = G_of 1 //int32 
let gL = G_of 1L //int64 
let gI = G_of 1I //bigint 

//allow us to limit to only integral numeric types 
//and to reap performance gain by using pre-computed instances of G 
let factorizen = factorize gn 
let factorizeL = factorize gL 
let factorizeI = factorize gI 

Ngoài ra, đây là một phiên bản mở rộng của NumericLiteralG kvb của cho phép chúng ta sử dụng "2G", " -8G ", v.v.Mặc dù tôi không thể tìm ra cách để thực hiện một chiến lược ghi nhớ (mặc dù điều đó có thể thực hiện được đối với G.any).

module NumericLiteralG = 
    let inline FromZero() = LanguagePrimitives.GenericZero 
    let inline FromOne() = LanguagePrimitives.GenericOne 
    let inline FromInt32(n:int):'a = 
     let one:'a = FromOne() 
     let zero:'a = FromZero() 
     let nu = if n > 0 then 1 else -1 
     let gu:'a = if n > 0 then one else zero-one 

     let rec get i g = 
      if i = n then g 
      else get (i+nu) (g+gu) 
     get 0 zero