потом применяем два арифметических действия ко всем элементам списка, затем переворачиваем список.
Проверим работает ли это в интерпретаторе, заодно поупражняемся в композиционном стиле:
Prelude> letns n = if(n ==0) then [] elsen :ns (n -1)
Prelude> leteven x =0 ==mod x 2
Prelude> letxs =reverse $map (( +1) .( *10)) $filter even $ns 20
Prelude>xs
[21,41,61,81,101,121,141,161,181,201]
Если бы не функция применения нам пришлось бы написать это выражение так:
xs =reverse (map (( +1) .( *10)) (filter even (ns 40)))
5.3 Функциональный калькулятор
Мне бы хотелось сделать акцент на одном из вступительных предложений этой главы:
За счёт развитых средств составления новых функций в Haskell пользователь определяет лишь
базовые функции, получая остальные “на лету” применением двух-трёх операций, это выглядит
примерно как (2 +3) *5, где вместо чисел стоят базовые функции, а операции +и *составляют
новые функции из простейших.
Такие обобщённые функции как id, const, ( .), map filter позволяют очень легко комбинировать различ-
ные функции. Бесточечный стиль записи функций превращает функции в простые значения или значения-
константы, которые можно подставлять в другие функции. В этом разделе мы немного потренируемся в пе-
регрузке численных значений и превратим числа в функции, функции и в самом деле станут константами.
Мы определим экземпляр Numдля функций, которые возвращают числа. Смысл этих операций заключается в
том, что теперь мы применяем обычные операции сложения умножения к функциям, аргумент которых сов-
падает по типу. Например для того чтобы умножить функции \t ->t +2 и \t ->t +3 мы составляем новую
функцию \t ->(t +2) *(t +3), которая получает на вход значение t применяет его к каждой из функций и
затем умножает результаты:
78 | Глава 5: Функции высшего порядка
module FunNat where
import Prelude( Show( ..), Eq( ..), Num( ..), error)
instance Show(t ->a) where
show _ = error”Sorry, no show. It’s just for Num”
instance Eq(t ->a) where
( ==) _ _ = error”Sorry, no Eq. It’s just for Num”
instance Numa => Num(t ->a) where
( +) =fun2 ( +)
( *) =fun2 ( *)
( -) =fun2 ( -)
abs
=fun1 abs
signum
=fun1 signum
fromInteger =const .fromInteger
fun1 ::(a ->b) ->((t ->a) ->(t ->b))
fun1 =( .)
fun2 ::(a ->b ->c) ->((t ->a) ->(t ->b) ->(t ->c))
fun2 op a b =\t ->a t ‘op‘ b t
Функции fun1 и fun2 превращают функции, которые принимают значения, в функции, которые прини-
мают другие функции.
Из-за контекста класса Numнам пришлось объявить два фиктивных экземпляра для классов Showи Eq.
Загрузим модуль FunNatв интерпретатор и посмотрим что же у нас получилось:
Prelude> :l FunNat.hs
[1 of1] Compiling FunNat
( FunNat.hs, interpreted )
Ok, modules loaded : FunNat.
*FunNat>2 2
2
*FunNat>2 5
2
*FunNat>(2 +( +1)) 0
3
*FunNat>(( +2) *( +3)) 1
12
На первый взгляд кажется что выражение 2 2 не должно пройти проверку типов, ведь мы применяем
значение к константе. Но на самом деле 2 это не константа, а значение 2 :: Numa =>a и подспудно к двойке
применяется функция fromInteger. Поскольку в нашем модуле мы определили экземпляр Numдля функций,
второе число 2 было конкретизировано по умолчанию до Integer, а первое число 2 было конкретизировано
до Integer -> Integer. Компилятор вывел из контекста, что под 2 мы понимаем функцию. Функция была
создана с помощью метода fromInteger. Эта функция принимает любое значение и возвращает двойку.
Далее мы складываем и перемножаем функции словно это обычные значения. Что интересно мы можем
составлять и такие выражения:
*FunNat> letf =(( +) -( *))
Читать дальше