我仍然不能很好地使用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]}
]
我得到了与功能方式相同的结果,但在上面,由于我使用了一个临时变量,我每点进行一次计算。
这是播种和收获之类的地方吗?我真的从未理解这两个功能。如果没有,你会如何以功能的方式做到这一点?
感谢
答案 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}]
此类用法是Rule
和RuleDelayed
具有属性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
增长很长,这将非常缓慢。处理由于此类过程而增长的列表的标准方法是使用Reap
和Sow
。在这个例子中,你可以这样做。
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}]
可以修改上述结构以用于Map
或ReplaceAll
版本,但我认为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]]}},{#}])& / @一个)