更有效的方法来更新Elm中列表中的元素?

时间:2016-11-22 02:24:13

标签: list elm

是否有更有效的方法来更新Elm中列表中的元素而不是map每个元素?

{ model | items = List.indexedMap (\i x -> if i == 2 then "z" else x) model.items }

也许Elm的编译器足够复杂以优化这一点,因此mapindexedMap不会不必要地复制除了1.嵌套列表之外的每个元素?

Clojure has assoc-in更新嵌套列表或记录中的元素(也可以组合)。榆树有同等效力吗?

4 个答案:

答案 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作为您的类型会更有效。

但是有一个权衡:

    当您经常检索/更新/添加项目时,
  • ArrayDict非常有效/高效。
  • 当您经常进行排序和过滤以及实际需要映射整个集合的其他操作时,
  • List非常高效。

这就是为什么在我的代码中,List是我在view代码中经常使用的内容。在数据方面(在我的update函数中),我使用DictArray更多。

答案 1 :(得分:1)

我并不过分熟悉Elm,但鉴于默认情况下它是不可变的,我假设它使用结构共享作为其底层数据结构,所以你关注的内存可能是没有根据的。

就我个人而言,我认为您上面发布的方法并没有错,但如果您不喜欢它,您可以尝试这样的事情(或List.concat):

List.take n list ++ newN :: List.drop (n+1) 

已更新根据评论修正/更新。

答案 2 :(得分:1)

基本上,Elm列表不适用于这种用例。相反,请考虑使用ArrayArray包含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,请注意您可以在ArrayList之间来回转换。

答案 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中为任意,动态的深度输入它时遇到了麻烦。