Байтовые строки бывают двух видов: строгие и ленивые. Строгие байтовые строки объявлены в модуле Data.ByteString
, и они полностью не ленивые. Не используется никаких «обещаний», строгая строка байтов представляет собой последовательность байтов в массиве. Подобная строка не может быть бесконечной. Если вы вычисляете первый байт из строгой строки, вы должны вычислить её целиком. Положительный момент – меньше накладных расходов, поскольку не используются «обещания». Отрицательный момент – такие строки заполнят память быстрее, так как они считываются целиком.
Второй вид байтовых строк определён в модуле Data.ByteString. Lazy
. Они ленивы – но не настолько, как списки. Как мы говорили ранее, в списке столько же «обещаний», сколько элементов. Вот почему это может сделать его медленным для некоторых целей. Ленивые строки байтов применяют другой подход: они хранятся блоками размером 64 Кб. Если вы вычисляете байт в ленивой байтовой строке (печатая или другим способом), то будут вычислены первые 64 Кб. После этого будет возращено обещание вычислить остальные блоки. Ленивые байтовые строки похожи на список строгих байтовых строк размером 64 Кб. При обработке файла ленивыми байтовыми строками файл будет считываться блок за блоком. Это удобно, потому что не вызывает резкого увеличения потребления памяти, и 64 Кб, вероятно, влезет в L2 – кэш вашего процессора.
Если вы посмотрите документацию на модуль Data.ByteString. Lazy
, то увидите множество функций с такими же именами, как и в модуле Data.List
, только в сигнатурах функций будет указан тип ByteString
вместо [a]
и Word8
вместо a
. Функции в этом модуле работают со значениями типа ByteString
так же, как одноимённые функции – со списками. Поскольку имена совпадают, нам придётся сделать уточнённый импорт в скрипте и затем загрузить этот скрипт в интерпретатор GHCi для того, чтобы поэкспериментировать с типом ByteString
.
import qualified Data.ByteString.Lazy as B
import qualified Data.ByteString as S
Модуль
Функция pack
имеет сигнатуру pack :: [Word8] –> ByteString
. Это означает, что она принимает список байтов типа Word8 и возвращает значение типа ByteString
. Можно думать, будто функция принимает ленивый список и делает его менее ленивым, так что он ленив только блоками по 64 Кб.
Что за тип Word8
? Он похож на Int
, но имеет значительно меньший диапазон, а именно 0 – 255. Тип представляет собой восьми битовое число. Так же как и Int
, он имеет экземпляр класса Num
. Например, мы знаем, что число 5 полиморфно, а значит, оно может вести себя как любой числовой тип. В том числе – принимать тип Word8
.
ghci> B.pack [99,97,110]
Chunk "can" Empty
ghci> B.pack [98..120]
Chunk "bcdefghijklmnopqrstuvwx" Empty
Как можно видеть, Word8
не доставляет много хлопот, поскольку система типов определяет, что числа должны быть преобразованы к нему. Если вы попытаетесь использовать большое число, например 336, в качестве значения типа Word8
, число будет взято по модулю 256, то есть сохранится 80.
Мы упаковали всего несколько значений в тип ByteString
; они уместились в один блок. Значение Empty
– это нечто вроде []
для списков.
Если нужно просмотреть байтовую строку байт за байтом, её нужно распаковать. Функция unpack
обратна функции pack
. Она принимает строку байтов и возвращает список байтов. Вот пример:
ghci> let by = B.pack [98,111,114,116]
ghci> by
Chunk "bort" Empty
ghci> B.unpack by
[98,111,114,116]
Вы также можете преобразовывать байтовые строки из строгих в ленивые и наоборот. Функция fromChunks
принимает список строгих строк и преобразует их в ленивую строку. Соответственно, функция toChunks
принимает ленивую строку байтов и преобразует её в список строгих строк.
ghci> B.fromChunks [S.pack [40,41,42], S.pack [43,44,45], S.pack [46,47,48]]
Chunk "()*" (Chunk "+,–" (Chunk "./0" Empty))
Это полезно, если у вас есть множество маленьких строгих строк байтов и вы хотите эффективно обработать их, не объединяя их в памяти в одну большую строгую строку.
Аналог конструктора :
для строк байтов называется cons
. Он принимает байт и строку байтов и помещает байт в начало строки.
ghci> B.cons 85 $ B.pack [80,81,82,84]
Chunk "U" (Chunk "PQRT" Empty)