Переключение на байтовые строки

EDIT: я последовал советам Юраса и Dave4420 (спасибо). У меня все еще есть некоторые ошибки. Обновил вопрос. Наконец, я буду использовать версию Мейерси (спасибо), но я все еще хочу найти свои ошибки...

У меня есть простой скрипт, который выглядит так:

import System.Environment

getRow :: Int -> String -> String
getRow n = (!!n) . lines

getField :: Int -> String -> String
getField n = (!!n) . words'

words' :: String -> [String]
words' str = case str of
                        [] -> []
                        _ -> (takeHead " ; " str) : (words' (takeTail " ; " str))

takeHead :: String -> String -> String
takeHead st1 st2 = case st2 of
                                [] -> []
                                _ -> if st1 == (nHead (length st1) st2) then [] else (head st2):(takeHead st1 (tail st2))

takeTail :: String -> String -> String
takeTail st1 st2 = case st2 of
                                [] -> []
                                _ -> if st1 == (nHead (length st1) st2) then nTail (length st1) st2 else takeTail st1 (tail st2)

nTail :: Int -> String -> String
nTail n str = let rec n str = if n == 0 then str else rec (n - 1) (tail str)
              in if (length str) < n then str else rec n str

nHead :: Int -> String -> String
nHead n str = let rec n str = if n == 0 then [] else (head str):(rec (n - 1) (tail str))
              in if (length str) < n then str else rec n str

getValue :: String -> String -> String -> String
getValue row field src = getField (read field) $ getRow (read row) src

main :: IO ()
main = do
    args <- getArgs
    case args of
        (path: opt1: opt2: _) -> do
            src <- readFile path
            putStrLn $ getValue opt1 opt2 src
        (path: _) -> do
            src <- readFile path
            putStrLn $ show $ length $ lines src

Он компилируется и работает. Потом захотелось перейти на ByteStrings. Вот моя попытка:

import qualified Data.ByteString.Lazy as B
import qualified Data.ByteString.Lazy.Char8 as Bc (cons, empty,unpack)
import qualified Data.ByteString.Lazy.UTF8 as Bu (lines)
import qualified System.Posix.Env.ByteString as Bg (getArgs)

separator :: B.ByteString
separator = (Bc.cons ' ' (Bc.cons ';' (Bc.cons ' ' Bc.empty)))

getRow :: Int -> B.ByteString -> B.ByteString
getRow n = (`B.index` n) $ Bu.lines

getCol :: Int -> B.ByteString -> B.ByteString
getCol n = (`B.index` n) $ wordsWithSeparator

wordsWithSeparator :: B.ByteString -> [B.ByteString]
wordsWithSeparator str = if B.null str then [] else (takeHead separator str):(wordsWithSeparator (takeTail separator str))

takeHead :: B.ByteString -> B.ByteString -> B.ByteString
takeHead st1 st2 = if B.null st2 then B.empty else if st1 == (nHead (toInteger (B.length st1)) st2) then B.empty else B.cons (B.head st2) (takeHead st1 (B.tail st2))

takeTail :: B.ByteString -> B.ByteString -> B.ByteString
takeTail st1 st2 = if B.null st2 then B.empty else if st1 == (nHead (toInteger (B.length st1)) st2) then nTail (toInteger (B.length st1)) st2 else takeTail st1 (B.tail st2)

nTail :: Integer -> B.ByteString -> B.ByteString
nTail n str = let rec n str = if n == 0 then str else rec (n - 1) (B.tail str)
              in if (toInteger (B.length str)) < n then str else rec n str

nHead :: Integer -> B.ByteString -> B.ByteString
nHead n str = let rec n str = if n == 0 then B.empty else B.cons (B.head str)(rec (n - 1) (B.tail str))
              in if (toInteger (B.length str)) < n then str else rec n str

getValue :: B.ByteString -> B.ByteString -> B.ByteString -> B.ByteString
getValue row field = getCol (read (Bc.unpack field)) . getRow (read (Bc.unpack row))

main = do args <- Bg.getArgs
          case (map (B.fromChunks . return) args) of
                                                    (path:opt1:opt2:_) -> do src <- B.readFile (Bc.unpack path)
                                                                             B.putStrLn $ getValue opt1 opt2 src

                                                    (path:_)           -> do src <- B.readFile (Bc.unpack path)
                                                                             putStrLn $ show $ length $ Bu.lines src

Это не работает. Я не мог отладить это. Вот что мне говорит GHC:

BETA_getlow2.hs:10:23:
    Couldn't match expected type `GHC.Int.Int64' with actual type `Int'
    In the second argument of `B.index', namely `n'
    In the expression: (`B.index` n)
    In the expression: (`B.index` n) $ Bu.lines

BETA_getlow2.hs:13:23:
    Couldn't match expected type `GHC.Int.Int64' with actual type `Int'
    In the second argument of `B.index', namely `n'
    In the expression: (`B.index` n)
    In the expression: (`B.index` n) $ wordsWithSeparator

Любые советы будут оценены.


person sarfraz    schedule 24.04.2012    source источник
comment
Вы можете вставить свой код в строку, и он будет выделен. Просто не забудьте оставить пустую строку до и после, а также сделать отступ каждой строки кода не менее четырех пробелов.   -  person rampion    schedule 25.04.2012
comment
Нормально будет делать. кажется, я не могу поставить баллы...   -  person sarfraz    schedule 26.04.2012


Ответы (3)


Я беру на себя смелость интерпретировать следующие два подвопроса в ваш первоначальный вопрос.

  1. Какой код на Haskell обычно пишут для сценария, подобного тому, который вы опубликовали.
  2. Каковы правильные структуры данных для эффективного выполнения желаемой функциональности.

Следующий код дает один ответ на эти два подвопроса. Он использует библиотеку text для представления последовательностей символов Unicode. Кроме того, он использует высокоуровневый API библиотеки text для реализации желаемой функциональности. Это упрощает понимание кода и тем самым позволяет избежать возможных ошибок при реализации низкоуровневых функций.

{-# LANGUAGE OverloadedStrings #-}

import qualified Data.Text    as T
import qualified Data.Text.IO as T

import System.Environment (getArgs)

type Table a = [[a]]

-- | Split a text value into a text table.
toTable :: T.Text -> Table T.Text
toTable = map (T.splitOn " ; ") . T.lines

-- | Retrieve a cell from a table.
cell :: Int -> Int -> Table a -> a
cell row col = (!! col) . (!! row)

main :: IO ()
main = do
    (path:rest) <- getArgs
    src <- T.readFile path
    case rest of
        row : col : _ -> T.putStrLn $ cell (read row) (read col) $ toTable src
        _             -> putStrLn $ show $ length $ T.lines src
person meiersi    schedule 25.04.2012

getRow n = (!!n) . lines

Сравнить с

getRow n = B.index . Bu.lines

Во второй версии вы вообще не используете n, так что это то же самое, что и

getRow _ = B.index . Bu.lines

В первом примере вы используете n в качестве аргумента оператора (!!). То же самое нужно сделать во второй версии.

Похоже, это не единственная проблема в вашем коде, но я надеюсь, что это хорошая точка для начала;)

person Yuras    schedule 24.04.2012

Первые две ошибки, я думаю, за вас исправил Юрий.


Насчет 3-й ошибки:

words' :: B.ByteString -> [B.ByteString]
words' str = if B.null str then B.empty else ...

B.empty должно быть []. B.empty :: B.ByteString, но результат должен иметь тип [B.ByteString].


О 4-7 ошибках:

  • length :: [a] -> Int
  • B.length :: B.ByteString -> Int64

В этом случае я бы изменил сигнатуры типов nTail и nHead, чтобы использовать Int64 вместо Int. Если бы это не сработало, я бы использовал Integer для всех типов Integral, используя toInteger для преобразования.


О 8-й ошибке:

Ввод read должен быть String. Этого не обойти. Вам нужно будет преобразовать B.ByteString в String и передать это read.

(Кстати, вы уверены, что хотите переключиться на ByteString, а не на Text?)


Относительно 9-й (последней) ошибки:

args :: [Data.ByteString.ByteString] (обратите внимание, это список строгих строк байтов, а не ленивых строк байтов, которые вы используете где-то еще), но в соответствии с шаблоном вы по какой-то причине ожидаете args :: B.ByteString.

Вы должны сопоставлять шаблоны для [ByteString] так же, как и для [String]: они оба являются списками.

Преобразуйте args во что-то типа [B.ByteString] с map (B.fromChunks . return) args.

person dave4420    schedule 25.04.2012