Mathematica: передать количество аргументов к функции?

Как я добираюсь, количество аргументов передало функции, такой как Plus[2,3,4,5] имеет 4 аргумента, переданные ему. Я думал, что это может включить использование функциональной Длины и получение аргументов в список. Намерение состоит в том, чтобы выполнить итерации операции на основе количества аргументов в пользу функции. Существует, вероятно, простое решение или функция, но я еще не столкнулся с ним. Какие-либо другие пути или предложения приветствуются также?

6
задан dbjohn 15 June 2010 в 21:04
поделиться

6 ответов

Вот один способ:

In[1]:= foo[args___] := Length[{args}]

In[2]:= foo[1,2,3,4]
Out[2]= 4

Когда вы определяете такую ​​функцию, шаблон args ___ (с 3 завершающими символами подчеркивания) будет соответствовать последовательности 0 или более вещей. Вы не можете использовать Length в Sequence и ничего разумного не произойдет, поэтому вам следует обернуть args в List ( {} ) первым.

Однако Велизарий прав. Для множества итерационных операций будет проще и эффективнее использовать встроенные функции высшего порядка, такие как Map и Fold .

ИЗМЕНИТЬ, чтобы добавить: Из-за того, что выражения Mathematica строятся поверх массивов с проверкой границ, Длина составляет O (1) во времени. Это может привести вас к мысли, что foo также имеет O (1) сложность, но вы ошибаетесь. Из-за того, как работает сопоставление с образцом, все элементы, соответствующие args , будут скопированы в новый List , который вы затем передадите в Length , что делает сложность O ( N ).Это не обязательно большая проблема, потому что использование действительно огромных списков аргументов с функцией почти всегда означает использование Apply , которое выполняет копирование O ( N ) в любом случае, но вы должны это знать.

ИЗМЕНИТЬ еще раз, чтобы добавить: Есть другой способ сделать это, используя Length непосредственно в вычисляемом выражении (как и большинство функций, ориентированных на списки Mathematica, Length может быть используется в выражениях с любым заголовком, а не только в списках). Ничего не копируется, потому что никакие последовательности не сопоставляются и не получают новые заголовки, а функция, для которой подсчитываются аргументы, не нуждается в каких-либо специальных атрибутах, таких как HoldAll . Тем не менее, это подлый прием, который использует причуду механизма сопоставления с образцом, вводя побочные эффекты, которым на самом деле не место, поэтому я бы использовал его с особой осторожностью, если вообще:

Module[{n},
 expr : foo[___] /; (n = Length[Unevaluated[expr]]; True) :=
  n]

Переменная n может быть глобальным, но Module создаст (или, по крайней мере, хорошо подделает) лексические замыкания, так что вы можете по крайней мере сохранить свои переменные локальными.

7
ответ дан 9 December 2019 в 22:29
поделиться

Я думаю, вам придется начать вмешиваться в Mathematica последовательность оценки или, возможно, проще, вмешиваясь в свойства его внутренних функций. Одна из ваших проблем заключается в том, что Mathematica вычисляет очень жадно, поэтому к тому времени, когда вы нажмете return после ввода Plus [2,3,4,5] , он сделает свое дело. и вернул 14.

Вы могли бы возиться с $ Pre , чтобы добиться желаемого. Но вам, возможно, придется Снять защиту [Плюс] и заставить его Удерживать его аргументы, пока у вас не будет возможности подсчитать их количество.

Конечно, если вы просто использовали Plus в качестве примера и действительно хотите определить собственную функцию, то ваша задача, вероятно, будет намного проще. Вот функция, которую я написал, которая просто возвращает количество получаемых аргументов:

fun[y___]:=Length[{y}]

Я тестировал это на нескольких простых случаях. Вам будет поучительно попробовать такие вещи, как:

fun[1,{2,3}]

Я склонен согласиться с уже сделанным комментарием, что то, что вы предлагаете сделать, не очень Mathematica -al

2
ответ дан 9 December 2019 в 22:29
поделиться

Вы всегда можете использовать списки:

f[list_]:= (len = Length[list];
            While [....
                   do whatever
                 ];
            Return [ ..];
           );

 myOut= f[{a,b,c}];

Этот способ подходит для использования в системе Mathematica, потому что управление списками очень мощное.

