Многие алгоритмы (например, алгоритм поиска следующей перестановки списка в лексикографическом порядке) включают поиск индекса последнего элемента в списке. Тем не менее, я не смог найти способ сделать это в Mathematica, что не является неудобным. Самый простой подход использует LengthWhile
, но это означает изменение всего списка, что, вероятно, будет неэффективным в тех случаях, когда вы знаете элемент, который вы want находится ближе к концу списка и реверсирует смысл предиката:
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]]]
Все это кажется слишком сложным. Кто-нибудь видит лучший способ?
EDIT, чтобы добавить: Конечно, у моего предпочтительного решения есть ошибка, которая означает, что она разбита на длинные списки из-за $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
не является моей любимой функцией Mathematica.