Изучай Haskell во имя добра! (Липовача) - страница 73

>firstTo :: Int -> Maybe Int

>firstTo n = find (\x -> digitSum x == n) [1..]

И небольшая проверка:

>ghci> firstTo 27

>Just 999

>ghci> firstTo 1

>Just 1

>ghci> firstTo 13

>Just 49

Отображение ключей на значения

Зачастую, работая с данными из некоторого набора, мы совершенно не заботимся, в каком порядке они расположены. Мы просто хотим получить к ним доступ по некоторому ключу. Например, желая узнать, кто живёт по известному адресу, мы ищем имена тех, кто по этому адресу проживает. В общем случае мы говорим, что ищем значение (чьё-либо имя) по ключу (адрес этого человека).

Почти хорошо: ассоциативные списки

Существует много способов построить отображение «ключ–значение». Один из них – ассоциативные списки. Ассоциативные списки (также называемые словарями или отображениями) – это списки, которые хранят неупорядоченные пары «ключ–значение». Например, мы можем применять ассоциативные списки для хранения телефонных номеров, используя телефонный номер как значение и имя человека как ключ. Нам неважно, в каком порядке они сохранены: всё, что нам требуется, – получить телефонный номер по имени. Наиболее простой способ представить ассоциативный список в языке Haskell – использовать список пар. Первый компонент пары будет ключом, второй – значением. Вот пример ассоциативного списка с номерами телефонов:

>phoneBook =

>  [("оля","555–29-38")

>  ,("женя","452–29-28")

>  ,("катя","493–29-28")

>  ,("маша","205–29-28")

>  ,("надя","939–82-82")

>  ,("юля","853–24-92")

>  ]

За исключением странного выравнивания, это просто список, состоящий из пар строк. Самая частая задача при использовании ассоциативных списков – поиск некоторого значения по ключу. Давайте напишем функцию для этой задачи.

>findKey :: (Eq k) => k –> [(k,v)] –> v

>findKey key xs = snd . head $ filter (\(k,v) –> key == k) xs

Всё довольно просто. Функция принимает ключ и список, фильтрует список так, что остаются только совпадающие ключи, получает первую пару «ключ–значение», возвращает значение. Но что произойдёт, если искомого ключа нет в списке? В этом случае мы будем пытаться получить «голову» пустого списка, что вызовет ошибку времени выполнения. Однако следует стремиться к тому, чтобы наши программы были более устойчивыми к «падениям», поэтому давайте используем тип >Maybe. Если мы не найдём ключа, то вернём значение >Nothing. Если найдём, будем возвращать >Just <то, что нашли>.

>findKey :: (Eq k) => k –> [(k,v)] –> Maybe v

>findKey key [] = Nothing

>findKey key ((k,v):xs)

>  | key == k = Just v

>  | otherwise = findKey key xs

Посмотрите на декларацию типа. Функция принимает ключ, который можно проверить на равенство (