2012-01-09 38 views
14

Tôi đã xem xét các cách kiểm tra các đối số của hàm. Tôi nhận thấy rằng MatrixQ có 2 đối số, thứ hai là một thử nghiệm để áp dụng cho từng phần tử.Cách được khuyến nghị để kiểm tra xem danh sách có phải là danh sách các số trong đối số của hàm không?

Nhưng ListQ chỉ nhận một đối số. (cũng vì một lý do nào đó, ?ListQ không có trang trợ giúp, chẳng hạn như ?MatrixQ).

Vì vậy, ví dụ, để kiểm tra xem một cuộc tranh cãi với một chức năng là một ma trận số, tôi viết

ClearAll[foo] 
foo[a_?(MatrixQ[#, NumberQ] &)] := Module[{}, a + 1] 

Điều gì sẽ là một cách tốt để làm tương tự cho một danh sách? Dưới đây chỉ kiểm tra mà đầu vào là một danh sách

ClearAll[foo] 
foo[a_?(ListQ[#] &)] := Module[{}, a + 1] 

tôi có thể làm một cái gì đó như thế này:

ClearAll[foo] 
foo[a_?(ListQ[#] && (And @@ Map[NumberQ[#] &, # ]) &)] := Module[{}, a + 1] 

để foo[{1, 2, 3}] sẽ làm việc, nhưng foo[{1, 2, x}] sẽ không (giả sử x là một biểu tượng). Nhưng có vẻ như tôi là một người phức tạp để làm điều này.

Câu hỏi: Bạn có biết một cách tốt hơn để kiểm tra xem một cuộc tranh cãi là một danh sách và cũng kiểm tra các nội dung danh sách là số

Và một liên quan (hoặc của bất kỳ Head khác biết đến Mathematica?) câu hỏi: Bất kỳ vấn đề hiệu suất thời gian chạy chính nào với việc thêm kiểm tra như vậy vào mỗi đối số? Nếu vậy, bạn có đề xuất các kiểm tra này bị xóa sau khi thử nghiệm và phát triển được hoàn thành để chương trình cuối cùng chạy nhanh hơn không? (ví dụ, có một phiên bản của mã với tất cả các kiểm tra trong, cho sự phát triển/thử nghiệm, và một phiên bản mà không cho sản xuất).

+0

Tôi cũng đôi khi thấy mình tự hỏi về thực hiện hit của này, * + 1 * cho câu hỏi đó – Szabolcs

+0

Tôi có nên làm cho phần biểu diễn câu hỏi như một câu hỏi riêng biệt hoặc chỉ để lại nó ở đây ? – Nasser

+0

Tôi để nó ở đây. Tôi nhấn mạnh chúng đậm bởi vì tôi nghĩ rằng nó dễ dàng hơn nhiều để đọc và * thông báo * theo cách này. Nếu không nhiều người sẽ không bao giờ đọc được điều đó. Nếu bạn không chấp nhận câu trả lời của tôi, câu hỏi sẽ thu hút nhiều lượt xem hơn. – Szabolcs

Trả lời

11

Về hit hiệu suất (kể từ câu hỏi đầu tiên của bạn đã được trả lời đã được) - bằng mọi cách, thực hiện kiểm tra , nhưng trong các chức năng cấp cao nhất của bạn (nhận dữ liệu trực tiếp từ người dùng chức năng của bạn. Người dùng cũng có thể là một mô-đun độc lập khác, do bạn hoặc người khác viết). Không đặt các kiểm tra này trong tất cả các hàm trung gian của bạn, vì các kiểm tra đó sẽ trùng lặp và thực sự không được điều chỉnh.

EDIT

Để giải quyết vấn đề sai sót trong các chức năng trung gian, nuôi dưỡng bởi @Nasser trong các ý kiến: có một kỹ thuật rất đơn giản mà cho phép một để chuyển đổi mô hình-kiểm tra và tắt trong "một cú nhấp chuột ". Bạn có thể lưu trữ các mẫu của bạn trong các biến bên trong gói của bạn, được định nghĩa trước các định nghĩa hàm của bạn.

Dưới đây là ví dụ, trong đó f là chức năng cấp cao nhất, trong khi gh là "hàm bên trong". Chúng ta định nghĩa hai mẫu: cho hàm chính và cho những người bên trong, như vậy:

Clear[nlPatt,innerNLPatt ]; 
nlPatt= _?(!VectorQ[#,NumericQ]&); 
innerNLPatt = nlPatt; 

Bây giờ, chúng ta xác định chức năng của chúng tôi:

ClearAll[f,g,h]; 
f[vector:nlPatt]:=g[vector]+h[vector]; 
g[nv:innerNLPatt ]:=nv^2; 
h[nv:innerNLPatt ]:=nv^3; 

Lưu ý rằng các mô hình được thay thế trong các định nghĩa tại định nghĩa thời gian, không chạy theo thời gian, do đó, điều này hoàn toàn tương đương với việc mã hóa các mẫu đó bằng tay.Khi bạn kiểm tra, bạn chỉ cần thay đổi một dòng: từ

innerNLPatt = nlPatt 

để

innerNLPatt = _ 

và tải lại gói của bạn.

Câu hỏi cuối cùng là - làm thế nào để bạn nhanh chóng tìm thấy lỗi? Tôi đã trả lời rằng here, trong các phần "Thay vì trả lại $Failed, người ta có thể ném ngoại lệ, sử dụng Throw"."Lập trình meta và tự động hóa".

END EDIT

tôi bao gồm một cuộc thảo luận ngắn gọn về vấn đề này trong cuốn sách của tôi here. Trong ví dụ đó, hiệu suất đạt được ở mức tăng 10% thời gian chạy, IMO là đường biên giới có thể chấp nhận được. Trong trường hợp ở bàn tay, việc kiểm tra đơn giản hơn và hiệu suất phạt ít hơn nhiều. Nói chung, đối với một hàm là bất kỳ phép kiểm tra loại văn bản nào được viết chính xác, chuyên sâu, chi phí chỉ chiếm một phần nhỏ trong tổng thời gian chạy.

Một vài thủ thuật mà là tốt để biết:

  • Pattern-khớp có thể rất nhanh, khi được sử dụng cú pháp (không Condition hay PatternTest hiện diện trong mẫu).

Ví dụ:

randomString[]:[email protected][{97,122},5]; 
rstest = Table[randomString[],{1000000}]; 

In[102]:= MatchQ[rstest,{__String}]//Timing 
Out[102]= {0.047,True} 

In[103]:= MatchQ[rstest,{__?StringQ}]//Timing 
Out[103]= {0.234,True} 

Chỉ vì trong trường hợp này các PatternTest đã được sử dụng, việc kiểm tra là chậm hơn nhiều, vì đánh giá được gọi bởi các mô hình-khớp cho mọi phần tử, trong khi trong lần đầu tiên trường hợp, tất cả mọi thứ là hoàn toàn cú pháp và tất cả được thực hiện bên trong pattern-matcher.


  • Điều này cũng đúng cho giải nén danh sách số (chênh lệch thời gian là tương tự). Tuy nhiên, đối với đóng gói danh sách số, MatchQ và các chức năng thử nghiệm mẫu khác không giải nén cho một số mẫu đặc biệt, hơn nữa, đối với một số mẫu, séc là tức thời.

Dưới đây là một ví dụ:

In[113]:= 
test = RandomInteger[100000,1000000]; 

In[114]:= MatchQ[test,{__?IntegerQ}]//Timing 
Out[114]= {0.203,True} 

In[115]:= MatchQ[test,{__Integer}]//Timing 
Out[115]= {0.,True} 

In[116]:= Do[MatchQ[test,{__Integer}],{1000}]//Timing 
Out[116]= {0.,Null} 

Cùng, rõ ràng, có vẻ là đúng đối với các chức năng như VectorQ, MatrixQArrayQ với các vị từ nhất định (NumericQ) - những thử nghiệm này là cực kỳ hiệu quả.


  • Rất nhiều phụ thuộc vào cách bạn viết thử nghiệm của bạn, ví dụ: ở mức độ nào bạn sử dụng lại các cấu trúc hiệu quả Mathematica.

Ví dụ, chúng tôi muốn kiểm tra rằng chúng ta có một ma trận số thực:

In[143]:= rm = RandomInteger[10000,{1500,1500}]; 

Đây là cách mà hầu hết thẳng về phía trước và chậm:

In[144]:= MatrixQ[rm,NumericQ[#]&&Im[#]==0&]//Timing 
Out[144]= {4.125,True} 

Điều này là tốt, vì chúng tôi sử dụng lại mẫu đối sánh tốt hơn:

In[145]:= MatrixQ[rm,NumericQ]&&FreeQ[rm,Complex]//Timing 
Out[145]= {0.204,True} 

Chúng tôi không sử dụng bản chất đóng gói của ma trận. Điều này vẫn tốt hơn:

In[146]:= MatrixQ[rm,NumericQ]&&Total[Abs[Flatten[Im[rm]]]]==0//Timing 
Out[146]= {0.047,True} 

Tuy nhiên, đây không phải là kết thúc. Người sau là gần ngay lập tức:

In[147]:= MatrixQ[rm,NumericQ]&&Re[rm]==rm//Timing 
Out[147]= {0.,True} 
+0

Nếu tôi hiểu bạn, tuy nhiên tôi không thể thực hiện kiểm tra ở chức năng trên cùng, nhưng khi thực hiện cuộc gọi đến các chức năng cấp thấp hơn của tôi, từ chức năng cấp cao nhất đó, đối số trùng khớp sai (do lỗi logic) được sử dụng trong cuộc gọi ở đâu đó xuống dòng? Tôi có thể thấy bản thân mình, trong quá trình phát triển, thay đổi mã, và truyền tham số sai, và không có các kiểm tra tham số trên mỗi hàm tôi sử dụng, tôi có thể bỏ qua lỗi này? Cảm ơn – Nasser

+0

@Nasser Tôi đã giải quyết câu hỏi của bạn trong chỉnh sửa của tôi, vui lòng xem –

13

Bạn có thể sử dụng VectorQ theo cách hoàn toàn tương tự như MatrixQ. Ví dụ,

f[vector_ /; VectorQ[vector, NumericQ]] := ... 

Cũng lưu ý hai sự khác biệt giữa VectorQListQ:

  1. Một đồng bằng VectorQ (không có đối số thứ hai) chỉ cung cấp cho đúng nếu không có yếu tố của danh sách là danh sách mình (tức là chỉ đối với cấu trúc 1D)

  2. VectorQ sẽ xử lý SparseArray s trong khi ListQ sẽ không


Tôi không chắc chắn về những tác động hiệu quả của việc sử dụng những trong thực tế, tôi rất tò mò về điều đó bản thân mình.

Đây là điểm chuẩn ngây thơ. Tôi so sánh hai chức năng: một hàm chỉ kiểm tra các đối số, nhưng không làm gì cả, và có thêm hai vectơ (đây là một hoạt động được xây dựng rất nhanh, tức là bất cứ điều gì nhanh hơn điều này có thể được coi là không đáng kể). Tôi đang sử dụng NumericQ là séc phức tạp hơn (do đó có khả năng chậm hơn) so với NumberQ.

In[2]:= add[a_ /; VectorQ[a, NumericQ], b_ /; VectorQ[b, NumericQ]] := 
    a + b 

In[3]:= nothing[a_ /; VectorQ[a, NumericQ], 
    b_ /; VectorQ[b, NumericQ]] := Null 

Mảng được đóng gói. Có thể xác minh rằng séc là thời gian không đổi (không được hiển thị ở đây).

In[4]:= rr = RandomReal[1, 10000000]; 

In[5]:= Do[add[rr, rr], {10}]; // Timing 

Out[5]= {1.906, Null} 

In[6]:= Do[nothing[rr, rr], {10}]; // Timing 

Out[6]= {0., Null} 

Mảng không đóng gói đồng nhất. Séc là thời gian tuyến tính, nhưng rất nhanh.

In[7]:= rr2 = Developer`[email protected][10000, 1000000]; 

In[8]:= Do[add[rr2, rr2], {10}]; // Timing 

Out[8]= {1.75, Null} 

In[9]:= Do[nothing[rr2, rr2], {10}]; // Timing 

Out[9]= {0.204, Null} 

Mảng không đóng gói không đồng nhất. Kiểm tra mất cùng thời gian như trong ví dụ trước.

In[10]:= rr3 = Join[rr2, {Pi, 1.0}]; 

In[11]:= Do[add[rr3, rr3], {10}]; // Timing 

Out[11]= {5.625, Null} 

In[12]:= Do[nothing[rr3, rr3], {10}]; // Timing 

Out[12]= {0.282, Null} 

Kết luận dựa trên ví dụ rất đơn giản này:

  1. VectorQ được tối ưu hóa cao, ít nhất là khi sử dụng đối số thứ hai chung. Nó nhanh hơn nhiều so với ví dụ: thêm hai vectơ, bản thân nó cũng là một hoạt động được tối ưu hóa tốt.
  2. Đối với mảng được đóng gói VectorQ là thời gian không đổi.

@Leonid's answer cũng rất phù hợp, vui lòng xem.

+1

cảm ơn, không biết về 'VectorQ'. Tôi nên có loại '? * Q' đầu tiên để xem tất cả các chức năng loại' Q'. Tôi thấy rất nhiều người trong số họ bây giờ. – Nasser

3

Kể từ ListQ chỉ cần kiểm tra mà người đứng đầu là List, sau đây là một giải pháp đơn giản:

foo[a:{___?NumberQ}] := Module[{}, a + 1] 
+2

Tôi chỉ nhận ra sự thiếu hụt này khi đọc câu trả lời của bạn "Vì' ListQ' chỉ kiểm tra đầu là 'List'": cả giải pháp này và bất kỳ 'ListQ' sẽ không hoạt động với' SparseArray '. 'VectorQ' sẽ. – Szabolcs

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