2010-04-24 29 views
9

Tôi có một chức năng trông như sau:Hiệu suất cà ri F #?

let isInSet setElems normalize p = 
     normalize p |> (Set.ofList setElems).Contains 

Chức năng này có thể được sử dụng để nhanh chóng kiểm tra xem một yếu tố là một phần ngữ nghĩa của một số bộ; ví dụ, để kiểm tra xem một đường dẫn tập tin thuộc về một tập tin html:

let getLowerExtension p = (Path.GetExtension p).ToLowerInvariant() 
let isHtmlPath = isInSet [".htm"; ".html"; ".xhtml"] getLowerExtension 

Tuy nhiên, khi tôi sử dụng một chức năng như, hiệu suất trên là nghèo từ đánh giá của cơ quan chức năng như được viết trong "isInSet" dường như bị trì hoãn cho đến khi tất cả các tham số được biết - đặc biệt, các bit bất biến như (Set.ofList setElems).Contains được đánh giá lại mỗi lần thực hiện isHtmlPath.

Làm cách nào tốt nhất tôi có thể duy trì sự kém phát triển của F #, bản chất có thể đọc được trong khi vẫn nhận được hành vi hiệu quả hơn trong đó việc xây dựng bộ được đánh giá trước.

Ở trên là chỉ là một ví dụ; Tôi đang tìm cách tiếp cận chung để tránh làm tôi thất vọng trong chi tiết triển khai - nếu có thể Tôi muốn tránh bị phân tâm bởi các chi tiết như lệnh thi hành vì điều đó thường không quan trọng đối với tôi và loại phá hoại điểm bán hàng của lập trình chức năng.

Trả lời

5

Câu trả lời từ Kha cho biết cách tối ưu hóa mã theo cách thủ công bằng cách sử dụng đóng trực tiếp. Nếu đây là mẫu thường xuyên mà bạn cần sử dụng thường xuyên, bạn cũng có thể xác định hàm bậc cao hơn để tạo mã hiệu quả từ hai hàm - số đầu tiên thực hiện trước khi xử lý của một số đối số và thực hiện việc xử lý thực tế thực tế sau khi nhận được các đối số còn lại.

Mã này sẽ trông như thế này:

let preProcess finit frun preInput = 
    let preRes = finit preInput 
    fun input -> frun preRes input 

let f : string list -> ((string -> string) * string) -> bool = 
    preProcess 
    Set.ofList       // Pre-processing of the first argument 
    (fun elemsSet (normalize, p) ->  // Implements the actual work to be 
     normalize p |> elemsSet.Contains) // .. done once we get the last argument 

Đó là một câu hỏi liệu đây là tao nhã hơn mặc dù ...

khác (điên) ý kiến ​​cho rằng bạn có thể sử dụng biểu thức tính toán cho việc này. Định nghĩa của người xây dựng tính toán cho phép bạn làm điều này là rất phi tiêu chuẩn (nó không phải là thứ mà mọi người thường làm với họ và nó không liên quan đến monads hay bất kỳ lý thuyết nào khác). Tuy nhiên, chúng ta có thể viết này:

type CurryBuilder() = 
    member x.Bind((), f:'a -> 'b) = f 
    member x.Return(a) = a 
let curry = new CurryBuilder() 

Trong curry tính toán, bạn có thể sử dụng let! để biểu thị mà bạn muốn để có những số tiếp theo của hàm (sau khi đánh giá các mã preceeding):

let f : string list -> (string -> string) -> string -> bool = curry { 
    let! elems =() 
    let elemsSet = Set.ofList elems 
    printf "elements converted" 
    let! normalize =() 
    let! p =() 
    printf "calling" 
    return normalize p |> elemsSet.Contains } 

let ff = f [ "a"; "b"; "c" ] (fun s -> s.ToLower()) 
// Prints 'elements converted' here 
ff "C" 
ff "D" 
// Prints 'calling' two times 

Dưới đây là một số tài nguyên với nhiều thông tin về các biểu thức tính toán:

+0

