Миран Липовача - Изучай Haskell во имя добра! Страница 46
- Категория: Компьютеры и Интернет / Программирование
- Автор: Миран Липовача
- Год выпуска: -
- ISBN: -
- Издательство: -
- Страниц: 96
- Добавлено: 2019-05-29 10:36:33
Миран Липовача - Изучай Haskell во имя добра! краткое содержание
Прочтите описание перед тем, как прочитать онлайн книгу «Миран Липовача - Изучай Haskell во имя добра!» бесплатно полную версию:На взгляд автора, сущность программирования заключается в решении проблем. Программист всегда думает о проблеме и возможных решениях – либо пишет код для выражения этих решений.Язык Haskell имеет множество впечатляющих возможностей, но главное его свойство в том, что меняется не только способ написания кода, но и сам способ размышления о проблемах и возможных решениях. Этим Haskell действительно отличается от большинства языков программирования. С его помощью мир можно представить и описать нестандартным образом. И поскольку Haskell предлагает совершенно новые способы размышления о проблемах, изучение этого языка может изменить и стиль программирования на всех прочих.Ещё одно необычное свойство Haskell состоит в том, что в этом языке придаётся особое значение рассуждениям о типах данных. Как следствие, вы помещаете больше внимания и меньше кода в ваши программы.Вне зависимости от того, в каком направлении вы намерены двигаться, путешествуя в мире программирования, небольшой заход в страну Haskell себя оправдает. А если вы решите там остаться, то наверняка найдёте чем заняться и чему поучиться!Эта книга поможет многим читателям найти свой путь к Haskell.Отображения, монады, моноиды и другое!Всё сказано в названии: «Изучай Хаскель во имя добра!» – весёлый иллюстрированный самоучитель по этому сложному функциональному языку.С помощью оригинальных рисунков автора, отсылке к поп-культуре, и, самое главное, благодаря полезным примерам кода, эта книга обучает основам функционального программирования так, как вы никогда не смогли бы себе представить.Вы начнете изучение с простого материала: основы синтаксиса, рекурсия, типы и классы типов. Затем, когда вы преуспеете в основах, начнется настоящий мастер-класс от профессионала: вы изучите, как использовать аппликативные функторы, монады, застежки, и другие легендарные конструкции Хаскеля, о которых вы читали только в сказках.Продираясь сквозь образные (и порой безумные) примеры автора, вы научитесь:• Смеяться в лицо побочным эффектам, поскольку вы овладеете техниками чистого функционального программирования.• Использовать волшебство «ленивости» Хаскеля для игры с бесконечными наборами данных.• Организовывать свои программы, создавая собственные типы, классы типов и модули.• Использовать элегантную систему ввода-вывода Хаскеля, чтобы делиться гениальностью ваших программ с окружающим миром.Нет лучшего способа изучить этот мощный язык, чем чтение «Изучай Хаскель во имя добра!», кроме, разве что, поедания мозга его создателей.Миран Липовача (Miran Lipovača) изучает информатику в Любляне (Словения). Помимо его любви к Хаскелю, ему нравится заниматься боксом, играть на бас-гитаре и, конечно же, рисовать. У него есть увлечение танцующими скелетами и числом 71, а когда он проходит через автоматические двери, он притворяется, что на самом деле открывает их силой своей мысли.
Миран Липовача - Изучай Haskell во имя добра! читать онлайн бесплатно
$ ./arg-test first second w00t "multi word arg"
Аргументы командной строки:
first
second
w00t
multi word arg
Имя программы:
arg-test
Ещё больше шалостей со списком дел
В предыдущих примерах мы писали отдельные программы для добавления и удаления заданий в списке дел. Теперь мы собираемся объединить их в новое приложение, а что ему делать, будем указывать в командной строке. Кроме того, позаботимся о том, чтобы программа смогла работать с разными файлами – не только 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, которая примет файл и номер задачи и вернёт действие ввода-вывода, которое поднимет указанную задачу на вершину списка задач.
Работаем с некорректным вводом
Можно было бы дописать эту программу, улучшив сообщения об ошибках, возникающих при некорректных исходных данных. Начать можно с добавления варианта функции dispatch, который срабатывает при любой несуществующей команде:
dispatch :: String -> [String] -> IO ()
dispatch "add" = add
dispatch "view" = view
dispatch "remove" = remove
dispatch command = doesntExist command
doesntExist :: String -> [String] -> IO ()
doesntExist command _ =
putStrLn $ "Команда " ++ command ++ " не определена"
Также можно добавить варианты определения функций add, view и remove для случаев, когда программе передано неправильное количество аргументов. Например:
Жалоба
Напишите нам, и мы в срочном порядке примем меры.