在Mathematica中生成一个列表,并对每个元素进行条件测试

Vor*_*ico 12 wolfram-mathematica

假设我们想要生成素数p的列表,其中p + 2也是素数.

一个快速的解决方案是生成前n个素数的完整列表,并使用Select函数返回满足条件的元素.

Select[Table[Prime[k], {k, n}], PrimeQ[# + 2] &]
Run Code Online (Sandbox Code Playgroud)

但是,这是低效的,因为它在返回筛选列表之前将大型列表加载到内存中.带有Sow/Reap(或l = {}; AppendTo[l, k])的For循环解决了内存问题,但它远非优雅,并且在Mathematica脚本中实现多次很麻烦.

Reap[
  For[k = 1, k <= n, k++,
   p = Prime[k];
   If[PrimeQ[p + 2], Sow[p]]
  ]
 ][[-1, 1]]
Run Code Online (Sandbox Code Playgroud)

理想的解决方案是内置功能,允许类似的选项.

Table[Prime[k], {k, n}, AddIf -> PrimeQ[# + 2] &]
Run Code Online (Sandbox Code Playgroud)

Leo*_*rin 18

我将此解释为一个关于自动化和软件工程的问题,而不是关于手头的具体问题,并且已经发布了大量的解决方案.Reap并且Sow是收集中间结果的好方法(可能是符号设置中最好的).让我们把它做成一般,避免代码重复.

我们需要的是编写一个更高阶的函数.我不会做任何全新的事情,但只会打包你的解决方案,使其更普遍适用:

Clear[tableGen];
tableGen[f_, iter : {i_Symbol, __}, addif : Except[_List] : (True &)] :=
 Module[{sowTag},   
  If[# === {}, #, First@#] &@
       Last@Reap[Do[If[addif[#], Sow[#,sowTag]] &[f[i]], iter],sowTag]];
Run Code Online (Sandbox Code Playgroud)

使用Doover 的优点For是循环变量是动态定位的(因此,在范围之外没有对它进行全局修改Do),并且迭代器语法Do更接近于Table(Do也稍快).

现在,这是用法

In[56]:= tableGen[Prime, {i, 10}, PrimeQ[# + 2] &]

Out[56]= {3, 5, 11, 17, 29}

In[57]:= tableGen[Prime, {i, 3, 10}, PrimeQ[# + 1] &]

Out[57]= {}

In[58]:= tableGen[Prime, {i, 10}]

Out[58]= {2, 3, 5, 7, 11, 13, 17, 19, 23, 29}
Run Code Online (Sandbox Code Playgroud)

编辑

这个版本更接近你提到的语法(它需要一个表达式而不是一个函数):

ClearAll[tableGenAlt];
SetAttributes[tableGenAlt, HoldAll];
tableGenAlt[expr_, iter_List, addif : Except[_List] : (True &)] :=
 Module[{sowTag}, 
  If[# === {}, #, First@#] &@
    Last@Reap[Do[If[addif[#], Sow[#,sowTag]] &[expr], iter],sowTag]];
Run Code Online (Sandbox Code Playgroud)

它还有一个额外的好处,你甚至可以在全局定义迭代器符号,因为它们是未经评估和动态本地化的.使用示例:

In[65]:= tableGenAlt[Prime[i], {i, 10}, PrimeQ[# + 2] &]

Out[65]= {3, 5, 11, 17, 29}

In[68]:= tableGenAlt[Prime[i], {i, 10}]

Out[68]= {2, 3, 5, 7, 11, 13, 17, 19, 23, 29}
Run Code Online (Sandbox Code Playgroud)

请注意,由于语法现在不同,我们必须使用Hold-attribute来防止传递的表达式expr过早评估.

编辑2

Per @ Simon的请求,这里是许多维度的概括:

ClearAll[tableGenAltMD];
SetAttributes[tableGenAltMD, HoldAll];
tableGenAltMD[expr_, iter__List, addif : Except[_List] : (True &)] :=
Module[{indices, indexedRes, sowTag},
  SetDelayed @@  Prepend[Thread[Map[Take[#, 1] &, List @@ Hold @@@ Hold[iter]], 
      Hold], indices];
  indexedRes = 
    If[# === {}, #, First@#] &@
      Last@Reap[Do[If[addif[#], Sow[{#, indices},sowTag]] &[expr], iter],sowTag];
  Map[
    First, 
    SplitBy[indexedRes , 
      Table[With[{i = i}, Function[Slot[1][[2, i]]]], {i,Length[Hold[iter]] - 1}]], 
    {-3}]];
Run Code Online (Sandbox Code Playgroud)

这是相当不重要的,因为我必须Sow将索引与附加值一起,然后根据索引分割得到的平面列表.这是一个使用示例:

{i, j, k} = {1, 2, 3};
tableGenAltMD[i + j + k, {i, 1, 5}, {j, 1, 3}, {k, 1, 2}, # < 7 &]

{{{3, 4}, {4, 5}, {5, 6}}, {{4, 5}, {5, 6}, {6}}, {{5, 6}, {6}}, {{6}}}
Run Code Online (Sandbox Code Playgroud)

我将值赋给i,j,k迭代器变量,以说明此函数确实对迭代器变量进行了本地化,并且对它们的可能全局值不敏感.要检查结果,我们可以使用Table然后删除不满足条件的元素:

In[126]:= 
DeleteCases[Table[i + j + k, {i, 1, 5}, {j, 1, 3}, {k, 1, 2}], 
    x_Integer /; x >= 7, Infinity] //. {} :> Sequence[]

Out[126]= {{{3, 4}, {4, 5}, {5, 6}}, {{4, 5}, {5, 6}, {6}}, {{5, 6}, {6}}, {{6}}}
Run Code Online (Sandbox Code Playgroud)

请注意,我没有进行大量检查,因此当前版本可能包含错误,需要进行更多测试.

编辑3 - BUG FIX

请注意重要的错误修复:在所有功能中,我现在使用Sow自定义唯一标记,Reap以及.如果没有这个更改,当它们评估的表达式也使用时,函数将无法正常工作Sow.这是一般情况Reap- Sow和类似于异常(Throw- Catch)的情况.

编辑4 - SyntaxInformation

由于这是一个非常有用的功能,因此很好地使它更像内置函数.首先,我们添加语法突出显示和基本参数检查

SyntaxInformation[tableGenAltMD] = {"ArgumentsPattern" -> {_, {_, _, _., _.}.., _.},
                                    "LocalVariables" -> {"Table", {2, -2}}};
Run Code Online (Sandbox Code Playgroud)

然后,添加用法消息允许菜单项"Make Template"(Shift+Ctrl+k)工作:

tableGenAltMD::usage = "tableGenAltMD[expr,{i,imax},addif] will generate \
a list of values expr when i runs from 1 to imax, \
only including elements if addif[expr] returns true.
The default of addiff is True&."
Run Code Online (Sandbox Code Playgroud)

可以在此要点中找到更完整和格式化的用法消息.