在 Elm 中更新列表中元素的更有效方法?
More efficient way to update an element in a list in Elm?
在 Elm 中更新列表中的元素是否有比 map
遍历每个元素更有效的方法?
{ model | items = List.indexedMap (\i x -> if i == 2 then "z" else x) model.items }
也许 Elm 的编译器足够复杂,可以对此进行优化,因此 map
或 indexedMap
不会不必要地复制除 1 之外的每个元素。嵌套列表呢?
Clojure has assoc-in
更新嵌套列表或记录中的元素(也可以组合)。 Elm 有等效项吗?
我对 Elm 不太熟悉,但考虑到它默认情况下是不可变的,我假设它对其底层数据结构使用结构共享,因此您对内存的担忧可能没有根据。
我个人认为你上面的方法没有什么问题,但如果你不喜欢它,你可以尝试这样的方法(或List.concat
):
List.take n list ++ newN :: List.drop (n+1)
就代码量而言效率更高(这类似于@MichaelKohl 的回答):
List.take n list ++ newN :: List.drop (n+1) list
PS: 如果 n 是 < 0 或 n > (列表的长度 - 1) 则新项目将被添加到列表之前或末尾。
PPS:我好像记得 a :: alist
比 [a] ++ alist
稍微好一点。
如果您指的是性能/操作次数方面的效率:
一旦您的列表变大,使用 Array
(或 Dict
)而不是列表作为您的类型会更有效。
但有一个权衡:
当您频繁检索/更新/添加项目时,Array
和 Dict
非常高效/高效。
List
当您进行频繁的排序和过滤以及您实际上需要映射整个集合的其他操作时,性能非常好。
这就是为什么在我的代码中,List
是我在 view
代码中经常使用的原因。在数据方面(在我的 update
函数中)我使用 Dict
和 Array
更多。
我绝对不是 Elm 专家,但查看 Elm 的 List 文档并没有揭示任何更新列表中给定索引处元素的函数。
我喜欢。它非常优雅。如果您更喜欢不太优雅的递归方法,则可以执行以下操作。 (就像我说的,我不是 Elm 专家,但希望代码的意图是清楚的,如果它不是很正确。另外,我不做任何错误处理。)
updateListAt :: List a -> Int -> a -> List a
updateListAt (head :: tail) 0 x = x :: tail
updateListAt (head :: tail) i x = head :: (updateListAt tail (i - 1) x)
但是,无论使用何种方法,运行时间和 space 复杂度在平均情况和最坏情况下都是 O(n)。这是 Elm 的 List
是单链表的结果。
关于 assoc-in
,如果您查看 Clojure 源代码,您会发现 assoc-in
只是根据 assoc
递归定义的。但是,我认为您在 Elm 中输入任意动态深度时会遇到问题。
基本上,Elm 列表不适用于此类用例。相反,请考虑使用 Array。 Array
包含一个 set
函数,您可以将其用于概念上 同步更新。这是一个例子:
import Html exposing (text)
import Array
type alias Model = { items : Array.Array String }
model =
{ items = Array.fromList ["a", "b", "c"]
}
main =
let
m = { model | items = Array.set 2 "z" model.items }
z = Array.get 2 m.items
output = case z of
Just n -> n
Nothing -> "Nothing"
in
text output -- The output will be "z"
如果出于某种原因您需要 model.items
成为 List
,请注意您可以在 Array
和 List
之间来回转换。
在 Elm 中更新列表中的元素是否有比 map
遍历每个元素更有效的方法?
{ model | items = List.indexedMap (\i x -> if i == 2 then "z" else x) model.items }
也许 Elm 的编译器足够复杂,可以对此进行优化,因此 map
或 indexedMap
不会不必要地复制除 1 之外的每个元素。嵌套列表呢?
Clojure has assoc-in
更新嵌套列表或记录中的元素(也可以组合)。 Elm 有等效项吗?
我对 Elm 不太熟悉,但考虑到它默认情况下是不可变的,我假设它对其底层数据结构使用结构共享,因此您对内存的担忧可能没有根据。
我个人认为你上面的方法没有什么问题,但如果你不喜欢它,你可以尝试这样的方法(或List.concat
):
List.take n list ++ newN :: List.drop (n+1)
就代码量而言效率更高(这类似于@MichaelKohl 的回答):
List.take n list ++ newN :: List.drop (n+1) list
PS: 如果 n 是 < 0 或 n > (列表的长度 - 1) 则新项目将被添加到列表之前或末尾。
PPS:我好像记得 a :: alist
比 [a] ++ alist
稍微好一点。
如果您指的是性能/操作次数方面的效率:
一旦您的列表变大,使用 Array
(或 Dict
)而不是列表作为您的类型会更有效。
但有一个权衡:
-
当您频繁检索/更新/添加项目时,
Array
和Dict
非常高效/高效。List
当您进行频繁的排序和过滤以及您实际上需要映射整个集合的其他操作时,性能非常好。
这就是为什么在我的代码中,List
是我在 view
代码中经常使用的原因。在数据方面(在我的 update
函数中)我使用 Dict
和 Array
更多。
我绝对不是 Elm 专家,但查看 Elm 的 List 文档并没有揭示任何更新列表中给定索引处元素的函数。
我喜欢
updateListAt :: List a -> Int -> a -> List a
updateListAt (head :: tail) 0 x = x :: tail
updateListAt (head :: tail) i x = head :: (updateListAt tail (i - 1) x)
但是,无论使用何种方法,运行时间和 space 复杂度在平均情况和最坏情况下都是 O(n)。这是 Elm 的 List
是单链表的结果。
关于 assoc-in
,如果您查看 Clojure 源代码,您会发现 assoc-in
只是根据 assoc
递归定义的。但是,我认为您在 Elm 中输入任意动态深度时会遇到问题。
基本上,Elm 列表不适用于此类用例。相反,请考虑使用 Array。 Array
包含一个 set
函数,您可以将其用于概念上 同步更新。这是一个例子:
import Html exposing (text)
import Array
type alias Model = { items : Array.Array String }
model =
{ items = Array.fromList ["a", "b", "c"]
}
main =
let
m = { model | items = Array.set 2 "z" model.items }
z = Array.get 2 m.items
output = case z of
Just n -> n
Nothing -> "Nothing"
in
text output -- The output will be "z"
如果出于某种原因您需要 model.items
成为 List
,请注意您可以在 Array
和 List
之间来回转换。