Если вы используете f[a,b,c], то количество аргументов жестко закодировано

Но опять же... попробуйте функциональный способ.

1
ответ дан 9 December 2019 в 22:29
поделиться

Не уверен, какой тип рекурсии вам нужен, но по моему опыту (вдохновленный Хаскелем?) во-первых, шаблон rest в определении вашей функции может быть довольно мощным:

f[onearg_]:=onearg
f[first_,rest__]:=first+2 f[rest]

In[148]= Trace@f[2,3,4]
Out[148]= {f[2,3,4],2+2 f[3,4],{{f[3,4],3+2 f[4],{{f[4],4},2 4,8},3+8,11},2 11,22},2+22,24}

И, конечно же, у вас есть доступ к Длина [{rest}] , если вам это нужно.

РЕДАКТИРОВАТЬ 2: (Старый график был неверным, как указал Пилси) Mathematica фактически копирует «остальную» часть, так что масштабирование становится квадратичным, если стоимость фактической функции незначительна.

f[] := 0;
f[onearg_] := onearg[[1, 1]];
f[first_, rest__] := f[first] + f[rest];
ListLogLogPlot[Part[#, -1, 1],
   Joined -> True, PlotRange -> {{100, All}, Automatic}, 
   AxesLabel -> {"#Arguments", "Runtime"}] &@
 Reap@Nest[
   Function[all, 
    Sow[{Length[all], First@Timing[Table[f @@ all, {10}]]}];
    Join[all, RandomReal[{-1, 1}, {10, 10, 10}]]], {}, 100]

На рисунке ниже показан результат для недорогой внутренней функции f [onearg _]: = onearg [[1,1]] , как указано выше, где масштабирование действительно квадратично по количеству аргументов, и для дорогая внутренняя функция f [onearg _]: = SingularValueList [onearg, 1] , где масштабирование ближе к линейному.

output of above code

0
ответ дан 9 December 2019 в 22:29
поделиться

Некоторые из вышеперечисленных решений требуют, чтобы вы явно вводили аргументы в функцию, которая помещает их в список. В моем собственном исследовании я обнаружил, что есть способ вычислить ответ. Учитывая выражение 3 + 2 * 2 * 2 * 4 + 5 , найдите, сколько аргументов передается функции Times . С небольшой помощью визуализировав это с помощью функции TreeForm , я собрал некоторые из встроенных функций mathematica для оценки ответа.

Шаги:
1 / Получить позицию функции.
2 / Возвращает вложенный список.
3 / Свести список.
4 / Получить длину списка, которая будет уровнем, на котором находятся аргументы функции.
5 / Level возвращает список аргументов, длину которых вы можете получить.

Пример:

In[89]:= Position[Hold[3 + 2*2*2*4 + 5], Times]

Out[89]= (1    2    0)

In[90]:= FullForm[%]

Out[90]= List[List[1,2,0]]

In[91]:= Flatten[%]

Out[91]= {1,2,0}

In[92]:= FullForm[%]

Out[92]= List[1,2,0]

In[93]:= Length[%]

Out[93]= 3

In[94]:= Level[Hold[3 + 2*2*2*4 + 5], {%}]

Out[94]= {2,2,2,4}

In[95]:= Length[%]

Out[95]= 4

Все это можно поместить в функцию. Хотя он может не обрабатывать случай, когда есть два экземпляра одной и той же функции в выражении автоматическим способом. Для этого может потребоваться установка или ввод некоторых условий пользователем.

0
ответ дан 9 December 2019 в 22:29
поделиться

Согласно моим комментариям в другом ответе, идиоматический способ сделать это обычно:

Length[Unevaluated[expr]]

Например:

In[1]:= Length[Unevaluated[Plus[1, 2, 3, 4]]]

Out[1]= 4

Использование Unevaluated предотвращает оценку аргумента, избежать ситуации, когда аргумент Length (который не имеет никаких атрибутов Hold * ) будет оценивать атомарное значение (например, число), которое не имеет длины, и Length возвращает 0 в таких случаях:

In[2]:= Length[Plus[1, 2, 3, 4]]

Out[2]= 0
1
ответ дан 9 December 2019 в 22:29
поделиться
Другие вопросы по тегам:

Похожие вопросы: