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

发布于 2024-12-25 18:22:59 字数 785 浏览 2 评论 0原文

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

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

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

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

并且我想将此函数应用于列表,所以如果我输入

Map[foo, lst]

This 给出

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

(更少的打字,但需要先转置)

问题:还有其他更好的方法吗?执行上述操作?我查看了其他 Map 及其朋友,但我看不到比我拥有的功能更直接的功能。

Simple question, given a list like this

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

and suppose I have a function defined like this:

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

And I want to apply this function to the list, so If I type

Map[foo, lst]

This gives

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

I want it to come out as

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

so it works.

What is the best way to do this? Assume I can't modify the function foo[] definition (say it is build-in)

Only 2 ways I know now are

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

(too much work), or

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

(less typing, but need to transpose first)

Question: Any other better ways to do the above? I looked at other Map and its friends, and I could not see a function to do it more directly than what I have.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

归属感 2025-01-01 18:22:59

您需要在 Level 1 处进行 Apply 或其缩写形式 @@@

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

You need Apply at Level 1 or its short form, @@@

foo@@@lst    
{foo[a, b], foo[c, d], foo[e, f]}
司马昭之心 2025-01-01 18:22:59

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

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

One possible way is to change head of each element of lst from List to foo:

foo @@ # & /@ lst
{foo[a, b], foo[c, d], foo[e, f]}
放血 2025-01-01 18:22:59

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

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

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

@@@ 对于 SubtractDividePowerLog< 的速度大约快 3-4 倍/code> while @@ # & /@ 的速度比 PlusTimes 快 4 倍,从而引发了另一个问题,(正如人们所相信的那样)可能会稍微
通过以下评估澄清:

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

只有 PlusTimes 具有属性 FlatOrderless,而其余的只有 < code>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 的编译允许(@@ # & /@) 的性能更好。

Just to report puzzling performance tests of the both methods (@@@, @@ # & /@) :

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

These results are not random, but roughly proportional for very different data sizes.

@@@ is roughly 3-4 times faster for Subtract, Divide, Power, Log while @@ # & /@ is 4 times faster for Plus and Times giving rise to another questions, which (as one can believe) could be slightly
clarified by the following evaluation:

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

Only Plus and Times have attributes Flat and Orderless, while among the rest only Power (which seems relatively the most efficient there) has also an attribute OneIdentity.

Edit

A reliable explanation to observed performance boosts (thanks to Leonid Shifrin's remarks) should go along a different route.

By default there is MapCompileLength -> 100 as we can check evaluating SystemOptions["CompileOptions"].
To reset autocompilation of Map we can evaluate :

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

Now we can test relative performance of the both methods by evaluating once more our H - performance testing function on related symbols and list :

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

Having these result we can conclude that in general Yoda's approach (@@@) is the most efficient, while that provided by Andrei is better in case of Plus and Times due to automatic compilation of Map allowing better performance of (@@ # & /@).

笑,眼淚并存 2025-01-01 18:22:59

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

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

Apply[foo, lst, {1}]

其作用相同,但将 Apply 映射到列表 lst (本质上是 Andrei 的答案):

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

这只是在级别 1 将模式 List[x__] 替换为 foo[x] :

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

A few more possibilities to pick from:

This one is a more verbose version of yoda's answer. It applies foo at level 1 of the list lst only (replaces the head List with the head foo):

Apply[foo, lst, {1}]

This does the same, but maps Apply over the list lst (essentially Andrei's answer):

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

And this just replaces the pattern List[x__] with foo[x] at level 1:

Replace[lst, List[x__] -> foo[x], 1]
风月客 2025-01-01 18:22:59

Apply[] 上的答案是正确的,并且是正确的做法,但是您想要做的是将 List[] 头替换为Sequence[]头,即List[List[3,5],List[6,7]]应该变成List[Sequence[3,5],序列[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}} 执行相同的操作。

The answers on Apply[] are spot on, and is the right thing to do, but what you were trying to do, was to replace a List[] head with a Sequence[] head, i.e. List[List[3,5],List[6,7]] should become List[Sequence[3,5],Sequence[6,7]].

Sequence head is what naturally remains if a head of any list of parameters is deleted, so Delete[Plus[3,5],0] and Delete[{3,5},0] and Delete[List[3,5],0] would all produce Sequence[3,5].

So foo@Delete[#,0]&/@{{a, b}, {c, d}, {e, f}} will give you the same as foo@@@{{a, b}, {c, d}, {e, f}}.

Alternatively, foo[#/.List->Sequence]&/@{{a, b}, {c, d}, {e, f}} does the same thing.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文