如何修改给定索引处的列表元素

How to modify a List element at a given index

我所做的(在朋友的帮助下)创建了一个函数,该函数采用 ListInt 作为索引,以及一个应用于元素的函数指定的索引。它与 Map 类似,但不是将函数应用于每个元素,而是仅将其应用于一个元素。

所以我的问题是:

代码如下:

import Html exposing (text)

main =
    let
        m = {arr=[1,5,3], msg=""}
    in
        text (toString (getDisplay m 4 (\x -> x + 5)))


type alias Model =
    { arr : List (Int)
    , msg : String
    }


getDisplay : Model -> Int -> (Int -> Int) -> Model
getDisplay model i f = 
    let 
        m = (changeAt model.arr i f)
    in
        case m of
            Ok val -> 
                {model | arr = val, msg = ""}
            Err err -> 
                {model | arr = [], msg = err}


changeAt : List a -> Int -> (a -> a) -> Result String (List a)
changeAt l i func = 
    let
        f j x = if j==i then func x else x
    in
        if i < (List.length l) && i >= 0 then
            Ok(List.indexedMap f l)
        else
            Err "Bad index"

注意:Elm discourages indexing Lists, as they are linked lists under the hood: to retrieve the 1001th element, you have to first visit all 1000 previous elements.尽管如此,如果您想这样做,这是一种方法。

List.indexedMap 是完成您所描述的事情的好方法。

但是,由于您提到了必须访问列表中所有前面元素的缺点,如果您确实非常担心性能,那么您示例中的实际情况实际上更糟。

不管索引是否存在,你的列表实际上至少被完整遍历了两次。要求链表长度的简单行为必须遍历整个链表。查看源代码,length is implemented in terms of a foldl.

此外,List.indexedMap至少遍历整个列表一次。我说,至少一次,因为除了使用 map 之外,indexedMap 的来源也 calls the length function。如果幸运的话,length 调用会被记住(我对 Elm 内部结构还不够熟悉,不知道它是否是,因此 at least 评论)。 map 本身在调用时会遍历整个列表,这与 Haskell 不同,后者会惰性地求值,只在必要时才求值。

并且如果您使用indexedMap,则无论您感兴趣的位置如何,都会对整个列表进行索引。也就是说,即使您想在索引零处应用函数,也会对整个列表进行索引。

如果你真的想将遍历次数减少到最低限度,你将(此时)必须实现你自己的功能并且你必须在不依赖lengthindexedMap.

这里是一个 changeAt 函数的例子,它避免了不必要的遍历,如果它找到了位置,它就会停止遍历列表。

changeAt : List a -> Int -> (a -> a) -> Result String (List a)
changeAt l i func =
  if i < 0 then
    Err "Bad Index"
  else
    case l of
      [] ->
        Err "Not found"
      (x::xs) ->
        if i == 0 then
          Ok <| func x :: xs
        else
          Result.map ((::) x) <| changeAt xs (i - 1) func

它不是很漂亮,但如果你想避免不必要地遍历列表 - 多次 - 那么你可能想要使用这样的东西。

您正在寻找 the set function for Arrays。与其使用您描述的效率低下的列表,不如使用此结构更适合您的用例。

这是您正在寻找的功能的有效实现:

changeAt : Int -> (a -> a) -> Array a -> Array a
changeAt i f array =
case get i array of
    Just item ->
        set i (f item) array
    Nothing ->
        array

另请注意,此实现中的 data structure is the last argument

Array 在你的问题的 link 中被提及,但是这个线程中没有人明确提到这个选项。