Как я добираюсь, количество аргументов передало функции, такой как Plus[2,3,4,5]
имеет 4 аргумента, переданные ему. Я думал, что это может включить использование функциональной Длины и получение аргументов в список. Намерение состоит в том, чтобы выполнить итерации операции на основе количества аргументов в пользу функции. Существует, вероятно, простое решение или функция, но я еще не столкнулся с ним. Какие-либо другие пути или предложения приветствуются также?
Вот один способ:
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
создаст (или, по крайней мере, хорошо подделает) лексические замыкания, так что вы можете по крайней мере сохранить свои переменные локальными.
Я думаю, вам придется начать вмешиваться в Mathematica последовательность оценки или, возможно, проще, вмешиваясь в свойства его внутренних функций. Одна из ваших проблем заключается в том, что Mathematica вычисляет очень жадно, поэтому к тому времени, когда вы нажмете return после ввода Plus [2,3,4,5]
, он сделает свое дело. и вернул 14.
Вы могли бы возиться с $ Pre
, чтобы добиться желаемого. Но вам, возможно, придется Снять защиту [Плюс]
и заставить его Удерживать
его аргументы, пока у вас не будет возможности подсчитать их количество.
Конечно, если вы просто использовали Plus
в качестве примера и действительно хотите определить собственную функцию, то ваша задача, вероятно, будет намного проще. Вот функция, которую я написал, которая просто возвращает количество получаемых аргументов:
fun[y___]:=Length[{y}]
Я тестировал это на нескольких простых случаях. Вам будет поучительно попробовать такие вещи, как:
fun[1,{2,3}]
Я склонен согласиться с уже сделанным комментарием, что то, что вы предлагаете сделать, не очень Mathematica -al
Вы всегда можете использовать списки:
f[list_]:= (len = Length[list];
While [....
do whatever
];
Return [ ..];
);
myOut= f[{a,b,c}];
Этот способ подходит для использования в системе Mathematica, потому что управление списками очень мощное.
Если вы используете f[a,b,c], то количество аргументов жестко закодировано
Но опять же... попробуйте функциональный способ.
Не уверен, какой тип рекурсии вам нужен, но по моему опыту (вдохновленный Хаскелем?) во-первых, шаблон 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]
, где масштабирование ближе к линейному.
Некоторые из вышеперечисленных решений требуют, чтобы вы явно вводили аргументы в функцию, которая помещает их в список. В моем собственном исследовании я обнаружил, что есть способ вычислить ответ. Учитывая выражение 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
Все это можно поместить в функцию. Хотя он может не обрабатывать случай, когда есть два экземпляра одной и той же функции в выражении автоматическим способом. Для этого может потребоваться установка или ввод некоторых условий пользователем.
Согласно моим комментариям в другом ответе, идиоматический способ сделать это обычно:
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