Mathematica: получить количество аргументов, переданных функции? - PullRequest
6 голосов
/ 15 июня 2010

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

Ответы [ 6 ]

7 голосов
/ 15 июня 2010

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

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

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

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

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

ИЗМЕНИТЬ, чтобы добавить: Из-заспособ, которым выражения Mathematica строятся поверх проверенных границ массивов, Length - это 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 создаст (или, по крайней мере, хорошо подделает) лексические замыкания, так что вы можете, по крайней мере, сохранить свои переменные локальными.

2 голосов
/ 15 июня 2010

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

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

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

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

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

fun[1,{2,3}]

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

1 голос
/ 16 июня 2010

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

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 голос
/ 15 июня 2010

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

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

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

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

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

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

0 голосов
/ 19 июня 2010

Некоторые из приведенных выше решений требуют явного ввода аргументов в функцию, которая помещает их в список. В моем собственном исследовании я нашел способ рассчитать ответ. Учитывая выражение 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 голосов
/ 16 июня 2010

Не уверен, какая рекурсия вам нужна, но по моему опыту шаблон (вдохновленный Хаскелем?) first,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}

И, конечно, у вас есть доступ к Length[{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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...