许多算法(如按字典顺序查找列表的下一个排列的算法)涉及查找列表中最后一个元素的索引。但是,我一直无法在Mathematica中找到一种不笨拙的方法。最简单的方法是使用LengthWhile
,但它意味着扭转了整个列表,这很可能是低效的,你知道你想要的元素是靠近列表的末尾,扭转谓语感病例:从Mathematica中的列表末尾开始搜索
findLastLengthWhile[list_, predicate_] :=
([email protected] - LengthWhile[[email protected], ! [email protected]# &]) /. (0 -> $Failed)
我们可以用Do
做一个明确的命令性循环,但是这样做也有点笨拙。这将有助于如果Return
实际上从一个函数,而不是Do
块返回,但事实并非如此,所以你还不如用Break
:
findLastDo[list_, pred_] :=
Module[{k, result = $Failed},
Do[
If[[email protected][[k]], result = k; Break[]],
{k, [email protected], 1, -1}];
result]
最终,我决定用尾递归,迭代这意味着提前终止会更容易一些。使用怪异,但很有用#0
符号,让匿名函数调用自己,这成为:
findLastRecursive[list_, pred_] :=
With[{
step =
Which[
#1 == 0, $Failed,
[email protected][[#1]], #1,
True, #0[#1 - 1]] &},
step[[email protected]]]
所有这一切都似乎太辛苦,虽然。有没有人看到更好的方法?
编辑补充:当然,我的首选解决方案有一个错误,这意味着它由于$IterationLimit
在长列表中被破坏。
In[107]:= findLastRecursive[Range[10000], # > 10000 &]
$IterationLimit::itlim: Iteration limit of 4096 exceeded.
Out[107]= (* gack omitted *)
您可以Block
解决这个问题:
findLastRecursive[list_, pred_] :=
Block[{$IterationLimit = Infinity},
With[{
step =
Which[
#1 == 0, $Failed,
[email protected][[#1]], #1,
True, #0[#1 - 1]] &},
step[[email protected]]]]
$IterationLimit
是不是我最喜欢的数学特征。
如果只有列表被表示为双链接结构... –
你确定你的第一行代码是正确的吗?除非我不理解它应该做什么,否则第二个'LengthWhile'是'LengthWhile [list // Reverse,predicate @#&]'?那么,添加一个'Reverse'并移除'!'? –
@Sjoerd C. de Vries是的,你是对的。我修复了一下。 – Pillsy