typedef typename Tail::inUnion dummy;
Однако я не уверен, что реализация inUnion верна. Если я правильно понимаю, этот класс не должен быть создан, поэтому вкладка «fail» никогда не будет автоматически терпеть неудачу. Возможно, было бы лучше указать, находится ли тип в объединении или нет с простым булевым значением.
template
struct Contains; template struct Contains > { enum { result = Contains ::result }; }; template struct Contains > { enum { result = true }; }; template struct Contains { enum { result = false }; }; PS: Посмотрите на Boost :: Variant
PS2: посмотрите на typelists , особенно в книге Андрея Александреску: Modern C ++ Design
Встроенное Scan
в основном делает это, хотя это более ужасно:
Scan[Print[#]&, {1,2,3}]
Это особенно ужасно, когда Вы хотите к destructure элементы:
Scan[Print[#[[1]] * #[[2]]]&, {{1,10}, {2,20}, {3,30}}]
следующая функция избегает уродства путем преобразования pattern
к body
для каждого элемента list
.
SetAttributes[ForEach, HoldAll]; ForEach[pat_, lst_, bod_] := Scan[Replace[#, pat:>bod]&, Evaluate@lst]
, который может использоваться в качестве в примере в вопросе.
пз: принятый ответ побудил меня переключаться на это, которое является тем, что я использовал, с тех пор и это, кажется, работает отлично (за исключением протеста, который я добавил к вопросу):
SetAttributes[ForEach, HoldAll]; (* ForEach[pattern, list, body] *)
ForEach[pat_, lst_, bod_] := ReleaseHold[ (* converts pattern to body for *)
Hold[Cases[Evaluate@lst, pat:>bod];]]; (* each element of list. *)
Встроенная функция Карты делает точно, что Вы хотите. Это может использоваться в подробной форме:
Карта [Печать, {1,2,3}]
или стенография
Печать / {1,2,3}
В Вашем втором случае, Вы использовали бы "Печать [Времена @@#]& / {{1,10}, {2,20}, {3,30}}"
я рекомендовал бы читать справку Mathematica на Карте, MapThread, Подал бы заявку, и Функция. Они могут взять бит привыкания к, но как только Вы, Вы никогда не будете хотеть возвращаться!
Вот небольшое улучшение, основанное на последнем ответе дривов, которое позволяет задавать шаблон без пробела (делая синтаксис похожим на другие функции, такие как Table или Do) и которое использует аргумент уровня Cases
SetAttributes[ForEach,HoldAll];
ForEach[patt_/; FreeQ[patt, Pattern],list_,expr_,level_:1] :=
Module[{pattWithBlanks,pattern},
pattWithBlanks = patt/.(x_Symbol/;!MemberQ[{"System`"},Context[x]] :> pattern[x,Blank[]]);
pattWithBlanks = pattWithBlanks/.pattern->Pattern;
Cases[Unevaluated@list, pattWithBlanks :> expr, {level}];
Null
];
Испытания:
ForEach[{i, j}, {{1, 10}, {2, 20}, {3, 30}}, Print[i*j]]
ForEach[i, {{1, 10}, {2, 20}, {3, 30}}, Print[i], 2]
В более новых версиях Mathematica (6.0+) есть обобщенные версии Do [] и Table [], которые делают почти то, что вы хотите, за счет использования альтернативной формы аргумента итератора. Например,
Do[
Print[i],
{i, {1, 2, 3}}]
точно такой же, как ваш
ForEach[i_, {1, 2, 3,},
Print[i]]
. Альтернативно, если вам действительно нравится конкретный синтаксис ForEach, вы можете создать функцию HoldAll, которая его реализует, например:
Attributes[ForEach] = {HoldAll};
ForEach[var_Symbol, list_, expr_] :=
ReleaseHold[
Hold[
Scan[
Block[{var = #},
expr] &,
list]]];
ForEach[vars : {__Symbol}, list_, expr_] :=
ReleaseHold[
Hold[
Scan[
Block[vars,
vars = #;
expr] &,
list]]];
Здесь в качестве имен переменных используются символы, а не шаблоны, но именно так работают различные встроенные управляющие структуры, такие как Do [] и For [].
Функции HoldAll [] позволяют собрать довольно широкий набор настраиваемых структур управления. ReleaseHold [Hold [...]] обычно самый простой способ собрать кучу кода Mathematica для последующего анализа, а Block [{x = #}, ...] & позволяет привязать переменные в теле выражения к любые ценности, которые вы хотите.
В ответ на вопрос Дрейва, приведенный ниже, вы можете изменить этот подход, чтобы разрешить более произвольную деструктуризацию с использованием DownValues уникального символа.
ForEach[patt_, list_, expr_] :=
ReleaseHold[Hold[
Module[{f},
f[patt] := expr;
Scan[f, list]]]]
На этом этапе, однако, я думаю, что вам может быть лучше построить что-то сверху of Cases.
ForEach[patt_, list_, expr_] :=
With[{bound = list},
ReleaseHold[Hold[
Cases[bound,
patt :> expr];
Null]]]
Мне нравится делать Null явным, когда я подавляю возвращаемое значение функции. РЕДАКТИРОВАТЬ : я исправил ошибку, указанную ниже; Мне всегда нравится использовать With
для интерполяции вычисленных выражений в формы Hold *
.
With
для интерполяции вычисленных выражений в формы Hold *
. m подавление возвращаемого значения функции. РЕДАКТИРОВАТЬ : я исправил ошибку, указанную ниже; Мне всегда нравится использовать With
для интерполяции вычисленных выражений в формы Hold *
. В Mathematica есть функции отображения, поэтому допустим, у вас есть функция Func
, принимающая один аргумент. Затем просто напишите
Func /@ list
Print /@ {1, 2, 3, 4, 5}
Возвращаемое значение - это список функций, применяемых к каждому элементу в списке.
PrimeQ /@ {10, 2, 123, 555}
вернет {False, True, False, False}
Я на много лет опоздал на вечеринку, и это, возможно, больше ответ на «мета-вопрос», но то, с чем многим людям изначально трудно при программировании в Mathematica (или других функциональных языках) подходит к проблеме с функциональной, а не структурной точки зрения. Язык Mathematica имеет структурные конструкции, но по своей сути он функциональный.
Рассмотрим свой первый пример:
ForEach[i_, {1,2,3},
Print[i]
]
Как указали несколько человек, функционально это можно выразить как Scan [Print, {1,2,3}]
или Print / @ {1 , 2,3}
(хотя вам следует отдать предпочтение Сканировать
, а не Map
, когда это возможно, как объяснялось ранее, но иногда это может раздражать, поскольку нет инфиксного оператора для Сканирование
).
В Mathematica обычно есть дюжина способов сделать все, что иногда красиво, а иногда разочаровывает. Имея это в виду, рассмотрим ваш второй пример:
ForEach[{i_, j_}, {{1,10}, {2,20}, {3,30}},
Print[i*j]
]
... который более интересен с функциональной точки зрения.
Одно из возможных функциональных решений - вместо этого использовать замену списком, например:
In[1]:= {{1,10},{2,20},{3,30}}/.{i_,j_}:>i*j
Out[1]= {10,40,90}
...но если бы список был очень большим, это было бы излишне медленно, поскольку мы выполняем так называемое «сопоставление с образцом» (например, ищем экземпляры {a, b} в списке и присваиваем их i
и j
) без необходимости.
Учитывая большой массив из 100 000 пар, array = RandomInteger [{1, 100}, {10 ^ 6, 2}]
, мы можем посмотреть на некоторые моменты времени:
Замена правил довольно быстро:
In[3]:= First[Timing[array /. {i_, j_} :> i*j;]]
Out[3]= 1.13844
... но мы можем сделать немного лучше, если воспользуемся структурой выражения, где каждая пара действительно List [i, j]
, и применим Times
в качестве главы каждой пары, превращая каждый {i, j}
в Times [i, j]
:
In[4]:= (* f@@@list is the infix operator form of Apply[f, list, 1] *)
First[Timing[Times @@@ array;]]
Out[4]= 0.861267
Как используется в реализации ForEach [.. .]
выше, Cases
явно неоптимальны:
In[5]:= First[Timing[Cases[array, {i_, j_} :> i*j];]]
Out[5]= 2.40212
... поскольку Cases
выполняет больше работы, чем просто замена правила, имея необходимость создания вывода совпадающих элементов один -одним. Оказывается, мы можем сделать много лучше, если разложим задачу по-другому и воспользуемся преимуществом того факта, что Times
является Listable
и поддерживает векторизованные операции.
Атрибут Listable
означает, что функция f
будет автоматически обрабатывать любые аргументы списка:
In[16]:= SetAttributes[f,Listable]
In[17]:= f[{1,2,3},{4,5,6}]
Out[17]= {f[1,4],f[2,5],f[3,6]}
Итак, поскольку Times
является Listable
, если бы вместо этого у нас были пары чисел как два отдельных массива:
In[6]:= a1 = RandomInteger[{1, 100}, 10^6];
a2 = RandomInteger[{1, 100}, 10^6];
In[7]:= First[Timing[a1*a2;]]
Out[7]= 0.012661
Вау , намного быстрее! Даже если входные данные не были представлены в виде двух отдельных массивов (или у вас более двух элементов в каждой паре), мы все равно можем сделать что-то оптимальное:
In[8]:= First[Timing[Times@@Transpose[array];]]
Out[8]= 0.020391
Мораль этого эпоса не в том, что ForEach
не является ценной конструкцией в целом или даже в системе Mathematica, но вы можете часто получать те же результаты более эффективно и элегантно, работая в функциональном, а не в структурном мышлении.