我将把它更多地解释为一个关于自动化和软件工程的问题,而不是关于手头的具体问题,并且已经发布了大量的解决方案。 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]];
使用Do 优于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}
编辑
这个版本更接近你提到的语法(它需要一个表达式而不是一个函数):
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]];
它还有一个额外的优势,即您甚至可以全局定义迭代器符号,因为它们在传递时是未经计算的并且是动态本地化的。使用示例:
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}
请注意,由于现在语法不同,我们必须使用 Hold-attribute 来防止传递的表达式 expr 过早评估。
编辑 2
根据@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}]];
这要简单得多,因为我必须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}}}
我将值分配给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}}}
请注意,我没有进行广泛的检查,因此当前版本可能包含错误并需要更多测试。
编辑 3 - 错误修复
注意重要的错误修复:在所有函数中,我现在使用带有自定义唯一标签的Sow,以及Reap。如果没有此更改,当它们评估的表达式也使用Sow 时,这些函数将无法正常工作。这是Reap-Sow 的一般情况,类似于异常情况 (Throw-Catch)。
编辑 4 - SyntaxInformation
由于这是一个潜在有用的函数,让它更像一个内置函数是很好的。首先我们通过
添加语法高亮和基本参数检查
SyntaxInformation[tableGenAltMD] = {"ArgumentsPattern" -> {_, {_, _, _., _.}.., _.},
"LocalVariables" -> {"Table", {2, -2}}};
然后,添加使用消息允许菜单项“制作模板”(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&."
可以在this gist 中找到更完整和格式化的使用消息。