在编译中,Do/Return的行为有所不同 - 为什么?

Sza*_*lcs 8 wolfram-mathematica

我想知道这是一个错误还是记录在案的行为?

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]}]]
Run Code Online (Sandbox Code Playgroud)

将它们应用于不包含负数的列表时,我们会得到不同的结果:

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

Out[66]= {Null, 3}
Run Code Online (Sandbox Code Playgroud)

当我尝试编译一个简短的函数(实际上是它的修改版本)时,这会导致错误.

Do 单独没有显示问题:

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

c2[] (* returns nothing, as expected *)
Run Code Online (Sandbox Code Playgroud)

Pil*_*lsy 5

我说这个方法的错误Compile是有效的,但它不能正常工作并不令人惊讶. Compile确实做了非常具体的假设,不仅是关于它的输入(这里,v它将是一个整数列表),还有它的输出.编译函数都应该返回一个单一的,特定类型的值,并且该类型必须是这是作为一个编译函数的输入可接受类型之一:True|False,Integer..,等c和相同的阵列.如果函数抱怨消息然后返回Null,显然会更好,但为了成为一个表现良好的编译函数,您需要提供一个适当的整数返回值作为defalult.

编辑以澄清输出类型,根据Szabolcs的评论如下.


WRe*_*ach 4

正如 @Pillsy 和 @Leonid 的答案中所指出的,问题在于原始函数有时返回Null,有时返回整数。相反,编译后的函数始终返回一个整数。在 V8 中,我们可以使用以下命令看到这一点CompilePrint

Needs["CompiledFunctionTools`"]
CompilePrint @
  Compile[{{v,_Integer,1}},Do[If[v[[i]]<0,Return[v[[i]]]],{i,1,Length[v]}]]
Run Code Online (Sandbox Code Playgroud)

在 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
Run Code Online (Sandbox Code Playgroud)

我们可以看到编译函数的结果就是整数寄存器中的结果I5。按照反编译指令的流程,我们看到如果没有匹配,那么I5最终将包含列表的最后一个元素。

编译器的行为可能会在 Mathematica 版本之间发生变化。我认为有理由认为,在返回结果类型不明确的情况下,编译器至少应该发出警告。