Ответ 1
Я буду интерпретировать это скорее как вопрос об автоматизации и разработке программного обеспечения, а не о конкретной проблеме и учитывая большое количество уже опубликованных решений. Reap
и Sow
являются хорошими средствами (возможно, лучшими в символической настройке) для сбора промежуточных результатов. Давайте просто сделаем его общим, чтобы избежать дублирования кода.
Нам нужно написать функцию более высокого порядка. Я не буду делать ничего принципиально нового, но просто упакую ваше решение, чтобы сделать его более общеприменимым:
Clear[tableGen];
tableGen[f_, iter : {i_Symbol, __}, addif : Except[_List] : (True &)] :=
Module[{sowTag},
If[# === {}, #, [email protected]#] &@
[email protected][Do[If[addif[#], Sow[#,sowTag]] &[f[i]], iter],sowTag]];
Преимущества использования Do
over For
заключаются в том, что переменная цикла динамически локализована (поэтому глобальные модификации для нее вне области Do
), а также синтаксис итератора Do
ближе до Table
(Do
также немного быстрее).
Теперь, это использование
In[56]:= tableGen[Prime, {i, 10}, PrimeQ[# + 2] &]
Out[56]= {3, 5, 11, 17, 29}
In[57]:= tableGen[Prime, {i, 3, 10}, PrimeQ[# + 1] &]
Out[57]= {}
In[58]:= tableGen[Prime, {i, 10}]
Out[58]= {2, 3, 5, 7, 11, 13, 17, 19, 23, 29}
ИЗМЕНИТЬ
Эта версия ближе к упомянутому синтаксису (вместо выражения используется выражение):
ClearAll[tableGenAlt];
SetAttributes[tableGenAlt, HoldAll];
tableGenAlt[expr_, iter_List, addif : Except[_List] : (True &)] :=
Module[{sowTag},
If[# === {}, #, [email protected]#] &@
[email protected][Do[If[addif[#], Sow[#,sowTag]] &[expr], iter],sowTag]];
У этого есть дополнительное преимущество, что вы можете даже иметь символы итератора, определенные глобально, поскольку они передаются неоценимыми и динамически локализованными. Примеры использования:
In[65]:= tableGenAlt[Prime[i], {i, 10}, PrimeQ[# + 2] &]
Out[65]= {3, 5, 11, 17, 29}
In[68]:= tableGenAlt[Prime[i], {i, 10}]
Out[68]= {2, 3, 5, 7, 11, 13, 17, 19, 23, 29}
Обратите внимание, что поскольку синтаксис сейчас отличается, нам пришлось использовать атрибут Hold
, чтобы предотвратить переданное выражение expr
от преждевременной оценки.
РЕДАКТИРОВАТЬ 2
В запросе на @Simon, вот обобщение для многих измерений:
ClearAll[tableGenAltMD];
SetAttributes[tableGenAltMD, HoldAll];
tableGenAltMD[expr_, iter__List, addif : Except[_List] : (True &)] :=
Module[{indices, indexedRes, sowTag},
SetDelayed @@ Prepend[Thread[Map[Take[#, 1] &, List @@ Hold @@@ Hold[iter]],
Hold], indices];
indexedRes =
If[# === {}, #, [email protected]#] &@
[email protected][Do[If[addif[#], Sow[{#, indices},sowTag]] &[expr], iter],sowTag];
Map[
First,
SplitBy[indexedRes ,
Table[With[{i = i}, Function[Slot[1][[2, i]]]], {i,Length[Hold[iter]] - 1}]],
{-3}]];
Это значительно менее тривиально, так как я должен был Sow
индексов вместе с добавленными значениями, а затем разбивать полученный плоский список в соответствии с индексами. Вот пример использования:
{i, j, k} = {1, 2, 3};
tableGenAltMD[i + j + k, {i, 1, 5}, {j, 1, 3}, {k, 1, 2}, # < 7 &]
{{{3, 4}, {4, 5}, {5, 6}}, {{4, 5}, {5, 6}, {6}}, {{5, 6}, {6}}, {{6}}}
Я присвоил значения переменным -тератору i,j,k
, чтобы проиллюстрировать, что эта функция локализует переменные итератора и нечувствительна к возможным глобальным значениям для них. Чтобы проверить результат, мы можем использовать Table
, а затем удалить элементы, не удовлетворяющие условию:
In[126]:=
DeleteCases[Table[i + j + k, {i, 1, 5}, {j, 1, 3}, {k, 1, 2}],
x_Integer /; x >= 7, Infinity] //. {} :> Sequence[]
Out[126]= {{{3, 4}, {4, 5}, {5, 6}}, {{4, 5}, {5, 6}, {6}}, {{5, 6}, {6}}, {{6}}}
Обратите внимание, что я не выполнял обширных проверок, поэтому текущая версия может содержать ошибки и требует еще нескольких тестов.
РЕДАКТИРОВАТЬ 3 - ОШИБКА
Обратите внимание на важное исправление ошибок: во всех функциях я теперь использую Sow
с уникальным уникальным тегом и Reap
. Без этого изменения функции не будут работать должным образом, когда выражение, которое они оценивают, также использует Sow
. Это общая ситуация с Reap
- Sow
и напоминает это для исключений (Throw
- Catch
).
РЕДАКТИРОВАТЬ 4 - SyntaxInformation
Так как это такая потенциально полезная функция, приятно заставить ее вести себя как встроенная функция. Сначала мы добавляем подсветку синтаксиса и проверку основных аргументов через
SyntaxInformation[tableGenAltMD] = {"ArgumentsPattern" -> {_, {_, _, _., _.}.., _.},
"LocalVariables" -> {"Table", {2, -2}}};
Затем добавление сообщения об использовании позволяет использовать пункт меню "Сделать шаблон" (Shift+Ctrl+k
):
tableGenAltMD::usage = "tableGenAltMD[expr,{i,imax},addif] will generate \
a list of values expr when i runs from 1 to imax, \
only including elements if addif[expr] returns true.
The default of addiff is True&."
Более полное и отформатированное сообщение об использовании можно найти в этом значении.