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

时间:2011-07-24 10:11:43

标签: wolfram-mathematica

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

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

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

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

我试过了:

If[#[[2]] > 3.5, {#, {#[[1]], -#[[2]]}}, #] & /@ a

但我得到了这个

{{1, 2}, {{3, 4}, {3, -4}}, {{5, 6}, {5, -6}}}

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

{{1, 2}, {3, 4}, {3, -4}, {5, 6}, {5, -6}}

我尝试插入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]}
 ]

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

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

更新1

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

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

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

If[#[[2]] > 3.5, 
   Unevaluated@Sequence[ {Norm[#], #}, {Norm[#], {#[[1]], -#[[2]]}}], 
   {Norm[#], #}
 ] & /@ a

这给了我想要的东西:

{    {Sqrt[5],{1,2}}, {5,{3,4}}, {5,{3,-4}}, {Sqrt[61],{5,6}}, {Sqrt[61],{5,-6}}   }

我唯一的问题是,我在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]}
]

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

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

感谢

6 个答案:

答案 0 :(得分:11)

一种方法是使用Sequence

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

If[#1[[2]] > 3.5, Unevaluated@Sequence[#1, {#1[[1]], -#1[[2]]}], #1] & /@ a

但是,普通ReplaceAll可能更简单:

a /. {x_, y_} /; y > 3.5 :> Sequence[{x, y}, {x, -y}]

此类用法是RuleRuleDelayed具有属性SequenceHold的确切原因。

回答更新1

我分两步完成:

b = a /. {x_, y_} /; y > 3.5 :> Sequence[{x, y}, {x, -y}]
{Norm[#], #}& /@ b

在实际计算中,您可能需要单独使用该规范,因此Norm /@ b可能会执行

答案 1 :(得分:10)

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

a = {{1, 2}, {3, 4}, {5, 6}};
Flatten[If[#[[2]] > 3.5, {#, {#[[1]], -#[[2]]}}, {#}] & /@ a, 1]

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

new = Reap[
  Do[If[el[[2]] > 3.5, Sow[{el[[1]], -el[[2]]}]],
  {el, a}]][[2, 1]];
Join[a, new]

答案 2 :(得分:10)

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

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

b = a /. p:{x_, y_ /; y > 3.5} :> Sequence[p, {x, -y}]

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

c = Cases[b, p_ :> {Norm@p, p}]

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

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

可以修改上述结构以用于MapReplaceAll版本,但我认为Table在这种情况下更清晰。没有评估的序列有点烦人。您可以改为使用一些未定义的函数f,然后在最后用f替换Sequence

答案 4 :(得分:1)

Mark的Sow / Reap代码不会按请求的顺序返回元素。这样做:

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

Reap[
  If[Sow[#][[2]] > 3.5, Sow[# {1, -1}]] & /@ a;
][[2, 1]]

答案 5 :(得分:0)

您可以使用加入申请(@@):

  

加入@@((如果[#[[2]]> 3.5,{#,{#[[1]], - #[[2]]}},{#}])& / @一个)