2011-11-18 75 views
8

我在想,如果這是一個錯誤或記錄的行爲?編譯中的Do/Return行爲有所不同 - 爲什麼?

f1 = Function[v, 
    Do[If[v[[i]] < 0, Return[v[[i]]]], {i, 1, Length[v]}]] 

c1 = Compile[{{v, _Integer, 1}}, 
    Do[If[v[[i]] < 0, Return[v[[i]]]], {i, 1, Length[v]}]] 

當他們申請不包含負數的列表,我們得到不同的結果:

In[66]:= Through[{f1, c1}[{1, 2, 3}]] 

Out[66]= {Null, 3} 

這當我試圖編譯short function(實際上是修改的版本,它)而造成的錯誤。

單獨Do不顯示的問題:

c2 = Compile[{}, Do[i, {i, 5}]] 

c2[] (* returns nothing, as expected *) 

回答

3

爲在@Pillsy和@Leonid答案指出,這個問題是,原來的功能有時會返回Null,有時一個整數。相比之下,編譯的函數總是返回一個整數。在V8中,我們可以使用CompilePrint看到:

Needs["CompiledFunctionTools`"] 
CompilePrint @ 
    Compile[{{v,_Integer,1}},Do[If[v[[i]]<0,Return[v[[i]]]],{i,1,Length[v]}]] 

其中,在V8.0.4,會產生這樣的結果:

 1 argument 
     1 Boolean register 
     6 Integer registers 
     1 Tensor register 
     Underflow checking off 
     Overflow checking off 
     Integer overflow checking on 
     RuntimeAttributes -> {} 

     T(I1)0 = A1 
     I3 = 0 
     I0 = 1 
     Result = I5 

1 I2 = Length[ T(I1)0] 
2 I4 = I3 
3 goto 10 
4 I5 = Part[ T(I1)0, I4] 
5 B0 = I5 < I3 
6 if[ !B0] goto 10 
7 I5 = Part[ T(I1)0, I4] 
8 goto 11 
9 goto 10 
10 if[ ++ I4 < I2] goto 4 
11 goto 12 
12 Return 

我們可以看到,編譯函數的結果是無論在結束了整數寄存器I5。根據反編譯指令的流程,我們看到如果沒有匹配,則I5將最終包含列表的最後一個元素。

編譯器的行爲可能會在Mathematica版本之間發生變化。我認爲有理由認爲編譯器應該在返回結果類型不明確的情況下至少發出警告。

+0

+1這正是我也看過的,看看返回的是什麼。 –

5

我會說這與Compile工作方式的缺陷,但它並不令人驚奇,它是不工作的權利。 Compile確實對於它的輸入(在這裏,v將是一個整數列表)做出了非常具體的假設,並且它的輸出也是如此。編譯函數都應該返回一個單一的,特定類型的值,並且該類型必須是這是作爲一個編譯函數的輸入可接受類型之一:True|FalseInteger,等c和相同的陣列。這顯然是更好,如果功能與消息抱怨,然後返回Null,但爲了編譯一個乖巧的功能,你需要提供適當的整數返回值作爲defalult。

編輯來澄清輸出類型,按Szabolcs下面的評論。

+0

不返回與輸入相同類型的值,但返回相同類型的值,而不管特定輸入(即不能返回Null和整數)。是的,你說的是合理的。 – Szabolcs

4

我不會說這是一個錯誤。正如@Pillsy所述,Compile -d功能受到更多限制,因爲它必須始終返回相同的類型。由於Do是一個作用域結構,在ReturnDo只跳出了Do,不Function。因此,在某些情況下,它會返回一個向量元素,而在其他情況下,它將返回Null。嚴格地說,就像書面一樣,函數根本不應該編譯。但是,可以更靈活一些,並假定該函數的作者知道得更好,並且將在該特定情況下丟棄答案。有了這個解釋,Compile可以自由產生任何答案。它在這裏做的是產生列表中的最後一個元素。而且我認爲,這不是每次都生成一個固定數字的臨時工。我還認爲,編譯更靈活的符號代碼時,這種情況不可避免。 Compile本來可以在這種情況下更嚴格的規則和要求在所有情況下(同一類型的)一些有意義的回報,但我不清楚這是否真的是有益的。從某種意義上說,所有的C都是這樣 - 編譯器假定你知道你在做什麼,但是如果你不小心的話,允許你創建很多未定義的行爲。

3

您可能會發現有用的一些其他信息。試想一下:

In[26]:= f1 = 
Function[v, Do[If[v[[i]] < 0, Return[v[[i]]]], {i, 1, Length[v]}]; 
    last = 1;]; 

In[27]:= last 

Out[27]= last 

In[28]:= f1[{-1, 2, 3}] 

In[29]:= last 

Out[29]= 1 

Eventhough的功能應該有它得到持續= 1,因此,當其他人注意到的第一個元素上返回,返回被打破。這不會被修復,因爲有太多的代碼依賴於這種行爲。現在

,您可以使用:

In[30]:= f2 = Function[v, Module[{}, 
    Do[If[v[[i]] < 0, Return[v[[i]], Module]], {i, 1, Length[v]}]; 
    last2 = 1;]]; 

In[31]:= f2[{-1, 2, 3}] 

Out[31]= -1 

In[32]:= last2 

Out[32]= last2 

預期其行爲。然而不幸的是,

In[33]:= c1 = Compile[{{v, _Integer, 1}}, 
    Module[{}, 
    Do[If[v[[i]] < 0, Return[v[[i]], Module]], {i, 1, Length[v]}]; 
    ] 
    ]; 

不會編譯。

這是一種使這項工作的方法。

In[137]:= c1=Compile[{{v,_Integer,1}}, 
Module[{res=1}, 
Do[If[v[[i]]<0,res=v[[i]];Break[]],{i,1,Length[v]}]; 
If[res==1,Internal`CompileError[]]; 
res 
] 
,"RuntimeOptions"->{"RuntimeErrorHandler"->Function[Null]}] 

In[140]:= c1[{1,2,3,1}] 

In[141]:= c1[{1,2,3,-1}] 

Out[141]= -1 

檢查輸出。

In[139]:= CompilePrint[c1] 

一些進一步的說明:「RuntimeErrorHandler」 - >函數[Null]這是一個函數!這點考慮一下吧。你可以喔,留言!

所以像這樣的工作。

cfquietfail = 
    Compile[{{x, _Real, 1}}, Exp[x], 
    "RuntimeOptions" -> {"WarningMessages" -> False, 
    "RuntimeErrorHandler" -> 
     Function[Message[MyFunctionName::"I can complain here!"]; 
     Throw[$Failed]]}]; 
Catch[ cfquietfail[{1000.}]] 

我希望這是有用的。

相關問題