ForEach循环在Mathematica中

我想要这样的东西:

each[i_, {1,2,3},
  Print[i]
]

或者更一般地说,在你正在循环的列表中解构任意的东西,如:

each[{i_, j_}, {{1,10}, {2,20}, {3,30}},
  Print[i*j]
]

通常你想使用Map或者其他纯粹的函数式结构,并且在使用副作用的时候避开非函数式的编程风格。 但这里有一个例子,我认为for-each构造是非常有用的:

假设我有一个将符号与表达式相匹配的选项(规则)列表,例如

attrVals = {a -> 7, b -> 8, c -> 9}

现在我想做一个哈希表,我将这些符号明显映射到这些数字。 我认为没有比这更简单的方法

each[a_ -> v_, attrVals, h[a] = v]

额外的测试案例

在这个例子中,我们转换一个变量列表:

a = 1;
b = 2;
c = 3;
each[i_, {a,b,c}, i = f[i]]

在上述之后,{a,b,c}应该评估为{f [1],f [2],f [3]}。 请注意,这意味着“每个”的第二个参数应该是未评估的,如果它是一个列表。

如果未评估的表格不是列表,则应评估第二个参数。 例如:

each[i_, Rest[{a,b,c}], Print[i]]

这应该打印b和c的值。

附录 :为了正确执行,它应该支持Break []和Continue []。 我不知道如何实现。 也许它需要以某种方式在For,While或Do方面实现,因为这些是唯一支持Break []和Continue []的循环结构。

到目前为止的答案还有另一个问题:他们吃Return [] s。 也就是说,如果您在函数中使用ForEach循环,并且想要从循环内的函数返回,则不能。 在ForEach循环中执行返回似乎像继续[]一样工作。 这只是(等待它)扔我一个循环。


较新版本的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 =#},...]&允许将表达式主体中的变量绑定到无论你想要什么值。

为了回应下面的dreeves的问题,你可以修改这个方法,以允许使用唯一符号的DownValues进行更多的任意解构。

ForEach[patt_, list_, expr_] := 
  ReleaseHold[Hold[
     Module[{f}, 
       f[patt] := expr; 
       Scan[f, list]]]]

不过,在这一点上,我认为你可能会更好地在案例之上构建一些东西。

ForEach[patt_, list_, expr_] :=
  With[{bound = list},
    ReleaseHold[Hold[
       Cases[bound,
         patt :> expr]; 
       Null]]]

我喜欢在抑制函数的返回值时使Null显式化。 编辑 :我修复了这个bug,指出下面是dreeves; 我总是喜欢用With将评估表达式插入Hold*形式。


我对这里的派对迟了多年,这也许更多地是对“元问题”的回答,但很多人最初在用Mathematica(或其他函数式语言)进行编程时遇到了困难,功能而不是结构性观点。 Mathematica语言具有结构构造,但它的功能是其核心。

考虑你的第一个例子:

ForEach[i_, {1,2,3},
  Print[i]
]

正如几位人员指出的那样,这可以在功能上表示为Scan[Print, {1,2,3}]Print /@ {1,2,3} (尽管您应该赞成在可能的情况下Scan Map ,如前所述,但由于Scan没有中缀操作符,所以有时可能很烦人)。

在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}的实例并将它们分配给ij ),这会不必要地缓慢。不必要的。

给定大量100,000对array = RandomInteger[{1, 100}, {10^6, 2}]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不仅仅是规则替换而是更多的工作,所以不得不一个一个地构建匹配元素的输出。 事实证明,通过分解问题,我们可以做得更好,并利用TimesListable的事实,并支持向量化操作。

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

所以,因为TimesListable ,所以如果我们把数字对作为两个独立的数组:

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中都不是一个有价值的结构,但是当你以功能性思维而不是结构化思维方式工作时,你可以更高效更优雅地获得相同的结果。


内置的Scan基本上可以做到这一点,尽管它更丑陋:

    Scan[Print[#]&, {1,2,3}]

当你想要解构元素时尤其难看:

    Scan[Print[#[[1]] * #[[2]]]&, {{1,10}, {2,20}, {3,30}}]

下面的函数通过转换避免了丑陋patternbody的每个元素list

SetAttributes[ForEach, HoldAll];
ForEach[pat_, lst_, bod_] :=  Scan[Replace[#, pat:>bod]&, Evaluate@lst]

这可以用在问题的例子中。

PS:接受的答案促使我切换到这种状态,这是我一直使用的方式,它似乎工作得很好(除了我在问题中附加的警告外):

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.        *)
链接地址: http://www.djcxy.com/p/35527.html

上一篇: ForEach loop in Mathematica

下一篇: The best way to construct a function with memory