*FunNat>f 1 2
1
Как была вычислена эта функция? Мы определили экземпляр функций для значений типа Numa =>t
->a. Если мы вспомним, что функция двух аргументов на самом деле является функцией одного аргумента:
Numa =>t1 ->(t2 ->a), мы заметим, что тип Numa =>(t2 ->a) принадлежит Num, теперь если мы
обозначим его за a’, то мы получим тип Numa’ =>t1 ->a’, это совпадает с нашим исходным экземпляром.
Получается, что за счёт механизма частичного применения мы одним махом определили экземпляры Num
для функций любого числа аргументов, которые возвращают значение типа Num.
Итак функция f имеет вид:
\t1 t2 ->(t1 +t2) -(t1 *t2)
Подставим значения:
Функциональный калькулятор | 79
(\t1 t2 ->(t1 +t2) -(t1 *t2)) 1 2
(\t2 ->(1 +t2) -(1 *t2) 2
(1 +2) -(1 *2)
3 -2
1
Теперь давайте составим несколько выражений с обобщёнными функциями. Для этого добавим в модуль
FunNatдирективу импорта функций из модуля Data.Function. Также добавим несколько основных функций
для списков и класс Ord:
module FunNat where
import Prelude( Show( ..), Eq( ..), Ord( ..), Num( ..), error)
import Data.Function(id, const, ( .), ( $), flip, on)
import Prelude(map, foldr, filter, zip, zipWith)
...
и загрузим модуль в интерпретатор:
Prelude> :load FunNat
[1 of1] Compiling FunNat
( FunNat.hs, interpreted )
Ok, modules loaded : FunNat.
Составим функцию, которая принимает один аргумент, умножает его на два, вычитает 10 и берёт модуль
числа.
*FunNat> letf =abs $id *2 -10
*FunNat>f 2
6
*FunNat>f 10
10
Давайте посмотрим как была составлена эта функция:
abs $id *2 -10
=>
abs $(id *2) -10
-- приоритет умножения
=>
abs $(\x ->x *\x ->2) -10
-- развернём id и 2
=>
abs $(\x ->x *2) -10
-- по определению (*) для функций
=>
abs $(\x ->x *2) -\x ->10
-- развернём 10
=>
abs $\x ->(x *2) -10
-- по определению (-) для функций
=>
\x ->abs x .\x ->(x *2) -10
-- по определению abs для функций
=>
\x ->abs ((x *2) -10)
-- по определению (.)
=>
\x ->abs ((x *2) -10)
Функция возведения в квадрат:
*FunNat> letf =id *id
*FunNat>map f [1,2,3,4,5]
[1,4,9,16,25]
*FunNat>map (id *id -1) [1,2,3,4,5]
[0,3,8,15,24]
Обратите внимание на краткость записи. В этом выражении (id *id -1) проявляется основное пре-
имущество бесточечного стиля, избавившись от аргументов, мы можем пользоваться функциями так, словно
это простые значения. Этот приём используется в Haskell очень активно. Пока нам встретились лишь две
инфиксных операции для функций (это композиция и применение с низким приоритетом), но в будущем вы
столкнётесь с целым морем подобных операций. Все они служат одной цели, они прячут аргументы функции,
позволяя быстро составлять функции на лету из примитивов. Чтобы не захлебнуться в этом море помните,
что скорее всего новый символ означает либо композицию либо применение для функций специального
вида.
Возведём в четвёртую степень:
80 | Глава 5: Функции высшего порядка
*FunNat>map (f .f) [1,2,3,4,5]
[1,16,81,256,625]
Составим функцию двух аргументов, которая будет вычислять сумму квадратов двух аргументов:
*FunNat> letx =const id
*FunNat> lety =flip $const id
*FunNat> letd =x *x +y *y
*FunNat>d 1 2
5
*FunNat>d 3 2
13
Так мы составили функцию, ни прибегая к помощи аргументов. Эти выражения могут стать частью других
выражений:
*FunNat>filter
(( <10) .d 1) [1,2,3,4,5]
[1,2]
*FunNat>zipWith d [1,2,3] [3,2,1]
Читать дальше