是否有更有效的方法来更新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
更新嵌套列表或记录中的元素(也可以组合)。榆树有同等效力吗?
答案 0 :(得分:3)
在代码量方面效率更高(这与@ MichaelKohl的答案相似):
List.take n list ++ newN :: List.drop (n+1) list
PS:如果n是< 0或n> (列表长度 - 1)然后新项目将在列表之前或之后添加。
PPS:我似乎记得a :: alist
的效果略好于[a] ++ alist
。
如果您的意思是效果 /操作次数有效:
只要您的列表变得越来越大,使用Array
(或Dict
)代替List作为您的类型会更有效。
但是有一个权衡:
Array
和Dict
非常有效/高效。List
非常高效。这就是为什么在我的代码中,List
是我在view
代码中经常使用的内容。在数据方面(在我的update
函数中),我使用Dict
和Array
更多。
答案 1 :(得分:1)
我并不过分熟悉Elm,但鉴于默认情况下它是不可变的,我假设它使用结构共享作为其底层数据结构,所以你关注的内存可能是没有根据的。
就我个人而言,我认为您上面发布的方法并没有错,但如果您不喜欢它,您可以尝试这样的事情(或List.concat
):
List.take n list ++ newN :: List.drop (n+1)
已更新根据评论修正/更新。
答案 2 :(得分:1)
基本上,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
之间来回转换。
答案 3 :(得分:0)
我绝对不是榆树专家,但是看看Elm的List文档并未发现更新列表中给定索引处元素的任何函数。
我喜欢Michael's answer。它非常优雅。如果您更喜欢不太优雅的递归方法,您可以执行以下操作。 (就像我说的那样,我不是榆树专家,但希望代码的意图是明确的,如果不完全正确。另外,我不做任何错误处理。)
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)
然而,无论使用何种方法,无论是平均情况还是最差情况,运行时和空间复杂度都是O(n)。这是Elm的List
是一个单链表的结果。
关于assoc-in
,如果您查看Clojure源代码,您会发现assoc-in
只是以assoc
的形式递归定义。但是,我认为你在Elm中为任意,动态的深度输入它时遇到了麻烦。