2011-11-27 34 views
14

Đối với hiệu suất và khả năng mở rộng trong thiết kế bao bì, nó là tốt nhất để:Cách tiếp cận thiết kế: Quá tải so với Switch?

  1. ... 'quá tải' tên hàm (để cho Mathematica loại ra phiên bản sử dụng dựa trên các mẫu/điều kiện/kiểm tra và cách các hệ thống định nghĩa đơn đặt hàng)?
  2. … hoặc để xây dựng một chức năng duy nhất với Công tắc [] (hoặc lệnh tương tự) để đánh giá trực tiếp?

Tính biểu cảm của toán học thường gây nhầm lẫn cho tôi với các vấn đề ngớ ngẩn (?) Như thế này.

+3

Tôi cảm thấy rằng bạn nên thêm chi tiết hơn một chút vào câu hỏi của mình. Nó là cực kỳ rộng vào thời điểm này. –

+1

Không thể nói rằng tôi không đồng ý với đánh giá của bạn. Ban đầu tôi đã viết nhiều hơn, nhưng nhận ra rằng tôi đã phục hồi những điều cơ bản mà hầu hết chúng ta biết từ trải nghiệm thủ công và cá nhân. Tôi sẽ cố gắng và làm rõ nếu tôi có thể --- nhưng sau khi da gấu trò chơi. :) – telefunkenvf14

+2

Tôi cũng đã viết một câu trả lời dài hơn, và sau đó nhận ra rằng các ví dụ của tôi bị cấm, vì vậy tôi đã để nó như vậy. Thưởng thức các trò chơi. –

Trả lời

6

Câu hỏi của bạn khá mơ hồ như được viết và có các cách diễn giải khác nhau về "quá tải" sẽ thay đổi câu trả lời của tôi. Tuy nhiên, nếu bạn đang nói về việc quá tải các chức năng của riêng bạn liên quan đến các kiểu khác nhau (đầu) và các mẫu đối số, thì bằng mọi cách, hãy tận dụng lợi thế của việc khớp mẫu tích hợp chặt chẽ của Mathematica.


Để cung cấp ví dụ thực tế, tôi sẽ sử dụng this solution of mine. Để tham khảo:

f[k_, {}, c__] := If[Plus[c] == k, {{c}}, {}] 

