loops - Mathematica 中的 ForEach 循环

标签 loops language-features wolfram-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 的第二个参数应保持未计算状态。

如果未计算的形式不是列表,则应计算第二个参数。例如:

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

这应该打印 bc 的值。

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

到目前为止的答案还有另一个问题:它们吃Return[]。也就是说,如果您在函数中使用 ForEach 循环并希望从循环内的函数返回,则不能。在 ForEach 循环内发出 Return 似乎与 Continue[] 一样工作。这(等等)让我陷入了困境。

最佳答案

我迟到了很多年,这也许更多的是对“元问题”的回答,但当使用 Mathematica(或其他函数式语言)进行编程时,许多人最初都很难解决这个问题从功能而非结构的角度来看是一个问题。 Mathematica 语言具有结构性构造,但其核心是功能性的。

考虑你的第一个例子:

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

正如一些人指出的,这可以在功能上表达为 Scan[Print, {1,2,3}]Print/@ {1,2,3} code> (尽管如前所述,在可能的情况下,您应该优先使用 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}],我们可以查看一些时序:

规则替换非常快:

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 通常不是一个有值(value)的构造,甚至在 Mathematica 中也是如此,而是说您在工作时通常可以更高效、更优雅地获得相同的结果以功能性思维方式,而不是结构性思维方式。

关于loops - Mathematica 中的 ForEach 循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/160216/

相关文章:

c# - 读取对象属性时忽略 NullReferenceException

php - PHP 的__autoload() 有多独特?

wolfram-mathematica - 将 Boole 与 MaxValue 和/或 PlotRegion 结合使用

wolfram-mathematica - 数学 : Write matrix-data to XML ; Read matrix-data from XML

java - "How can I request an if-else input after a switch statement?"

python - 在两个不同列表中查找相同索引号以比较值的最有效方法

c# - 扩展方法和源代码的向前兼容性

function - Mathematica中的TunkRank

php - SET a1 WHERE a AND b1 WHERE b AND c1 WHERE c, 等等

python - 使用循环或列表理解创建多个 pandas 数据框