我想要这样的事情:

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循环内发出返回似乎与 Continue [] 类似。这只是(等待它)把我扔了一圈。

有帮助吗?

解决方案 7

感谢 Pillsy Leonid Shifrin ,这就是我现在正在使用的内容:

SetAttributes[each, HoldAll];               (* each[pattern, list, body]      *)
each[pat_, lst_List, bod_] :=               (*  converts pattern to body for  *)
  (Cases[Unevaluated@lst, pat:>bod]; Null); (*   each element of list.        *)
each[p_, l_, b_] := (Cases[l, p:>b]; Null); (* (Break/Continue not supported) *)

其他提示

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代码以便稍后进行评估的最简单方法,并阻止[{x =#},...]&允许表达式主体中的变量绑定到您想要的任何值。

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

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

但是,在这一点上,我认为你最好在Case之上构建一些东西。

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

当我抑制函数的返回值时,我喜欢使Null显式化。 编辑:我修正了下面指出的漏洞;我总是喜欢使用 With 将计算表达式插入到 Hold * 表单中。

我在这里参加聚会的时间已经很晚了,这或许更像是对“元问题”的回答,但是很多人最初在使用Mathematica(或其他函数式语言)编程时遇到的困难是从功能而非结构的角度来解决问题。 Mathematica语言具有结构构造,但它的核心功能。

考虑你的第一个例子:

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

有几个人指出,这可以在功能上表示为 Scan [Print,{1,2,3}] Print / @ {1,2,3} (尽管如前所述,你应该支持 Scan over 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}的实例,并将它们分配给 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 比规则替换更有效,必须逐个构建匹配元素的输出。事实证明,通过以不同方式分解问题,我们可以更好地完成 lot ,并利用 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,但是当你工作时,你通常可以更高效,更优雅地获得相同的结果在功能性思维中,而不是结构性思维。

内置的 Scan 基本上是这样做的,虽然它很丑陋:

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

当你想要破坏元素时,它尤其难看:

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

以下函数通过将 pattern 转换为 list 的每个元素的 body 来避免丑陋。

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.        *)

内置的Map功能完全符合您的要求。它可以以长形式使用:

地图[打印,{1,2,3}]

或简写

打印/ @ {1,2,3}

在你的第二个案例中,你要使用“打印[时间@@#]&amp; / @ {{1,10},{2,20},{3,30}}&quot;

我建议您阅读Map,MapThread,Apply和Function上的Mathematica帮助。他们可能会习惯,但一旦你,你永远不会想回去!

这是基于dreeves的最后一个答案的一个小改进,它允许指定没有Blank的模式(使语法类似于Table或Do等其他函数)并且使用了Cases的level参数

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有map函数,所以假设你有一个函数 Func 取一个参数。然后写下

Func /@ list

Print /@ {1, 2, 3, 4, 5}

返回值是应用于列表中每个元素的函数列表。

PrimeQ /@ {10, 2, 123, 555}

将返回 {False,True,False,False}

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top