【问题标题】:modify a different index of list inside loop修改循环内列表的不同索引
【发布时间】:2018-12-20 18:30:05
【问题描述】:

我们可以修改相同的索引

mylist = [1,2,3]    
Enum.map(mylist, fn x-> -x end) // [-1,-2,-3]

我们如何修改不同的索引,例如

 for (i = 0; i < mylist.length; i++) {
     mylist[i+1] = -mylist[i];
 }

【问题讨论】:

  • 旁注:您的伪代码会导致 JavaScript heap out of memory 异常,从而使 JS 进程完全崩溃。这就是为什么我们都讨厌改变对象的主要原因。

标签: arrays list loops elixir


【解决方案1】:

最重要的是要记住您必须创建一个全新的列表,因此您应该考虑如何最好地获得您正在寻找的内容,而不是如何复制您将如何使用另一种语言来解决它。

例如,如果您想将项目与附近的项目进行比较,您可以压缩一个列表及其偏移量:

iex> enum = 1..5
1..5
iex> stream = Stream.drop(enum, 1) # Enum.drop/2 would also work
#Stream<[enum: 1..5, funs: [#Function<48.15162342/1 in Stream.drop/2>]]>
iex> Enum.zip(enum, stream) # Stream.zip/2 works if you're going to iterate
[{1, 2}, {2, 3}, {3, 4}, {4, 5}]

对于您的具体示例,下一个值是前一个取反值,您可以创建一个无限流并获取您需要的内容:

iex> 1 |> Stream.iterate(&-/1) |> Enum.take(3)
[1, -1, 1]

如果您只想修改一个值,则必须创建一个仅更改新值的新列表(注意这不是您想要进行多次更新的方式) :

iex> {previous, [current | next]} = Enum.split(1..5, 3)
{[1, 2, 3], [4, 5]}
iex> Enum.concat([previous, [current * 12], next])
[1, 2, 3, 48, 5]

【讨论】:

  • 我会赞成这一点,因为您花时间解释了为什么将命令式编程映射到函数式编程不是一个好主意。 :thumbup:
  • 我会补充说 Stream 对于小型列表效率低下,它最适合大型列表和集合以及作为减少的更高级别的抽象。另外,您能否更新您的答案以包含导致正确结果的配方?
  • 你说的是哪个正确的结果?我看到的唯一命令式代码是否定循环
【解决方案2】:

到目前为止,这里的两个答案都是使用 Elixir 核心库帮助程序,例如 Enum.with_index/2Stream.iterate/2Enum.map/2

我将展示修改邻居索引的准系统功能方法,如您的伪代码示例所示。我将复制不会使 VM 崩溃的 sn-p。

> mylist = [ 1, 2, 3 ]
> for (i = 1; i < mylist.length; i++) { mylist[i-1] = -mylist[i] }
> mylist
//⇒ [ -2, -3, 3 ]

在 Elixir 中,使用递归,这将是:

defmodule Neighbour do
  def map(list, acc \\ [])
  def map([_, next | rest], acc), do: map(rest, [-next | acc])
  def map([next], acc), do: Enum.reverse([next, -next | acc])
end

Neighbour.map([1, 2, 3])
#⇒ [-2, -3, 3]

【讨论】:

  • 赞成,因为在这种情况下,自定义函数与高阶函数确实是要走的路。
【解决方案3】:

记住数据在 Erlang 中是不可变的,除非将命令式编程映射到函数式编程不是一个好主意,否则我们可以执行以下操作:

new_list = Enum.map(Enum.with_index(list), fn {v,i} -> 
  ((Enum.at(list, i + 1) ||  0) - v) 
end)

这将导致[1, 1, -3] 或换句话说:

  2 - 1 = 1 
  3 - 2 = 1
  0 - 3 = -3

正如其他人指出的那样,根据 Enum.at/2 的 O(n) 复杂度,这将是一种低效的方法。

一个更高效和惯用的解决方案是:

iex(1)> list0 = [1,2,3]
[1, 2, 3]
iex(2)> sliced = Enum.slice(list, 1, 2)  
[2,3]
iex(3) list2 = List.duplicate(0, length(list0) - length(sliced))
[2, 3, 0]
iex(21)> Enum.map(Enum.zip(list, list2), fn({v1, v2}) -> v2 - v1 end)
[1, 1, -3]

【讨论】:

  • 请记住 Enum.at/2 是 O(n) 复杂度。所以这将是非常低效的。
  • 确实如此。我会用不那么麻烦的东西来更新答案。
猜你喜欢
  • 1970-01-01
  • 2021-12-01
  • 2023-04-05
  • 1970-01-01
  • 2011-02-19
  • 1970-01-01
  • 2017-08-03
  • 1970-01-01
  • 2020-12-11
相关资源
最近更新 更多