Bạn có liên kết nào bạn muốn đọc về biểu thức tính toán không? Tôi có ý tưởng nhưng không nắm bắt được chi tiết ... –

+0

@Eamon: Tôi đã thêm hai liên kết vào cuối câu trả lời của tôi. –

+0

Cảm ơn ví dụ tuyệt vời và cách sử dụng các biểu thức tính toán hơi tâm trí! Điều này cuối cùng đã thuyết phục tôi nhìn kỹ hơn - ngay cả khi tôi không chắc họ thực sự cần ở đây. –

2
  1. Cà ri không bị tổn thương. Currying đôi khi giới thiệu đóng cửa là tốt. Chúng thường hiệu quả. tham chiếu đến this question Tôi đã hỏi trước đây. Bạn có thể sử dụng nội dòng để tăng hiệu suất nếu cần.

  2. Tuy nhiên, vấn đề hiệu suất của bạn trong ví dụ này chủ yếu là do mã của bạn:

    normalize p |> (Set.ofList setElems).Contains

đây bạn cần phải thực hiện Set.ofList setElems thậm chí bạn cà ri nó. Nó tốn thời gian O (n log n). Bạn cần thay đổi loại setElems thành F # Set, chứ không phải Danh sách bây giờ. Btw, cho tập hợp nhỏ, sử dụng danh sách nhanh hơn so với bộ ngay cả khi truy vấn.

+0

Tôi nhận ra tôi có thể thay đổi ngữ nghĩa chức năng của tôi để giải quyết các vấn đề về hiệu năng - nhưng điều đó không phải lúc nào cũng dễ dàng và không mong muốn. Mã hiện tại dễ đọc; nếu tôi cần phải vượt qua trong bộ mã đã nhận được lâu hơn - mỗi mới 'isHtmlPath' giống như chức năng là nhận được nhiều hơn boilerplate. Vì vậy, tôi muốn một mô hình thiết kế tốt hơn: nơi bit precomputable được precomputed mà không có tôi (lập trình lười biếng) cần phải cung cấp cho nó nhiều trường hợp từng trường hợp suy nghĩ. –

+0

@Eamon Tôi thấy điểm của bạn. Tôi không nghĩ rằng F # thực hiện loại tối ưu hóa này ngay bây giờ. Vì vậy, chúng tôi lập trình chịu trách nhiệm thiết kế giao diện hiệu quả. –

9

Miễn là F # không phân biệt giữa mã thuần túy và không tinh khiết, tôi nghi ngờ chúng ta sẽ thấy tối ưu hóa loại đó. Bạn có thể, tuy nhiên, làm cho currying rõ ràng.

let isInSet setElems = 
    let set = Set.ofList setElems 
    fun normalize p -> normalize p |> set.Contains 

isHtmlSet bây giờ sẽ gọi isInSet chỉ một lần để có được việc đóng cửa, đồng thời thực hiện ofList.

+1

Tôi nghĩ rằng đây là những gì tôi sẽ làm - và nó có thể được viết thậm chí ngắn hơn, quá: 'let isInSet setElems normalize = normalize >> (Set.ofList setElems) .Contains'. Tuy nhiên, về mặt phong cách, bạn có thích sự tổng quát hơn, linh hoạt hơn "trả lại một biểu thức lamba" hoặc phiên bản thành phần chức năng ít linh hoạt hơn, ít tổng quát hơn nhưng ngắn hơn, trong mã bình thường? –

5

@ Câu trả lời của Kha được phát hiện. F # không thể viết lại

// effects of g only after both x and y are passed 
let f x y = 
    let xStuff = g x 
    h xStuff y 

vào

// effects of g once after x passed, returning new closure waiting on y 
let f x = 
    let xStuff = g x 
    fun y -> h xStuff y 

trừ khi nó biết rằng g không có tác dụng, và trong .NET Framework ngày hôm nay, nó thường là không thể suy luận về những ảnh hưởng của 99% của tất cả các biểu thức. Điều này có nghĩa là lập trình viên vẫn chịu trách nhiệm về thứ tự đánh giá mã hóa rõ ràng như trên.

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