2016-04-11 3 views
0

Что я сделал (с некоторой помощью от друга), создайте функцию, которая принимает индекс, который принимает индекс, а для функции - значение , Int и функцию указанный индекс. Он похож на 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. Тем не менее, если вы хотите, чтобы это сделать, это один из способов.

ответ

1

List.indexedMap - это хороший способ сделать то, что вы описываете.

Однако, поскольку вы указываете на недостаток посещения всех предыдущих элементов в списке, реальность в вашем примере на самом деле немного хуже, если вы действительно беспокоитесь о производительности.

Ваш список фактически пройден полностью как минимум два раза, независимо от того, существует ли индекс или нет. Простой акт запроса длины связанного списка должен пересекать весь список. Проверьте исходный код, length is implemented in terms of a foldl.

Кроме того, List.indexedMap проходит по всему списку хотя бы один раз. Я говорю, хотя бы один раз, так как источник indexedMap также calls the length function в дополнение к использованию map. Если нам повезет, звонок length будет замечен (я не достаточно знаком с внутренними компонентами Элма, чтобы узнать, есть он или нет, поэтому не менее комментарий). Сам map проходит весь список при вызове, в отличие от Haskell, который оценивает вещи лениво, только столько, сколько необходимо.

И если вы используете indexedMap, весь список индексируется независимо от интересующей вас позиции. То есть, даже если вы хотите применить функцию с нулевым индексом, весь список индексируется.

Если вы действительно хотите сократить количество обходов до минимума, вы собираетесь (в это время) выполнить свою собственную функцию, и вам придется делать это, не полагаясь на length или indexedMap.

Вот пример функции 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 

Это не очень красиво, но если вы хотите, чтобы избежать излишне ходить по списку - несколько раз - то вы можете пойти с чем-то вроде этого.

0

Вы ищете 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 указан в ссылке на ваш вопрос, но никто в этой теме явно не упомянул этот вариант.

Смежные вопросы