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

时间:2012-01-06 22:54:17

标签: wolfram-mathematica

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

Clear[a, b, c, d, e, f];
lst = {{a, b}, {c, d}, {e, f}};

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

foo[x_,y_]:=Module[{},...]

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

Map[foo, lst]

这给出了

{foo[{a, b}], foo[{c, d}], foo[{e, f}]}

我想把它作为

出来
{foo[a, b], foo[c, d], foo[e, f]}

所以它有效。

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

我现在只知道两种方式

Map[foo[#[[1]], #[[2]]] &, lst]
{foo[a, b], foo[c, d], foo[e, f]}

(工作太多),或

MapThread[foo, Transpose[lst]]
{foo[a, b], foo[c, d], foo[e, f]}

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

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

5 个答案:

答案 0 :(得分:14)

您需要Apply的{​​{1}}或其简短形式Level

@@@

答案 1 :(得分:7)

一种可能的方法是将lst的每个元素的头部从List更改为foo

foo @@ # & /@ lst
{foo[a, b], foo[c, d], foo[e, f]}

答案 2 :(得分: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}}

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

对于@@@SubtractDividePower

Log大约快3-4倍,而@@ # & /@的速度提高4倍PlusTimes引发了另一个问题,(人们可以相信)可能会略有问题 通过以下评估澄清:

 Attributes@{Plus, Subtract, Times, Divide, Power, Log}

只有PlusTimes具有FlatOrderless属性,而其余只有Power(这看起来相对效率最高)也有属性OneIdentity

修改

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

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

SetSystemOptions["CompileOptions" -> "MapCompileLength" -> Infinity]

现在我们可以通过再次评估相关符号和列表上的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}}

有了这些结果,我们可以得出结论,一般来说Yoda的方法(@@@)是最有效的,而Andrei提供的方法在PlusTimes由于自动的情况下更好Map汇编,可以更好地表现(@@ # & /@)。

答案 3 :(得分:4)

还有一些可供选择的可能性:

这是yoda答案的更详细版本。它仅在foo列表的第1级应用lst(将头部List替换为头部foo):

Apply[foo, lst, {1}]

这样做相同,但将Apply映射到列表lst(基本上是Andrei的答案):

Map[Apply[foo, #] &, lst ]

这只是用级别1的foo [x]替换模式List [x__]:

Replace[lst, List[x__] -> foo[x], 1]

答案 4 :(得分:3)

Apply[]上的答案是正确的,并且是正确的做法,但您要做的是用List[]头代替Sequence[]头部,即List[List[3,5],List[6,7]]应成为List[Sequence[3,5],Sequence[6,7]]

如果删除任何参数列表的头部,序列头部自然会保留,因此Delete[Plus[3,5],0]Delete[{3,5},0]以及Delete[List[3,5],0]都会生成Sequence[3,5]

因此foo@Delete[#,0]&/@{{a, b}, {c, d}, {e, f}}会与foo@@@{{a, b}, {c, d}, {e, f}}相同。

或者,foo[#/.List->Sequence]&/@{{a, b}, {c, d}, {e, f}}做同样的事情。