f[k_, {x_, r___}, c___] := Join @@ (f[k, {r}, c, #] & /@ Range[0, Min[x, k - Plus[c]]]) 

Nếu tôi viết lại f mà không phù hợp với mô hình và gọi nó là g:

g = Function[{k, L, c}, 
     If[L === {}, 
     If[[email protected] == k, {c}, {}], 
     Join @@ (g[k, [email protected], Append[c, #]] & /@ Range[0, Min[[email protected], k - [email protected]]]) 
     ] 
    ]; 

tôi cảm thấy rằng đây là chưa rõ ràng, và nó chắc chắn ít là thuận tiện để viết. Tôi đã phải sử dụng các hàm RestFirst rõ ràng và tôi phải giới thiệu Append vì tôi không thể chứa số lượng đối số thay đổi. Điều này cũng đòi hỏi một đối số thứ ba giả sử dụng: {}.

Thời gian hiển thị rằng hình thức ban đầu cũng là nhanh hơn đáng kể:

f[12, {1, 5, 8, 10, 9, 9, 4, 10, 8}]; // Timing 
g[12, {1, 5, 8, 10, 9, 9, 4, 10, 8}, {}]; // Timing 
{0.951, Null}
{1.576, Null}

Đáp lại câu trả lời Timo, tôi cảm thấy nó có giá trị để chia sẻ kết quả thời gian của tôi, như chúng khác với anh. (Tôi đang sử dụng Mathematica 7 trên Windows 7.) Hơn nữa, tôi tin rằng anh ta phức tạp phiên bản DownValues ​​ngoài chức năng của phiên bản Switch.

Thứ nhất, timings của tôi về các chức năng của mình như là bằng văn bản, nhưng sử dụng một loạt các giá trị:

Array[switchFunc2, 1*^6]; // Timing 
Array[overloadFunc2, 1*^6]; // Timing 
{1.014, Null}
{0.749, Null}

Vì vậy, ngay cả khi viết, các DownValues ​​chức năng là nhanh hơn đối với tôi.Nhưng điều kiện thứ hai là không cần thiết:

ClearAll[overloadFunc2] 

overloadFunc2[a_ /; a < 5] := 6; 
overloadFunc2[a_] := 4; 

Array[overloadFunc2, 1*^6]; // Timing 
{0.546, Null}

Tất nhiên, trong trường hợp của một hàm đơn giản như người ta cũng có thể sử dụng If:

ifFunc[a_] := If[a < 5, 6, 4] 

Array[ifFunc, 1*^6]; // Timing 
{0.593, Null}

Và nếu điều này được viết như một hàm thuần túy mà Mathematica biên dịch bên trong Array:

ClearAll[ifFunc] 
ifFunc = If[# < 5, 6, 4] &; 

Array[ifFunc, 1*^6]; // Timing 
{0.031, Null}
+0

Điều này nhắc tôi về lý do thứ ba tôi thích các chức năng - có thể đặt tên cho các mẫu và sử dụng lại chúng sau này. –

+0

Kiểm tra tốt thời gian! Về việc tải quá nhiều, tôi muốn tranh luận rằng đối với chức năng giống nhau, cả hai DownValues ​​của tôi là cần thiết. Hãy suy nghĩ về 'overloadFunc2 [" string "]' và 'switchFunc2 [" string "]'. Tất nhiên các cuộc triển khai của tôi rất đơn giản và bất kỳ ứng dụng thế giới thực nào cũng phải được thử nghiệm một mình. – Timo

+0

@Timo Tôi đang bối rối: là '* Func2' được cho là xử lý chuỗi không? Làm sao? –

9

Ngoại trừ trường hợp rất đơn giản, tôi thích sử dụng hàm có nhiều định nghĩa thay vì Switch. Lý do cho điều này là ba lần:

  1. Tôi thấy dễ đọc mã miễn là chức năng được đặt tên tốt.
  2. Trường hợp thông báo lỗi/lỗi dễ dàng hơn để đặt mặc định phù hợp và gọi lại hàm.
  3. Nếu bạn sử dụng một hàm, bạn có thể sử dụng các mẫu được đặt tên khi tính kết quả.

Sửa

Dưới đây là một ví dụ tạo ra như là một ví dụ về # 2 cho Sjoerd:

createNColors[fn_, Automatic, n_] := Table[Hue[i/n], {i, n}] 

createNColors[fn_, colors_List, n_] := PadRight[colors, n, colors] 

createNColors[fn_, color:(Hue | RGBColor | CMYKColor | GrayLevel)[__], n_] := 
    Table[color, {n}] 

createNColors[fn_, color_, n_] := (
    Message[fn::"color", HoldForm[color]]; 
    createNColors[fn, Automatic, n] 
    ) 

Nó có thể được sử dụng để tạo ra một tập hợp các n màu sắc cho một số tùy chọn.

+0

Bạn nhận được +1 nếu bạn có thể cung cấp một ví dụ tốt đẹp về điểm thứ hai của bạn ;-) –

+0

Cảm ơn, rất có tính hướng dẫn! Định nghĩa cuối cùng, không phải là cũng cho 'createNColors' thay vì' createNStyle', để bạn có thể xử lý thích hợp một cuộc gọi như 'createNColors [fn, CIELab [1, 0.5, 0.25], 6]'? –

+0

@Sjoerd: Cảm ơn bạn đã phát hiện lỗi. Tôi hoàn toàn đổi tên mọi thứ vào phút cuối. –

9

Để trả lời phần thực hiện các câu hỏi của bạn, hãy xem xét hai ví dụ sau đây của quá tải và sử dụng Switch[]

switchFunc[a_] := Switch[a, _String, 5, _Integer, var, _Symbol, "string"] 

overloadFunc[a_String] := 5; 
overloadFunc[a_Integer] := var; 
overloadFunc[a_Symbol] := "string"; 

này là cực kỳ đơn giản, nhưng cũng đủ để chứng minh sự khác biệt trong hoạt động

In[1] := [email protected][switchFunc, x, 1000000] 
Out[1] := {3.435, "string"} 

In[2] := [email protected][overloadFunc, x, 1000000] 
Out[2] := {0.754, "string"} 

Tuy nhiên, nếu bạn dự định làm quá tải chức năng của mình dựa trên các thử nghiệm có điều kiện, hiệu suất sẽ kém hơn Switch[]:

switchFunc2[a_] := Switch[a < 5, True, 6, False, 4]; 

overloadFunc2[a_ /; a < 5] := 6; 
overloadFunc2[a_ /; a > 5] := 4; 
overloadFunc2[a_] := a; 

In[3] := [email protected][switchFunc2, 4, 1000000] 
Out[3] := {2.63146, 4} 

In[4] := [email protected][overloadFunc2, 6, 1000000] 
Out[4] := {4.349, 6} 

EDIT: Thời gian trong câu trả lời này được thực hiện bằng cách sử dụng Mathematica 8.0.1 trên OS X 10.7.2. Xem câu trả lời của Mr.Wizard cho một số kết quả bổ sung khi lệnh trên bị đảo ngược. Tuy nhiên, tôi nghĩ rằng nó là một ý tưởng hiệu suất xấu khôn ngoan để làm kiểm tra mẫu hợp lý trên các đối số chức năng.

Từ quan điểm thiết kế của chế độ xem, trải nghiệm cá nhân của tôi là Switch[] và thật là khủng khiếp vì chúng khó đọc và chậm. Tuy nhiên, tôi cũng nghĩ rằng việc có cùng một chức năng hoạt động khác nhau dựa trên loại đối số thường là thiết kế tồi và làm cho mã của bạn khó khăn hơn nhiều (mặc dù nó có thể dễ đọc hơn).

11

Đây là một câu hỏi rộng, nhưng tôi sẽ nhân cơ hội này để đưa ra câu trả lời rộng ...

Tôi ủng hộ người ta nên nắm lấy mô hình chính của ngôn ngữ lập trình, thay vì cố gắng chống lại hoặc viết mã theo thành ngữ của một ngôn ngữ khác. Mathematica được xây dựng xung quanh khái niệm phù hợp với mô hình như vậy, IMHO, chúng ta nên luôn luôn xem xét phù hợp với mô hình đầu tiên khi cố gắng thể hiện bản thân. Theo nguyên tắc đó, tôi sẽ ưu tiên các định nghĩa trên Switch.

Khi đặt câu hỏi về hiệu suất, tôi ngày càng trở nên bực mình bởi sự nhấn mạnh ngày càng tăng trên các tiêu chuẩn vi sinh khi so sánh các cấu trúc Mathematica. Trong khi nó có giá trị để biết chi phí liên quan đến cấu trúc, chúng ta nên chú ý Knuth (hoặc là Hoare?): "Chúng ta nên quên đi hiệu quả nhỏ, nói khoảng 97% thời gian: tối ưu hóa sớm là gốc rễ của mọi điều ác". "Cái ác" là sự mất khả năng đọc trong một chương trình mà, vì lợi ích của hiệu quả, sử dụng một số cách tiếp cận tối nghĩa hoặc gián tiếp để đạt được hiệu quả. Đây là danh sách kiểm tra hiệu suất của tôi:

  1. Hiệu suất có phải là vấn đề không? Nếu không, hãy bỏ qua phần còn lại của danh sách kiểm tra.

  2. Nút cổ chai hiệu suất ở đâu? Một profiler giúp ở đây, nhưng thường nút cổ chai có thể được tìm thấy dễ dàng bằng cách kiểm tra hoặc một vài báo cáo in. Sau đó ...

  3. Thuật toán có hiệu quả không? Rất thường xuyên: có một vòng lặp lồng nhau kép có thể được tuyến tính hóa hoặc được hỗ trợ bởi một lược đồ lập chỉ mục không?

  4. Được rồi, thuật toán là tốt vì vậy tôi đoán đã đến lúc để microbenchmark.

Tôi không biết liệu việc sử dụng Mathematica của tôi có đủ tham vọng hay không, nhưng phần lớn thời gian tôi không vượt qua bướC# 1. Và sau đó # 3 bắt hầu hết phần còn lại. Trong Mathematica, tôi thấy tôi thường rất vui mừng rằng tôi có thể thực hiện một số nhiệm vụ đầy tham vọng với một lượng nhỏ mã - hiệu suất tổng thể thường không đi vào hình ảnh.

Uh-oh, tôi nên bỏ hộp xà phòng đi. Xin lỗi vì việc đó.

+2

Một điểm tuyệt vời về việc sử dụng mô hình tự nhiên. Về việc sử dụng mma của bạn có lẽ không đủ tham vọng, có khả năng là việc bạn sử dụng nó rất phức tạp. Ít nhất trong trường hợp của tôi, tôi chủ yếu sử dụng mma để làm điều gì đó dọc theo các dòng: xây dựng ma trận thưa thớt rất lớn, thu được giá trị riêng và vectơ, làm điều gì đó đơn giản với chúng. Thông thường, các bộ phận tốn nhiều thời gian nhất là xây dựng các ma trận, không giải quyết được hệ thống eigens. Và trong trường hợp đó microbenchmarking (hoặc chỉ là 'Compile' thành C) là hy vọng cuối cùng trước C. Để sử dụng tinh vi hơn, tôi hoàn toàn đồng ý với bạn. – acl

+4

Uh-oh, anh ấy đã bực bội. http://www.youtube.com/watch?v=0t2pWUWE1Y8 –

+0

Trừ khi bộ nhớ của tôi không thành công, tôi không bao giờ chúc mừng bạn về đại diện 10K ... Xin chúc mừng! –

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