Wolfram mathematica 检查列表是否为函数参数中的数字列表的推荐方法是什么?

Wolfram mathematica 检查列表是否为函数参数中的数字列表的推荐方法是什么?,wolfram-mathematica,Wolfram Mathematica,我一直在研究检查函数参数的方法。我注意到 MatrixQ接受两个参数,第二个参数是应用于每个元素的测试 但是ListQ只接受一个参数。(同样由于某些原因,?ListQ没有帮助页面,就像?MatrixQ一样) 例如,为了检查函数的参数是否是数字矩阵,我写 ClearAll[foo] foo[a_?(MatrixQ[#, NumberQ] &)] := Module[{}, a + 1] 对于一个列表,有什么好方法可以做同样的事情?下面仅检查输入是否为列表 ClearAll[foo] fo

我一直在研究检查函数参数的方法。我注意到
MatrixQ
接受两个参数,第二个参数是应用于每个元素的测试

但是
ListQ
只接受一个参数。(同样由于某些原因,
?ListQ
没有帮助页面,就像
?MatrixQ
一样)

例如,为了检查函数的参数是否是数字矩阵,我写

ClearAll[foo]
foo[a_?(MatrixQ[#, NumberQ] &)] := Module[{}, a + 1]
对于一个列表,有什么好方法可以做同样的事情?下面仅检查输入是否为列表

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知道的任何其他头的数字)吗


和一个相关问题:在每个参数中添加这样的检查是否会导致运行时性能问题?如果是这样,您是否建议在测试和开发完成后删除这些检查,以便最终程序运行得更快?(例如,为开发/测试准备一个包含所有检入的代码版本,为生产准备一个不包含检入的版本)。

您可以使用
VectorQ
的方式完全类似于
MatrixQ
。比如说,

f[vector_ /; VectorQ[vector, NumericQ]] := ...
还要注意
VectorQ
ListQ
之间的两个区别:

  • 普通的
    VectorQ
    (没有第二个参数)仅在列表中没有元素是列表本身时(即仅用于1D结构)才给出true

  • 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[7]:= rr2 = Developer`FromPackedArray@RandomInteger[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}
    
    基于这个非常简单的例子得出的结论:

  • VectorQ
    是高度优化的,至少在使用公共第二个参数时是如此。这比添加两个向量要快得多,这本身就是一个很好的优化操作
  • 对于压缩数组
    VectorQ
    是常数时间

  • 也非常相关,请查看。

    由于
    ListQ
    只是检查头部是否为
    List
    ,以下是一个简单的解决方案:

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

    关于性能影响(因为您的第一个问题已经得到了回答)-通过各种方式进行检查,但是在您的顶级功能中(直接从功能的用户接收数据。用户也可以是您或其他人编写的另一个独立模块)。不要把这些检查放在所有的中间函数中,因为这样的检查是重复的,而且是不合理的

    编辑

    为了解决@Nasser在评论中提出的中间函数中的错误问题:有一种非常简单的技术,允许用户在“一次单击”中打开和关闭模式检查。您可以将模式存储在包中的变量中,这些变量是在函数定义之前定义的

    下面是一个示例,其中
    f
    是顶级函数,而
    g
    h
    是“内部函数”。我们定义了两种模式:主功能模式和内部模式,如下所示:

    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;
    
    请注意,这些模式在定义时在定义内部被替换,而不是在运行时,因此这完全等同于手工编码这些模式。一旦您进行了测试,您只需更改一行:from

    innerNLPatt = nlPatt 
    

    然后重新装入你的包裹

    最后一个问题是-如何快速发现错误?我回答说,在“不返回
    $Failed
    ,可以使用throw抛出异常”和“元编程和自动化”部分

    结束编辑

    我在书中简要讨论了这个问题。在那个例子中,性能的影响是运行时间增加了10%,IMO几乎可以接受。在本例中,检查更简单,性能损失也更小。通常,对于任何计算密集型函数,正确编写的类型检查只需花费总运行时间的一小部分

    以下是一些值得了解的技巧:

    • 模式匹配器在语法上使用时可以非常快(模式中不存在
      条件
      模式测试
    例如:

    randomString[]:=FromCharacterCode@RandomInteger[{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}
    
    只是因为在后一种情况下使用了
    patternetest
    ,检查速度要慢得多,因为模式匹配器会为每个元素调用求值器,而在第一种情况下,一切都是纯语法的,都是在模式匹配器内完成的


    • 对于未打包的数字列表也是如此(时间差类似)。然而,对于压缩的数字列表,
      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}
    
    显然,对于像
    VectorQ
    MatrixQ
    和<
    randomString[]:=FromCharacterCode@RandomInteger[{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}
    
    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}
    
    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}