ЗНАЕТЕ ЛИ ВЫ?

Ещё больше шалостей со списком дел



В предыдущих примерах мы писали отдельные программы для до- бавления и удаления заданий в списке дел. Теперь мы собираемся объединить их в новое приложение, а что ему делать, будем указы- вать в командной строке. Кроме того, позаботимся о том, чтобы программа смогла работать с разными файлами – не только todo.txt.

Назовём программу просто todo, она сможет делать три разные вещи:

® просматривать задания;

® добавлять задания;

® удалять задания.

 
 

Для добавления нового задания в список дел в файле todo.txt мы будем писать:

$ ./todo add todo.txt "Найти магический меч силы"


 

 
 

Просмотреть текущие задания можно будет командой view:

$ ./todo view todo.txt

 
 

Для удаления задания потребуется дополнительно указать его индекс:

$ ./todo remove todo.txt 2

 

Многозадачный список задач

 
 

Начнём с реализации функции, которая принимает команду в виде строки (например, "add" или "view") и возвращает функцию, кото- раявсвоюочередьпринимаетсписокаргументовивозвращаетдейс- твие ввода-вывода, выполняющее в точности то, что необходимо:

import System.Environment import System.Directory import System.IO

import Data.List

import Control.Exception

 

dispatch :: String -> [String] –> IO () dispatch "add" = add

 
 

dispatch "view" = view dispatch "remove" = remove

Функция main будет выглядеть так:

main = do

 
 

(command:argList) <- getArgs dispatch command argList

Первым делом мы получаем аргументы и связываем их со спис- ком (command:argsList). Таким образом, первый аргумент будет связан с именем command, а все остальные – со списком argList. В следующей строке к переменной commands применяется функ- ция dispatch, результатом которой может быть одна из функций add, view или remove. Затем результирующая функция применяется к списку аргументов argList.

Предположим, программа запущена со следующими парамет- рами:


 

 
 

$ ./todo add todo.txt "Найти магический меч силы"

Тогда значением command будет "add", а значением argList – спи- сок ["todo.txt", "Найти магический меч силы"]. Поэтому сработает первый вариант определения функции dispatch и будет возвраще- на функция add. Применяем её к argList, результатом оказывается действие ввода-вывода, добавляющее новое задание в список.

 
 

Теперь давайте реализуем функции add, view и remove. Начнём с первой из них:

add :: [String] –> IO ()

 
 

add [fileName, todoItem] = appendFile fileName (todoItem ++ "\n")

При вызове

 
 

$ ./todo add todo.txt "Найти магический меч силы"

функции add будет передан список ["todo.txt", "Найти магический меч силы"]. Поскольку пока мы не обрабатываем некорректный ввод, достаточно будет сопоставить аргумент функции add с двухэлемен- тным списком. Результатом функции будет действие ввода-вывода, добавляющее строку вместе с символом конца строки в конец фай- ла.

Далее реализуем функциональность просмотра списка. Если мы хотим просмотреть элементы списка, то вызываем программу так: todo view todo.txt. В первом сопоставлении с образцом идентифика- тор command будет связан со строкой view, а идентификатор argList будет равен ["todo.txt"].

 
 

Вот код функции view:

view :: [String] –> IO () view [fileName] = do

contents <– readFile fileName let todoTasks = lines contents

numberedTasks = zipWith (\n line –> show n ++ " – " ++ line) [0..] todoTasks

 
 

putStr $ unlines numberedTasks

Программа, которая удаляла задачу из списка, производила практически те же самые действия: мы отображали список задач, чтобы пользователь мог выбрать, какую из них удалить. Но в этой функции мы просто отображаем список.


 

 
 

Ну и наконец реализуем функцию remove. Функция будет очень похожа на программу для удаления элемента, так что если вы не понимаете, как работает функция удаления, прочитайте поясне- ния к её определению. Основное отличие – мы не задаём жёстко имя файла, а получаем его как аргумент. Также мы не спрашиваем у пользователя номер задачи для удаления – его мы также получаем в виде аргумента.

remove :: [String] -> IO ()

remove [fileName, numberString] = do contents <- readFile fileName let todoTasks = lines contents

number = read numberString

newTodoItems = unlines $ delete (todoTasks !! number) todoTasks bracketOnError (openTempFile "." "temp")

(\(tempName, tempHandle) –> do hClose tempHandle removeFile tempName)

(\(tempName, tempHandle) –> do hPutStr tempHandle newTodoItems hClose tempHandle

 
 

removeFile fileName renameFile tempName fileName)

Мы открываем файл, полное имя которого задаётся в иденти- фикаторе fileName, открываем временный файл, удаляем строку по индексу, записываем во временный файл, удаляем исходный файл и переименовываем временный в fileName. Приведем полный лис- тинг программы во всей её красе:

import System.Environment import System.Directory import System.IO

import Control.Exception import Data.List

 

dispatch :: String -> [String] -> IO () dispatch "add" = add

dispatch "view" = view dispatch "remove" = remove

 

main = do


 

(command:argList) <- getArgs dispatch command argList

 

add :: [String] -> IO ()

add [fileName, todoItem] = appendFile fileName (todoItem ++ "\n")

 

view :: [String] -> IO () view [fileName] = do

contents <- readFile fileName let todoTasks = lines contents

numberedTasks = zipWith (\n line -> show n ++ " – " ++ line) [0..] todoTasks

putStr $ unlines numberedTasks

 

remove :: [String] -> IO ()

remove [fileName, numberString] = do contents <- readFile fileName let todoTasks = lines contents

number = read numberString

newTodoItems = unlines $ delete (todoTasks !! number) todoTasks bracketOnError (openTempFile "." "temp")

(\(tempName, tempHandle) -> do hClose tempHandle removeFile tempName)

(\(tempName, tempHandle) -> do hPutStr tempHandle newTodoItems hClose tempHandle

 
 

removeFile fileName renameFile tempName fileName)

Резюмируем наше решение. Мы написали функцию dispatch, отображающую команды на функции, которые принимают аргумен- ты командной строки в виде списка и возвращают соответствующее действие ввода-вывода. Основываясь на значении первого аргумен- та, функция dispatch даёт нам необходимую функцию. В результате вызова этой функции мы получаем требуемое действие и выполня- ем его.

 
 

Давайте проверим, как наша программа работает:

$ ./todo view todo.txt 0 – Погладить посуду 1 – Помыть собаку


 

2 – Вынуть салат из печи

 

$ ./todo add todo.txt "Забрать детей из химчистки"

 

$ ./todo view todo.txt 0 – Погладить посуду 1 – Помыть собаку

2 – Вынуть салат из печи

3 – Забрать детей из химчистки

 

$ ./todo remove todo.txt 2

 

$ ./todo view todo.txt 0 – Погладить посуду 1 – Помыть собаку

2

 
 

– Забрать детей из химчистки

Большой плюс такого подхода – легко добавлять новую функ- циональность. Добавить вариант определения функции dispatch, реализовать соответствующую функцию – и готово! В качестве уп- ражнения можете реализовать функцию bump, которая примет файл и номер задачи и вернёт действие ввода-вывода, которое поднимет указанную задачу на вершину списка задач.

 





Последнее изменение этой страницы: 2017-02-17; Нарушение авторского права страницы

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.237.66.86 (0.012 с.)