Другие решали проблему выполнения операций с байтами, поэтому я сосредоточусь на другой половине вашего вопроса: выборе и обновлении определенного байта в пределах ByteString
. Давайте начнем с реализации операции для простых списков, используя более знакомый интерфейс:
onNth :: Int -> (a -> a) -> ([a] -> [a])
onNth n f xs = case splitAt n xs of
(beginning, x:ending) -> beginning ++ f x : ending
_ -> xs -- happens when n is out-of-bounds
Вы могли бы эквивалентно реализовать это, используя take
и drop
вместо splitAt
. Теперь, как мы можем перевести это, чтобы работать на ByteString
с? Ну, интерфейс ByteString
предлагает take
, drop
, splitAt
, append
и cons
; единственное, что у нас не совсем получилось, это сопоставление с образцом, которое мы сделали в x:ending
части выше. К счастью, ByteString
предлагает нечто подобное:
uncons :: ByteString -> Maybe (Word8, ByteString)
Итак, используя это, мы можем написать новую onNth
функцию, которая работает для ByteString
s:
second :: (b -> c) -> (a, b) -> (a, c)
second f (a, b) = (a, f b)
onNth :: Int -> (Word8 -> Word8) -> (ByteString -> ByteString)
onNth n f bs = case second uncons (splitAt n bs) of
(beginning, Just (x, ending)) -> append beginning (cons (f x) ending)
_ -> bs -- again, for out-of-bounds cases
Наконец, мы можем обсудить, какую функцию мы должны использовать в качестве аргумента f :: Word8 -> Word8
выше. Хотя вы говорите о тексте выше, я укажу, что вы все равно не должны использовать ByteString
для текста (ByteString
s - это последовательности байтов, а не последовательности Char
s). Поэтому, если вы решили использовать ByteString
, вы должны говорить о байтах, а не о тексте. ; -)
Следовательно, вы действительно хотели спросить о функции, которая уменьшает байт на единицу, по-видимому, оборачиваясь на границе. subtract 1
- это функция, которая делает именно это, поэтому для преобразования pack [97, 97, 97, 97, 97]
в pack [97, 97, 96, 97, 97]
вы можете написать onNth 2 (subtract 1)
. Читает почти как на английском!