Добавление двух последних элементов списка

#wolfram-mathematica

#wolfram-mathematica

Вопрос:

Какой самый чистый способ получения списка в Mathematica

 {r1, r2, r3, ..., rn, a, b}
  

и возврат

 {r1, r2, r3, ..., rn, a   b}
  

или в более общем смысле

 {r1, r2, r3, ..., rn, f[a, b]}
  

для какой-то функции f ?

Комментарии:

1. Саймон, я ожидал, что этот вопрос задаст новичок, а не ты. Что именно вам нужно? Позвольте мне перефразировать это: по какой метрике вы будете оценивать ответы? Скорость выполнения? Самый короткий код? Наиболее легко читаемый для новичков Mathematica?

2. @Mr.Wizard: Просто самый чистый код. Я предложил несколько вариантов, но большинство из них меня не устроили. Поэтому я подумал, что, возможно, у кого-то еще есть идея получше. И они сделали — по какой-то причине я полностью отказался от использования ReplaceAll !

3. @Mr.Wizard: В любом случае — я подумал, что это может быть приятный простой / забавный вопрос после всего того мрачного копания во внутренностях Mma, которое продолжалось!

4. 1, за то, что заставил меня думать, что у меня есть простое решение, MapAt , а затем обнаружил, что оно явно не выполняет то, о чем вы просили …

Ответ №1:

 lst = {a[1], a[2], a[3], a[4], a[5], a[6], a[7], a, b}; 
lst /. {a___, b_, c_} -> {a, f[b, c]}

 ==> {a[1], a[2], a[3], a[4], a[5], a[6], a[7], f[a, b]}
  

или (некрасиво):

 Append[Take[lst, {1, -3}], f @@ lst[[{-2, -1}]]]
  

Комментарии:

1. Спасибо, Сэр! Первое решение, вероятно, хочет использовать RuleDelayed вместо Rule . Также последний элемент можно слегка приукрасить как Append[lst[[1;;-3]], f@@lst[[-2;;-1]]] . (конечно, красота в глазах смотрящего и все такое)

2. кстати: я полностью забыл об этом ReplaceAll при рассмотрении этой проблемы, поэтому все мои решения были похожи на ваше второе (уродливое).

3. Интересно, что ваш «уродливый» метод немного быстрее моего, но я думал (в прошлом) Я протестировал аналогичные операции и обнаружил, что опубликованная мной форма работает быстрее. Я думаю, что нет. Возможно, в то время я мог выполнять манипуляции на месте.

4. Вы получаете «галочку», поскольку /. она визуально самая четкая, а ваше «уродливое» решение (по словам @Mr.W) самое быстрое!

Ответ №2:

Я бы использовал правила, если производительность не является большой проблемой (списки не упакованы и т.д.):

  lst = {a, b, c, d, e}

 In[13]:= Replace[lst, {left__, ntl_, l_} :> {left, f[ntl, l]}, {0}]

 Out[13]= {a, b, c, f[d, e]}
  

Комментарии:

1. Леонид, последняя часть вашего кода: , {0} что-нибудь делает?

2. @Mr.Wizard Да, это так. Это гарантирует, что замена выполняется только в самом выражении, а не в какой-либо из его частей. Я нахожу эту идиому полезной во многих случаях, когда вы хотите сделать свои решения, основанные на правилах, пуленепробиваемыми. Для сравнения, ReplaceAll часто приводит к ошибкам, когда правила сопоставляются на другом уровне выражения, чем планировалось.

3. Но Replace это свойство уже есть, не так ли? Replace[{1, 2, 3}, _Integer -> "x"]

4. @Mr.Wizard По-видимому, вы правы, я только что проверил документы. Я часто использую его со спецификацией другого уровня, например, {1} или {2} , так что, вероятно, у меня вошло в привычку использовать его явно. Здесь, действительно, нет необходимости.

Ответ №3:

Если бы я не переубедил Саймона, я бы ответил первым. Орехи. В любом случае, вот мой ответ с опозданием на вечеринку.

 combineLast =
  Module[{x = #},
    x[[-2]] = #2 @@ x[[-2 ;;]];
    Most[x]
  ] amp;;
  

Сравнение:

 leoCL[lst_, f_] := Replace[lst, {left__, ntl_, l_} :> {left, f[ntl, l]}, {0}]

a = RandomInteger[1*^9, 5000];

Do[combineLast[a, Plus], {5000}] // Timing
Do[leoCL[a, Plus], {5000}] // Timing
  
{0.078, Null}

{1.844, Null}

Комментарии:

1. Вау! Ваш компьютер НАМНОГО быстрее моего! Если вы хотите немного большей скорости, то нарезку списка [[-2;;]] можно заменить более явной [[{-2, -1}]] .

2. @Simon, это не должно быть быстрее; это старая машина, использующая процессор начального уровня. Какую версию Mathematica вы используете? Я не удивлюсь, если [[{-2, -1}]] это быстрее, но моя любовь к краткости победила.

3. Я работаю 8.0.1.0 for Linux x86 (64-bit) на двухъядерном процессоре AMD Turion 64 X2 в недорогом ноутбуке HP.

4. @Simon Мне интересно, почему это происходит медленно на вашем компьютере. Я только что попробовал [[{-2, -1}]] , и это слишком близко к вызову, еще один признак того, что поведение отличается. Кажется, я припоминаю, что кто-то в MathGroup жаловался, что Mma 8 работает медленно. Возможно ли вам попробовать эту операцию на Mma 7? Вы проводили какие-либо сравнения скорости, когда получили Mma 8?

5. @Simon @Mr. Это в два раза быстрее, чем на моем совершенно новом ноутбуке i7 8-( (mma v8)

Ответ №4:

Предположим, что ‘list’ определен:

 Remove[list];
list = {r1, r2, r2, r4, r5, a, b};
  

Измените значение ‘list’ на {r1, r2, r3, r4, r5, a}, заменив [[-1]] суммой двух последних элементов в ‘list’.

 list = ReplacePart[Drop[list, -1], -1 -> Plus @@ list[[-2 ;; -1]]]
  

Кстати, спасибо, что задали этот вопрос. 🙂

Ответ №5:

Вот мой взгляд на это:

 addLastTwo = Function[Append[Drop[#, -2], Total[Take[#, -2]]]];

In[225]:= addLastTwo[{r1, r2, r3, r4, r5}]

Out[225]= {r1, r2, r3, r4   r5}
  

Это немного быстрее, чем решение мистера Уизарда, хотя и менее общее:

 In[226]:= Do[addLastTwo@a, {10000}] // Timing

Out[226]= {0.25, Null}

In[227]:= Do[combineLast[a, Plus], {10000}] // Timing

Out[227]= {0.39, Null}