Обновление значения в Data.ByteString

Язык C предоставляет очень удобный способ обновления nth элемента массива: array[n] = new_value. Насколько я понимаю, тип Data.ByteString таков, что он обеспечивает функциональность, очень похожую на массив C из uint8_t — доступ через index :: ByteString -> Int -> Word8. Оказывается, обратная операция — обновление значения — не так проста.

Мой первоначальный подход заключался в использовании функций take, drop и singleton, concatetaned следующим образом:

updateValue :: ByteString -> Int -> Word8 -> ByteString
updateValue bs n value = concat [take (n-1) bs, singleton value, drop (n+1) bs] 

(это очень наивная реализация, поскольку она не обрабатывает крайние случаи)

Учитывая фон C, он кажется немного слишком тяжелым, чтобы вызывать 4 функции для обновления одного значения. Теоретически сложность операции не так уж и плоха:

  • take is O(1)
  • drop is O(1)
  • singleton is O(1)
  • concat равно O(n), но здесь я не уверен, является ли n полной длиной составного списка или в нашем случае это просто 3.

Мой второй подход заключался в том, чтобы попросить у Hoogle функцию с похожей сигнатурой типа: ByteString -> Int -> a -> ByteString, но ничего подходящего не появилось.

Я упустил что-то очень очевидное или действительно сложно обновить значение?

Я хотел бы отметить, что я понимаю тот факт, что ByteString является неизменяемым и что изменение любого из его элементов приведет к созданию нового экземпляра ByteString.

РЕДАКТИРОВАТЬ: возможное решение, которое я нашел, читая о библиотеке Control.Lens, использует линзу set. Ниже приведен отрывок из GHCi с опущенными именами модулей:

> import Data.ByteString
> import Control.Lens
> let clock = pack [116, 105, 99, 107]
> clock
"tick"
> let clock2 = clock & ix 1 .~ 111
> clock2
"tock"

person Daniel Lovasko    schedule 18.11.2015    source источник
comment
Зависит от того, какая у вас конечная цель. Если вы действительно хотите прочитать файл, изменить один байт и снова записать его, этот подход кажется разумным. Если то, что вы на самом деле пытаетесь сделать, это изменить каждый байт, но по одному байту за раз, вы, вероятно, захотите map. Можете ли вы объяснить немного больше о том, что вы в конечном итоге после?   -  person MathematicalOrchid    schedule 19.11.2015
comment
@MathematicalOrchid Я понимаю твою точку зрения. Я пытаюсь создать очень виртуальную машину и использую ByteString в качестве резервного хранилища памяти. Речь идет о десятках байт. Приходит инструкция, и ее выполнение изменяет определенный байт памяти, например. mov 4, mem7 (и здесь в дело вступает наша функция updateValue). Поэтому mapping здесь не вариант. Что может быть важным дополнением, так это то, что в этом случае производительность является серьезной проблемой.   -  person Daniel Lovasko    schedule 19.11.2015


Ответы (1)


Одним из решений является преобразование ByteString в Storable Vector, а затем изменение этого:

import Data.ByteString (ByteString)
import Data.Vector.Storable (modify)
import Data.Vector.Storable.ByteString  -- provided by the "spool" package
import Data.Vector.Storable.Mutable (write)
import Data.Word (Word8)

updateAt :: Int -> Word8 -> ByteString -> ByteString
updateAt n x s = vectorToByteString . modify inner . byteStringToVector
  where
    inner v = write v n x

См. документацию для spool и < a href="https://hackage.haskell.org/package/vector-0.11.0.0/docs/Data-Vector-Storable.html#g:18" rel="nofollow">vector.

person Lambda Fairy    schedule 18.11.2015
comment
Если я правильно понимаю, здесь используются 4 отдельные функции, две из которых — просто преобразование. Считаете ли вы, что Data.Vector может быть подходящей заменой Data.ByteString в этом случае? (Чтобы избавиться от двух преобразований) - person Daniel Lovasko; 19.11.2015
comment
@DanielLovasko Я абсолютно думаю, что замена ByteString на изменяемую структуру здесь уместна. Я бы пошел еще дальше, чем использовать Vector для использования, например. STArray и поднимите пошаговую операцию виртуальной машины до ST. Переход от повторного обновления O(n) к O(1) не вызывает затруднений, когда важна производительность. - person Daniel Wagner; 19.11.2015
comment
@DanielWagner Я никогда раньше не слышал о монаде ST, но здесь имеет смысл использовать ее. Всем, кто читает это, я настоятельно рекомендую журналы IRC в начале этой страницы. - person Daniel Lovasko; 19.11.2015