Миран Липовача - Изучай Haskell во имя добра!
- Название:Изучай Haskell во имя добра!
- Автор:
- Жанр:
- Издательство:ДМК Пресс
- Год:2012
- Город:Москва
- ISBN:978-5-94074-749-9
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Миран Липовача - Изучай Haskell во имя добра! краткое содержание
Язык Haskell имеет множество впечатляющих возможностей, но главное его свойство в том, что меняется не только способ написания кода, но и сам способ размышления о проблемах и возможных решениях. Этим Haskell действительно отличается от большинства языков программирования. С его помощью мир можно представить и описать нестандартным образом. И поскольку Haskell предлагает совершенно новые способы размышления о проблемах, изучение этого языка может изменить и стиль программирования на всех прочих.
Ещё одно необычное свойство Haskell состоит в том, что в этом языке придаётся особое значение рассуждениям о типах данных. Как следствие, вы помещаете больше внимания и меньше кода в ваши программы.
Вне зависимости от того, в каком направлении вы намерены двигаться, путешествуя в мире программирования, небольшой заход в страну Haskell себя оправдает. А если вы решите там остаться, то наверняка найдёте чем заняться и чему поучиться!
Эта книга поможет многим читателям найти свой путь к Haskell.
Отображения, монады, моноиды и другое! Всё сказано в названии: «Изучай Хаскель во имя добра!» – весёлый иллюстрированный самоучитель по этому сложному функциональному языку.
С помощью оригинальных рисунков автора, отсылке к поп-культуре, и, самое главное, благодаря полезным примерам кода, эта книга обучает основам функционального программирования так, как вы никогда не смогли бы себе представить.
Вы начнете изучение с простого материала: основы синтаксиса, рекурсия, типы и классы типов. Затем, когда вы преуспеете в основах, начнется настоящий мастер-класс от профессионала: вы изучите, как использовать аппликативные функторы, монады, застежки, и другие легендарные конструкции Хаскеля, о которых вы читали только в сказках.
Продираясь сквозь образные (и порой безумные) примеры автора, вы научитесь:
• Смеяться в лицо побочным эффектам, поскольку вы овладеете техниками чистого функционального программирования.
• Использовать волшебство «ленивости» Хаскеля для игры с бесконечными наборами данных.
• Организовывать свои программы, создавая собственные типы, классы типов и модули.
• Использовать элегантную систему ввода-вывода Хаскеля, чтобы делиться гениальностью ваших программ с окружающим миром.
Нет лучшего способа изучить этот мощный язык, чем чтение «Изучай Хаскель во имя добра!», кроме, разве что, поедания мозга его создателей. Миран Липовача (Miran Lipovača) изучает информатику в Любляне (Словения). Помимо его любви к Хаскелю, ему нравится заниматься боксом, играть на бас-гитаре и, конечно же, рисовать. У него есть увлечение танцующими скелетами и числом 71, а когда он проходит через автоматические двери, он притворяется, что на самом деле открывает их силой своей мысли.
Изучай Haskell во имя добра! - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
(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
для случаев, когда программе передано неправильное количество аргументов. Например:
add :: [String] -> IO ()
add [fileName, todoItem] = appendFile fileName (todoItem ++ "\n")
add _ = putStrLn "Команда add принимает в точности два аргумента"
Если функция add
будет применена к списку, содержащему не два элемента, первый образец не сработает, поэтому пользователю будет выведено сообщение об ошибке. Аналогично дописываются функции view
и remove
.
Интервал:
Закладка: