在 Mathematica 中生成一个列表,对每个元素进行条件测试 [英] Generate a list in Mathematica with a conditional tested for each element

查看:45
本文介绍了在 Mathematica 中生成一个列表,对每个元素进行条件测试的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我们要生成一个质数列表 p,其中 p + 2 也是质数.

Suppose we want to generate a list of primes p for which p + 2 is also prime.

一个快速的解决方案是生成前 n 个素数的完整列表,并使用 Select 函数返回满足条件的元素.

A quick solution is to generate a complete list of the first n primes and use the Select function to return the elements which meet the condition.

Select[Table[Prime[k], {k, n}], PrimeQ[# + 2] &]

然而,这是低效的,因为它在返回过滤列表之前将一个大列表加载到内存中.带有 Sow/Reap(或 l = {}; AppendTo[l, k])的 For 循环解决了内存问题,但它远非优雅,而且在一个Mathematica 脚本.

However, this is inefficient as it loads a large list into the memory before returning the filtered list. A For loop with Sow/Reap (or l = {}; AppendTo[l, k]) solves the memory issue, but it is far from elegant and is cumbersome to implement a number of times in a Mathematica script.

Reap[
  For[k = 1, k <= n, k++,
   p = Prime[k];
   If[PrimeQ[p + 2], Sow[p]]
  ]
 ][[-1, 1]]

一个理想的解决方案是内置函数,它允许类似于此的选项.

An ideal solution would be a built-in function which allows an option similar to this.

Table[Prime[k], {k, n}, AddIf -> PrimeQ[# + 2] &]

推荐答案

我将更多地将此解释为关于自动化和软件工程的问题,而不是关于手头的具体问题,并且已经发布了大量解决方案.ReapSow 是收集中间结果的好方法(可能是符号设置中最好的).让我们让它通用,以避免代码重复.

I will interpret this more as a question about automation and software engineering rather than about the specific problem at hand, and given a large number of solutions posted already. Reap and Sow are good means (possibly, the best in the symbolic setting) to collect intermediate results. Let us just make it general, to avoid code duplication.

我们需要的是写一个高阶函数.我不会做任何全新的事情,只会简单地打包您的解决方案以使其更普遍适用:

What we need is to write a higher-order function. I will not do anything radically new, but will simply package your solution to make it more generally applicable:

Clear[tableGen];
tableGen[f_, iter : {i_Symbol, __}, addif : Except[_List] : (True &)] :=
 Module[{sowTag},   
  If[# === {}, #, First@#] &@
       Last@Reap[Do[If[addif[#], Sow[#,sowTag]] &[f[i]], iter],sowTag]];

使用 Do 优于 For 的优点是循环变量是动态本地化的(因此,在 Do 范围之外不会对其进行全局修改code>),而且Do的迭代器语法更接近Table(Do也稍微快一点).

The advantages of using Do over For are that the loop variable is localized dynamically (so, no global modifications for it outside the scope of Do), and also the iterator syntax of Do is closer to that of Table (Do is also slightly faster).

现在,这是用法

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}

编辑

这个版本更接近你提到的语法(它需要一个表达式而不是一个函数):

This version is closer to the syntax you mentioned (it takes an expression rather than a function):

ClearAll[tableGenAlt];
SetAttributes[tableGenAlt, HoldAll];
tableGenAlt[expr_, iter_List, addif : Except[_List] : (True &)] :=
 Module[{sowTag}, 
  If[# === {}, #, First@#] &@
    Last@Reap[Do[If[addif[#], Sow[#,sowTag]] &[expr], iter],sowTag]];

它有一个额外的优势,你甚至可以全局定义迭代器符号,因为它们是未经评估和动态本地化的.使用示例:

It has an added advantage that you may even have iterator symbols defined globally, since they are passed unevaluated and dynamically localized. Examples of use:

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 过早求值.

Note that since the syntax is different now, we had to use the Hold-attribute to prevent the passed expression expr from premature evaluation.

编辑 2

根据@Simon 的要求,这里是许多维度的概括:

Per @Simon's request, here is the generalization for many dimensions:

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[# === {}, #, First@#] &@
      Last@Reap[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,然后根据索引拆分生成的平面列表.下面是一个使用示例:

It is considerably less trivial, since I had to Sow the indices together with the added values, and then split the resulting flat list according to the indices. Here is an example of use:

{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,然后删除不满足条件的元素:

I assigned the values to i,j,k iterator variables to illustrate that this function does localize the iterator variables and is insensitive to possible global values for them. To check the result, we may use Table and then delete the elements not satisfying the condition:

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}}}

请注意,我没有进行大量检查,因此当前版本可能包含错误并需要更多测试.

Note that I did not do extensive checks so the current version may contain bugs and needs some more testing.

编辑 3 - 错误修复

请注意重要的错误修复:在所有功能中,我现在使用带有自定义唯一标记的 SowReap.如果没有此更改,当它们评估的表达式也使用 Sow 时,函数将无法正常工作.这是Reap-Sow 的一般情况,类似于异常 (Throw-Catch).

Note the important bug-fix: in all functions, I now use Sow with a custom unique tag, and Reap as well. Without this change, the functions would not work properly when expression they evaluate also uses Sow. This is a general situation with Reap-Sow, and resembles that for exceptions (Throw-Catch).

编辑 4 - SyntaxInformation

因为这是一个非常有用的函数,所以让它的行为更像一个内置函数是很好的.首先我们通过

Since this is such a potentially useful function, it is nice to make it behave more like a built-in function. First we add syntax highlighting and basic argument checking through

SyntaxInformation[tableGenAltMD] = {"ArgumentsPattern" -> {_, {_, _, _., _.}.., _.},
                                    "LocalVariables" -> {"Table", {2, -2}}};

然后,添加一条使用消息就可以让菜单项制作模板"(Shift+Ctrl+k)起作用:

Then, adding a usage message allows the menu item "Make Template" (Shift+Ctrl+k) to work:

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&."

可以在此要点中找到更完整和格式化的使用消息.

A more complete and formatted usage message can be found in this gist.

这篇关于在 Mathematica 中生成一个列表,对每个元素进行条件测试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