Как объединить пользовательский ввод со списком кортежей и записать полный список кортежей в файл?

#haskell

#haskell

Вопрос:

Я пытаюсь использовать пользовательский ввод и преобразовать его в виде списка кортежей. Что я хочу сделать, так это то, что мне нужно взять данные от пользователя и преобразовать их в форму [(Code,Name, Price)] и, наконец, объединить этот пользовательский ввод с предыдущим списком и записать новый список в тот же файл.

Проблема, с которой я сталкиваюсь, заключается в том, что, как только программа завершает прием пользовательского ввода, WinHugs выдает ошибку, подобную этой Program error: Prelude.read: no parse .

Вот код:

 type Code=Int
type Price=Int
type Name=String
type ProductDatabase=(Code,Name,Price)

finaliser=do
           a<-input_taker 
           b<-list_returner
           let w=a  b
           outh <- openFile "testx.txt" WriteMode
           Print outh w
           Close outh   
  

Ответ №1:

Проблема в том, что вы используете ленивый ввод-вывод для чтения из файла во время одновременной записи в него. Это вызывает проблемы при read просмотре данных, которые были частично записаны.

Нам нужно принудительно завершить чтение входных данных, прежде чем пытаться выполнить запись в файл. Один из способов сделать это — использовать seq для принудительного чтения списка продуктов в память.

 list_returner :: IO ([ProductDatabase])
list_returner = do
    inh <- openFile "testx.txt" ReadMode
    product_text <- hGetContents inh
    let product :: [ProductDatabase]
        product = read product_text
    product `seq` hClose inh
    return product
  

Кроме того, это приведет к сбою, если файл пуст. Файл должен содержать, по крайней мере [] , перед первым запуском вашего кода, чтобы он анализировался как пустой список.

Комментарии:

1. : Я получаю сообщение об ошибке «Не удается найти импортированный модуль». DeepSeq»»

2. @Roy: Это в пакете deepseq. Я не слишком знаком с тем, как устанавливать пакеты на WinHugs, но я бы рекомендовал вместо этого установить платформу Haskell , поскольку она намного более современная и включает в себя ряд распространенных пакетов, включая deepseq, из коробки.

3. Любой другой способ сделать это, я имею в виду, кроме deepseq

4. @Roy: На самом деле, поскольку read будет принудительно вводить весь ввод, этого должно быть достаточно для использования seq вместо deepseq . Попробуйте это.

5. : Пробовал. Но та же ошибка «Ошибка программы: Prelude.read: нет синтаксического анализа»

Ответ №2:

Для меня код выглядит нормально, за исключением определенных точек стиля. Это должно работать так. Постарайтесь больше разделять проблемы. Исключение «нет синтаксического анализа» означает, что read функция не смогла преобразовать строку своего аргумента в нужный тип. Базовая библиотека, поставляемая с Hugs, может быть более строгой в отношении пробелов и перевода строк. Я бы рекомендовал использовать GHC вместо Hugs в целом.

В случае, если вам интересно: один пункт стиля, который вы, возможно, захотите рассмотреть, — это использование withFile вместо комбинации openFile / hClose . Вы также можете использовать writeFile с show :

 writeFile "testx.txt" (show w)
  

Еще один момент стиля: ваше input_taker действие не должно возвращать список. На самом деле нет причин возвращать список. Вместо этого верните один кортеж, чтобы вы могли использовать (:) вместо ( ) . В целом использование ( ) указывает на то, что вы, возможно, используете неправильный подход.

Кроме того, ваше ProductDatabase имя типа вводит в заблуждение, потому что я бы интерпретировал [ProductDatabase] его как список баз данных. Ваш кортеж — a Product .

Заключительный пункт стиля: на самом деле речь идет только о красоте кода, поэтому это спорно. Это не C / C , поэтому вы действительно хотели бы написать f x вместо f(x) :

     ...
    return product

-- Since your `Product` is just a type alias, I would use
-- a smart constructor:
product :: Code -> Name -> Price -> Product
product = (,,)

readProduct :: IO Product
readProduct = do
    ...
    code <- fmap read getLine
    ...
    name <- getLine
    ...
    price <- fmap read getLine
    return (product code name price)