C语言提供了一种非常方便的方法来更新数组的nth
元素:array[n] = new_value
。我对Data.ByteString
类型的理解是它提供了与uint8_t
的C数组非常相似的功能 - 通过index :: ByteString -> Int -> Word8
访问。似乎相反的操作 - 更新值 - 并不那么容易。
我最初的做法是使用以下方式获得的take
,drop
和singleton
函数concat
:
updateValue :: ByteString -> Int -> Word8 -> ByteString
updateValue bs n value = concat [take (n-1) bs, singleton value, drop (n+1) bs]
(这是一个非常天真的实现,因为它不处理边缘情况)
使用C背景时,感觉位太重了,无法调用4个函数来更新一个值。从理论上讲,操作的复杂性并不是那么糟糕:
take
是O(1)drop
是O(1)singleton
是O(1)concat
是O(n),但在这里我不确定 n 是否完全是连接列表的长度,或者它是否只是,在我们的例子中,是< / LI>
我的第二种方法是向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"
答案 0 :(得分:3)
一种解决方案是将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