将函数映射到列表时如何删除额外的{}

Nas*_*ser 7 wolfram-mathematica

简单的问题,给出这样的列表

Clear[a, b, c, d, e, f];
lst = {{a, b}, {c, d}, {e, f}};
Run Code Online (Sandbox Code Playgroud)

并假设我有一个这样定义的函数:

foo[x_,y_]:=Module[{},...]
Run Code Online (Sandbox Code Playgroud)

我想将此功能应用于列表,所以如果我输入

Map[foo, lst]
Run Code Online (Sandbox Code Playgroud)

这给了

{foo[{a, b}], foo[{c, d}], foo[{e, f}]}
Run Code Online (Sandbox Code Playgroud)

我想要它出来

{foo[a, b], foo[c, d], foo[e, f]}
Run Code Online (Sandbox Code Playgroud)

所以它有效.

做这个的最好方式是什么?假设我无法修改函数foo []定义(假设它是内置函数)

我现在只知道两种方式

Map[foo[#[[1]], #[[2]]] &, lst]
{foo[a, b], foo[c, d], foo[e, f]}
Run Code Online (Sandbox Code Playgroud)

(太多的工作),或

MapThread[foo, Transpose[lst]]
{foo[a, b], foo[c, d], foo[e, f]}
Run Code Online (Sandbox Code Playgroud)

(减少输入,但需要先转置)

问题:还有其他更好的方法吗?我看了看其他地图和它的朋友,我看不到比我拥有的更直接的功能.

abc*_*bcd 14

您需要ApplyLevel1或它的缩写形式,@@@

foo@@@lst    
{foo[a, b], foo[c, d], foo[e, f]}
Run Code Online (Sandbox Code Playgroud)


And*_*rei 7

一种可能的方式是改变的每个元素的头lstListfoo:

foo @@ # & /@ lst
{foo[a, b], foo[c, d], foo[e, f]}
Run Code Online (Sandbox Code Playgroud)


Art*_*tes 6

只是报告这两种方法的令人费解的性能测试(@@@,@@ # & /@):

        T = RandomReal[{1,100}, {1000000, 2}];

        H[F_Symbol, T_List] := 

                     First@AbsoluteTiming[F @@@ T;]/First@AbsoluteTiming[F @@ # & /@ T;]

        Table[{ToString[F], H[F, T]},  {F, {Plus, Subtract, Times, Divide, Power, Log}}]

Out[3]= {{"Plus",     4.174757}, 
         {"Subtract", 0.2596154}, 
         {"Times",    3.928230}, 
         {"Divide",   0.2674164}, 
         {"Power",    0.3148629},
         {"Log",      0.2986936}}
Run Code Online (Sandbox Code Playgroud)

这些结果不是随机的,但对于非常不同的数据大小大致成比例.

@@@是更快大约3-4倍Subtract,Divide,Power,Log@@ # & /@更快的4倍Plus,并Times引起其他问题,这些问题(作为一个可以相信)有可能会稍微
通过下面的评测澄清:

 Attributes@{Plus, Subtract, Times, Divide, Power, Log}
Run Code Online (Sandbox Code Playgroud)

只有PlusTimes具有属性FlatOrderless,而只有剩下的中Power(这似乎相对最有效的存在)也有一个属性 OneIdentity.

编辑

对观察到的性能提升的可靠解释(感谢Leonid Shifrin的评论)应该采用不同的方式.

默认情况下MapCompileLength -> 100,我们可以检查评估SystemOptions["CompileOptions"].要重置Map的自动编译,我们可以评估:

SetSystemOptions["CompileOptions" -> "MapCompileLength" -> Infinity]
Run Code Online (Sandbox Code Playgroud)

现在我们可以通过再次评估H相关符号和列表上的性能测试函数来测试这两种方法的相对性能:

          Table[{ToString[F], H[F, T]}, {F, {Plus, Subtract, Times, Divide, Power, Log}}]

 Out[15]= {{"Plus",      0.2898246},
           {"Subtract",  0.2979452}, 
           {"Times",     0.2721893}, 
           {"Divide",    0.3078512}, 
           {"Power",     0.3321622},
           {"Log",       0.3258972}}
Run Code Online (Sandbox Code Playgroud)

有了这些结果,我们可以得出结论,一般来说Yoda的方法(@@@)是最有效的,而Andrei提供的方法更好Plus,Times因为自动编译Map允许更好的(@@ # & /@)性能.

  • 如果我们记得`Map`可以自动编译,并且只需3个头就可以编译`Apply`:'Plus`,`Times`和`List`,这不是那么令人费解.OTOH,`@@@`不自动编译.由于`@@#&/ @'构造中的自动编译,你会看到'Plus`和`Times`的效率提升,并且因为你的输入是一个大的打包数组(允许一个人从自动编译中受益) (3认同)