Leo*_*rin 19
这里是一个版本Table是Abort-able,并会不断迄今收集的中间结果.它是此处发布的解决方案的修改版本.
ClearAll[abortableTable];
SetAttributes[abortableTable, HoldAll];
abortableTable[expr_, iter__List] :=
Module[{indices, indexedRes, sowTag},
SetDelayed @@
Prepend[Thread[Map[Take[#, 1] &, List @@ Hold @@@ Hold[iter]],
Hold], indices];
indexedRes =
If[# === {}, #, First@#] &@Last@Reap[
CheckAbort[Do[Sow[{expr, indices}, sowTag], iter], {}], sowTag];
AbortProtect[
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)
它应该能够采用相同的迭代器规范Table.
下面是它的工作原理.第一个语句(SetDelayed @@...)"解析"迭代器,假设它们都是表单{iteratorSymbol_,bounds__},并将迭代器变量列表赋给变量indices.Hold需要构造以防止对迭代器变量进行可能的评估.有很多方法可以做到这一点,我只使用其中一种方法.下面是它的工作原理:
In[44]:=
{i, j, k} = {1, 2, 3};
Prepend[Thread[Map[Take[#, 1] &, List @@ Hold @@@
Hold[{i, 1, 10}, {j, 1, 5}, {k, 1, 3}]], Hold], indices]
Out[45]= Hold[indices, {i, j, k}]
Run Code Online (Sandbox Code Playgroud)
SetDelayed @@ the-above然后使用将自然地产生表格的延迟定义indices:={i,j,k}.我将值分配给索引i,j,k以证明在使用此构造时不会对它们进行不必要的评估.
下一个语句生成一个收集结果列表,其中每个结果都被分组到一个列表中,其中包含用于生成它的索引列表.由于indices变量是由延迟定义定义的,因此对于新的索引组合,它将每次重新评估.这里使用的另一个重要特性是Do循环接受与Table(和动态本地化迭代器变量)相同的迭代器语法,同时是顺序(常量内存)构造.收集中间结果,Reap并Sow使用.因为expr可以是任何代码段,并且特别是也可以使用Sow,所以只需要通过我们的函数而不是它执行的代码的Reap那些值,需要具有唯一名称的自定义标记Sown.由于Module自然地生成具有唯一名称的(临时)符号,我只使用Module没有值的生成变量作为标记.这是一种通常有用的技术.
为了能够在Abort[]交互式或代码中由用户发布的情况下收集结果,我们将Do循环包装在内CheckAbort.在这里执行的代码Abort[]({}此处)在很大程度上是任意的,因为结果的集合无论如何都是通过Sow和完成的Reap,虽然在更复杂的版本中可能会有用,它会将结果保存到用户提供的某个变量中然后重新发布Abort[](当前未实现的功能).
结果,我们将变量放入indexedRes表单的平面列表中
{{expr1, {ind11,ind21,...indn1}},...,{exprk, {ind1k,ind2k,...indnk}}
Run Code Online (Sandbox Code Playgroud)
其中结果与相应的索引组合分组.我们需要这些索引组合来从平面列表重建多维结果列表.这样做的方法是根据i-th索引的值重复拆分列表.该函数SplitBy具有此功能,但我们需要提供用于拆分步骤的函数列表.由于i子列表中-th迭代器索引的索引{expr,{ind1,...,indn}}是2,i,在i第一步执行拆分的函数是#[[2, i]]&,我们需要动态构造这些函数的列表以将其提供给SplitBy.这是一个例子:
In[46]:= Table[With[{i = i}, Function[Slot[1][[2, i]]]], {i, 5}]
Out[46]= {#1[[2, 1]] &, #1[[2, 2]] &, #1[[2, 3]] &, #1[[2, 4]] &, #1[[2, 5]] &}
Run Code Online (Sandbox Code Playgroud)
该With[{i=i},body]构造用于注入i内部纯函数的特定值.替代注入的价值i为 Function确实存在,例如:
In[75]:=
Function[Slot[1][[2, i]]] /. Map[List, Thread[HoldPattern[i] -> Range[5]]]
Out[75]= {#1[[2, 1]] &, #1[[2, 2]] &, #1[[2, 3]] &, #1[[2, 4]] &, #1[[2, 5]] &}
Run Code Online (Sandbox Code Playgroud)
要么
In[80]:= Block[{Part}, Function /@ Thread[Slot[1][[2, Range[5]]]]]
Out[80]= {#1[[2, 1]] &, #1[[2, 2]] &, #1[[2, 3]] &, #1[[2, 4]] &, #1[[ 2, 5]] &}
Run Code Online (Sandbox Code Playgroud)
要么
In[86]:= Replace[Table[{2, i}, {i, 5}], {inds__} :> (#[[inds]] &), 1]
Out[86]= {#1[[2, 1]] &, #1[[2, 2]] &, #1[[2, 3]] &, #1[[2, 4]] &, #1[[ 2, 5]] &}
Run Code Online (Sandbox Code Playgroud)
但可能更加模糊(可能除了最后一个).
生成的嵌套列表具有适当的结构,子列表{expr,{ind1,...,indn}}位于级别-3(从底部开始的第三级).通过使用Map[First,lst,{-3}],我们删除了索引组合,因为嵌套列表已经被重建并且不再需要它们.剩下的是我们的结果 - 结果表达式的嵌套列表,其结构对应于由类似的嵌套列表生成的结构Table.最后一个语句包含在内AbortProtect- 以防万一,以确保在可能的Abort[]触发之前返回结果.
这是一个示例,我在评估命令后不久就按下了Alt+.(Abort[]):
In[133]:= abortableTable[N[(1+1/i)^i],{i,20000}]//Short
Out[133]//Short= {2.,2.25,2.37037,2.44141,<<6496>>,2.71807,2.71807,2.71807}
Run Code Online (Sandbox Code Playgroud)
它几乎和以下一样快Table:
In[132]:= abortableTable[N[(1+1/i)^i,20],{i,10000}]//Short//Timing
Out[132]= {1.515,{2.0000000000000000000,2.2500000000000000000,<<9997>>,2.7181459268252248640}}
In[131]:= Table[N[(1+1/i)^i,20],{i,10000}]//Short//Timing
Out[131]= {1.5,{2.0000000000000000000,2.2500000000000000000,<<9997>>,2.7181459268252248640}}
Run Code Online (Sandbox Code Playgroud)
但它不会自动编译,Table而是:
In[134]:= Table[N[(1+1/i)^i],{i,10000}]//Short//Timing
Out[134]= {0.,{2.,2.25,2.37037,2.44141,<<9993>>,2.71815,2.71815,2.71815}}
Run Code Online (Sandbox Code Playgroud)
可以编写自动编译代码并将其添加到上面的解决方案中,我只是没有这样做,因为要做正确的工作会很多.
编辑
我重写了这个函数,使一些部分更简洁,更容易理解.此外,它在大型列表上比第一个版本快约25%.
ClearAll[abortableTableAlt];
SetAttributes[abortableTableAlt, HoldAll];
abortableTableAlt[expr_, iter : {_Symbol, __} ..] :=
Module[{indices, indexedRes, sowTag, depth = Length[Hold[iter]] - 1},
Hold[iter] /. {sym_Symbol, __} :> sym /. Hold[syms__] :> (indices := {syms});
indexedRes = Replace[#, {x_} :> x] &@ Last@Reap[
CheckAbort[Do[Sow[{expr, indices}, sowTag], iter], Null],sowTag];
AbortProtect[
SplitBy[indexedRes, Array[Function[x, #[[2, x]] &], {depth}]][[##,1]] & @@
Table[All, {depth + 1}]
]];
Run Code Online (Sandbox Code Playgroud)
很不幸的是,不行.如果你想做一些类似的lst=Table[f[i],{i,1,10000}]事情,如果中止你仍然有结果,你可以这样做
Clear[lst2];
lst2 = {};
(Do[lst2 = {lst2, f[i]}, {i, 1, 10000}];
lst2=Flatten[lst2];) // Timing
Run Code Online (Sandbox Code Playgroud)
对于undefined f,在我的机器上占用0.173066s,而lst = Table[f[i], {i, 1, 100000}]大约需要0.06s(即,Table它的速度是不可中断的3倍).
请注意,显而易见的"可中断"解决方案lst = {};
Do[AppendTo[lst, f[i]], {i, 1, 100000}]需要大约40秒,所以不要这样做:使用链接列表并在最后展平,就像在我的第一个示例中一样(但是,如果f[i]返回列表则会中断,然后更加小心需要).