Просмотреть текущие задания можно будет командой 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