如何使用条件,功能方式动态构建列表

Nas*_*ser 7 wolfram-mathematica

我仍然不能很好地使用Mathematica中的列表来实现功能.这是一个小问题,我想问一下什么是一个很好的功能解决方法.

我说以下列表由点组成.因此,每个元素是一个点的坐标(x,y).

a = {{1, 2}, {3, 4}, {5, 6}}
Run Code Online (Sandbox Code Playgroud)

我想遍历这个列表,每当我找到一个y坐标为> 3.5的点时,我想生成一个复杂的共轭点.最后,我想返回生成的点列表.因此,在上面的例子中,有2个点可以满足这个条件.因此,最终列表中将有5个点,3个原始列表和2个复共轭点.

我试过这个:

If[#[[2]] > 3.5, {#, {#[[1]], -#[[2]]}}, #] & /@ a
Run Code Online (Sandbox Code Playgroud)

但我明白了

{{1, 2}, {{3, 4}, {3, -4}}, {{5, 6}, {5, -6}}}
Run Code Online (Sandbox Code Playgroud)

你会看到中间的额外{},我必须添加一个复共轭点.我希望结果是这样的:

{{1, 2}, {3, 4}, {3, -4}, {5, 6}, {5, -6}}
Run Code Online (Sandbox Code Playgroud)

我尝试插入Flatten,但没有工作,所以,我发现自己有时会回到我原来的程序方式,并使用像Table和Do这样的东西:

a = {{1, 2}, {3, 4}, {5, 6}}
result = {};
Do[

 If[a[[i, 2]] > 3.5, 
   {
    AppendTo[result, a[[i]]]; AppendTo[result, {a[[i, 1]], -a[[i, 2]]}]
   }, 
   AppendTo[result, a[[i]]]
 ],
 {i, 1, Length[a]}
 ]
Run Code Online (Sandbox Code Playgroud)

这给了我想要的东西,但不是功能性解决方案,我不喜欢它.

什么是解决这种列表操作的最佳功能方法?

更新1

使用上面相同的数据,假设我想在遍历列表时对每个点进行计算,并使用此计算来构建列表.假设我想找到点的Norm(位置向量),并使用它来构建一个列表,其每个元素现在将是{norm,point}.并遵循与上述相同的逻辑.因此,唯一的区别是我在每一步都做了额外的计算.

这就是我使用提供的解决方案所做的事情:

a = {{1, 2}, {3, 4}, {5, 6}}

If[#[[2]] > 3.5, 
   Unevaluated@Sequence[ {Norm[#], #}, {Norm[#], {#[[1]], -#[[2]]}}], 
   {Norm[#], #}
 ] & /@ a
Run Code Online (Sandbox Code Playgroud)

这给了我想要的东西:

{    {Sqrt[5],{1,2}}, {5,{3,4}}, {5,{3,-4}}, {Sqrt[61],{5,6}}, {Sqrt[61],{5,-6}}   }
Run Code Online (Sandbox Code Playgroud)

我对此唯一的问题是,我在3个地方重复了对Norm [#]的调用.如果没有这种重复计算,有没有办法做到这一点?

这就是我目前使用我的旧程序方式再次执行上述操作的方法:

a = {{1, 2}, {3, 4}, {5, 6}}
result = {};
Do[
 o = Norm[a[[i]]];
 If[a[[i, 2]] > 3.5, 
  {
   AppendTo[result, {o, a[[i]]}]; AppendTo[result, {o, {a[[i, 1]], -a[[i, 2]]}}]
  }, 
  AppendTo[result, {o, a[[i]]}]
 ],
 {i, 1, Length[a]}
]
Run Code Online (Sandbox Code Playgroud)

我得到了与功能方式相同的结果,但在上面,由于我使用了一个临时变量,我每个点进行一次计算.

这是播种和收获之类的地方吗?我真的从未理解这两个功能.如果没有,你会如何以功能的方式做到这一点?

谢谢

Sza*_*lcs 11

一种方法是使用Sequence.

只需对您的解决方案进行微小修改:

If[#1[[2]] > 3.5, Unevaluated@Sequence[#1, {#1[[1]], -#1[[2]]}], #1] & /@ a
Run Code Online (Sandbox Code Playgroud)

但是,平原ReplaceAll可能更简单:

a /. {x_, y_} /; y > 3.5 :> Sequence[{x, y}, {x, -y}]
Run Code Online (Sandbox Code Playgroud)

这种用法是确切的原因RuleRuleDelayed具有属性SequenceHold.

回答更新1

我分两步完成:

b = a /. {x_, y_} /; y > 3.5 :> Sequence[{x, y}, {x, -y}]
{Norm[#], #}& /@ b
Run Code Online (Sandbox Code Playgroud)

在实际的计算中,你有可能想要单独使用规范,所以Norm /@ b可能会这样做


Mar*_*ure 10

Flatten选择第二个参数,指定要展平的深度.因此,您还可以执行以下操作.

a = {{1, 2}, {3, 4}, {5, 6}};
Flatten[If[#[[2]] > 3.5, {#, {#[[1]], -#[[2]]}}, {#}] & /@ a, 1]
Run Code Online (Sandbox Code Playgroud)

你的Do循环最严重的问题是使用AppendTo.如果长得很result长,这将非常缓慢.处理由于这样的过程而增长的列表的标准方法是使用ReapSow.在这个例子中,你可以这样做.

new = Reap[
  Do[If[el[[2]] > 3.5, Sow[{el[[1]], -el[[2]]}]],
  {el, a}]][[2, 1]];
Join[a, new]
Run Code Online (Sandbox Code Playgroud)


WRe*_*ach 10

虽然Mathematica可以很好地模拟函数式编程范例,但您可以考虑使用Mathematica的本机范例 - 模式匹配:

a = {{1,2},{3,4},{5,6}}

b = a /. p:{x_, y_ /; y > 3.5} :> Sequence[p, {x, -y}]
Run Code Online (Sandbox Code Playgroud)

然后,您可以进一步转换结果以包含Norms:

c = Cases[b, p_ :> {Norm@p, p}]
Run Code Online (Sandbox Code Playgroud)

毫无疑问,使用Sequence生成非常大的列表并不像预先分配正确大小的数组那样高效,然后使用元素赋值更新它.然而,我通常更喜欢表达清晰度而非这种微优化,除非所测量的优化对我的应用至关重要.

  • @Szabolcs我同意Sow/Reap通常是表现力和效率之间的最佳平衡.在Mathematica中,通常有许多可能的问题解决方案,只有秒表才能从性能角度分辨出来.毫无疑问,必要的技术往往提供最佳的优化前景.OP正在寻求从命令式转向功能性技术的指导.我猜测动机是表达性的,所以我的回答主要关注的是建议超越功能性到模式重写,这是主要的Mathematica范式. (3认同)
  • 在Matlab中,通常使用预分配和赋值,但在Mathematica中,使用`Sow`和`Reap`通常更容易和同样有效,如Mark McClure所示.特别是当你事先不知道你需要的尺寸时(因此需要通过加倍它的尺寸或类似的东西来有效地增长阵列). (2认同)

Sim*_*mon 7

要回答您的编辑,请使用With(或Module),如果您打算多次使用昂贵的东西.

这是我编辑中的问题版本:

a = {{1, 2}, {3, 4}, {5, 6}};
Table[With[{n = Norm[x]}, 
  Unevaluated@Sequence[{n, x}, 
    If[x[[2]] > 3.5, {n, {1, -1} x}, Unevaluated@Sequence[]]]], 
 {x, a}]
Run Code Online (Sandbox Code Playgroud)

可以修改上述结构以用于版本MapReplaceAll版本,但我认为Table在这种情况下更清楚.没有评估的序列有点烦人.你也可以使用一些不确定的功能f,然后替换fSequence在最后.