2012-01-09 36 views
14

我一直在尋找檢查函數參數的方法。我注意到 MatrixQ需要2個參數,第二個是適用於每個元素的測試。什麼是推薦的方法來檢查一個列表是一個函數參數中的數字列表?

但是ListQ只需要一個參數。 (也出於某種原因,?ListQ沒有幫助頁面,就像?MatrixQ一樣)。

因此,例如,檢查參數傳遞給函數是數字矩陣,我寫

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

什麼是做一個列表一樣的好辦法?這下面只檢查輸入是一個List

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

我可以做這樣的事情:

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

使foo[{1, 2, 3}]將工作,但foo[{1, 2, x}]不會(假設x是一個符號)。但在我看來,這是一種複雜的方式。

問:你知道更好的方法來檢查的參數是一個清單,並檢查清單的內容是數字

和相關的(或已知的Mathematica任何其他頭?)問題:將任何主要的運行時性能問題添加到每個參數中?如果是這樣,你是否建議在測試和開發完成後刪除這些檢查,以便最終程序運行速度更快? (例如,有一個包含所有檢查代碼的版本,用於開發/測試,還有一個沒有用於生產的版本)。

+0

我有時也發現自己想知道的這對性能的影響,* + 1 *對於這個問題 – Szabolcs 2012-01-09 09:57:59

+0

我應該做的性能問題的部分作爲一個單獨的問題或只是離開這裏? – Nasser 2012-01-09 10:00:26

+0

我會把它留在這裏。我突出強調了他們,因爲我認爲閱讀和*注意*這種方式更容易。否則許多人將永遠不會閱讀那麼遠。如果你不接受我的回答,這個問題會吸引更多的意見。 – Szabolcs 2012-01-09 10:01:48

回答

11

關於對性能的影響(因爲你的第一個問題已經被回答) - 通過各種手段,做檢查,但是在你的頂層函數中(它直接從用戶的功能接收數據,用戶也可以是另一個獨立的模塊,由你或其他人編寫)。不要把這些檢查放在你所有的中間函數中,因爲這些檢查將會重複,而且確實是不合理的。

EDIT

爲了解決中間函數,通過@Nasser評價引發的錯誤的問題:有一個很簡單的技術,該技術允許一個上切換圖案的檢查和關斷「點擊」。您可以將模式存儲在包中的變量中,這些變量在函數定義之前定義。

下面是一個示例,其中f是頂級函數,而gh是「內部函數」。我們定義兩種模式:主功能和內部的,就像這樣:

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

現在,我們定義功能:

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

注意的是,圖案內取代的定義定義時間,而不是運行時間,所以這與手動編碼這些模式完全相同。一旦你考,你只需要改變一個行:從

innerNLPatt = nlPatt 

innerNLPatt = _ 

並重新加載你的包。

最後一個問題是 - 你如何快速找到錯誤?我回答了here,在部分中,「使用Throw可以拋出異常,而不是返回$Failed。」「元編程和自動化」

編輯完

我包括這個問題在我的書here一個簡短的討論。在這個例子中,運行時間增加了10%,這是IMO可接受的邊界。在這種情況下,檢查更簡單,性能損失更小。通常,對於任何計算密集型的函數,正確編寫的類型檢查僅佔總運行時間的一小部分。

一些技巧,這是很好的瞭解:

  • 模式匹配器可以非常快,語法使用時(無ConditionPatternTest存在的模式)。

例如:

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} 

僅僅因爲在後者的情況下使用了PatternTest,檢查要慢得多,因爲評價者由圖案匹配每個元素調用,而在第一情況下,一切都是純粹的語法,所有都是在模式匹配器內完成的。


  • 同樣是真實爲解壓數值列表(定時差是相似的)。然而,對於包裝的數字列表,MatchQ和其他模式測試功能不解開某些特殊模式,此外,其中一些檢查是瞬間

下面是一個例子:

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} 

相同,顯然,似乎對於功能是真實的像VectorQMatrixQArrayQ與某些謂詞(NumericQ) - 這些測試是非常有效的。


  • 在很大程度上取決於你如何寫你的測試,即你重用何種程度上有效的數學結構。

例如,我們要測試的,我們有一個真正的數字矩陣:

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

這裏是最直接的和緩慢的方式:

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

這是更好的,因爲我們更好地重用了模式匹配器:

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

我們沒有然而利用矩陣的包裝性質。這還是更好的:

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

但是,這不是結束。下面一個是接近瞬間:

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

如果我理解你,不可能做到,但是我只在頂層函數中執行檢查,但是在調用我自己的低層函數時,從頂層函數調用錯誤的匹配參數(由於邏輯錯誤)在下游的某個地方使用?在開發過程中,我可以看到自己改變了代碼並傳遞了錯誤的參數,並且在我使用的每個函數上沒有這些參數檢查,我可以忽略這個錯誤?謝謝 – Nasser 2012-01-09 11:54:29

+0

@Nasser我在編輯中提到了你的問題,請看看 – 2012-01-09 12:17:47

13

您可能會以與MatrixQ完全類似的方式使用VectorQ。例如,

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

還要注意VectorQListQ之間的兩個不同之處:

  1. 平紋VectorQ(沒有第二個參數)只有在沒有列表的元素列表本身賦予真(即僅一維結構)

  2. VectorQ將處理SparseArray s,而ListQ不會


我不知道在實踐中使用這些對性能的影響,我對我自己很好奇。

這是一個天真的基準。我比較了兩個函數:一個只檢查參數,但什麼也不做,另一個增加兩個向量(這是一個非常快速的內置操作,即比這更快的任何操作都可以忽略)。我正在使用NumericQ,這是比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 

打包數組。可以證實檢查是恆定時間的(這裏沒有顯示)。

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} 

均勻非包裝數組。檢查是線性時間,但非常快。

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} 

非均質非包裝數組。該檢查與前面的示例相同。基於這個非常簡單的例子

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} 

結論:

  1. VectorQ是高度優化,使用共同的第二參數時至少。它比例如添加兩個向量,這本身是一個很好的優化操作。
  2. 對於包裝數組VectorQ是恆定時間。

@Leonid's answer是非常相關的,請看看它。

+1

謝謝,不知道'VectorQ'。我應該先輸入'?* Q'來查看所有'Q'類型的函數。我現在看到很多人。 – Nasser 2012-01-09 09:58:12

3

由於ListQ只檢查頭部List,下面是一個簡單的解決方案:

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

我剛剛在閱讀你的答案時意識到了這個缺點「由於'ListQ'只是檢查頭是否是'List'」:這個和任何'ListQ'解決方案都不能用於'SparseArray's。 'VectorQ'會。 – Szabolcs 2012-01-09 10:12:35

相關問題