Функциональное программирование

advertisement
Функциональное
программирование
Лямбда-исчисление Черча. Аппликация и
абстракция. Бэта-редукция. Нетипизированное и
типизированное лямбда-исчисление. Функции
высшего порядка в функциональных языках
программирования, Currying.
Лямбда-исчисление
• Система формализации и анализа
вычислимости
• Разработана в начале 30-х годов
американским математиком Алонзо Чёрчем
• Может быть рассмотрено как семейство
прототипных языков программирования
• Обеспечивает систематический подход к
исследованию операторов, аргументами
которых могут быть другие операторы, а
значением может быть также оператор
Лямбда-исчисление
•
•
•
•
F(x)=2x+3
^x. 2x+1
let f x = x + 1
Впервые реализовано Джоном Маккарти в
языке Lisp
• На данный момент имеет аппаратную
реализацию
• Обладает полнотой по Тьюрингу
Аппликация
• Применение функции по отношению к
заданному значению
• Fa
• F – функция, под которой в данном случае
понимается алгоритм
• а - значение
Абстракция
• Построение функций по заданным
выражениям
• t[x] – выражение, свободно содержащее x
• ^x.t[x] – функция от х
Бэта-редукция
• Подстановка терма в выражение
• (^x.2*x+1) 3
• (^x.t) a = t[x=a]
Каррирование (currying)
• ^x.^y.x+y
• Преобразование функции от пары
аргументов в функцию, берущую свои
аргументы по одному
Типизированное
Лямбда-исчисление
• это типовый формализм, использующий
символ абстракции «лямбда» для записи
выражений, обозначающих безымянные
функции.
Безтиповое Лямбда-исчисление
• Термы лямбда-исчисления действуют как
функции, применимые к самим же термам
лямбда-исчисления
• Множество D, в которое вкладывается
пространство функций D->D
• Это сложность, которую преодолел Д.С. Скотт,
построив понятие области D (полного частично
упорядоченного множества со специальной
топологией)
Функции высшего порядка
• Функция, принимающая в качестве
аргументов другие функции или
возвращающая функцию в качестве
результата
• В функциональных языках
программирования все функции являются
функциями высшего порядка.
Сравнение подходов
F#
let rec map func lst =
match lst with
| [] -> []
| head :: tail -> func head :: map func tail
let myList = [1;3;5]
let newList = map (fun x -> x + 1) myList
F# каррирование
•
let add a b = a + b //'a -> 'a -> 'a
let addFour = add 4 //'a -> 'a
F# каррирование
• Однако все функции из .NET не обладают
свойством каррируемости, и для их
применения в F# используются кортежи —
наборы нескольких разнотипных значений.
• Кортеж может содержать множество
различных параметров внутри себя, однако
рассматривается F# как один параметр, и как
следствие применяется только целиком.
• Записываются кортежи в круглых скобках,
через запятую.
F# каррирование
•
let add (a,b) = a + b
let addFour = add 4
• Не правильно
• Однако следует помнить, что при разработке
собственных функций, особенно тех, которые
будут использоваться другими
программистами, следует по возможности
делать их каррируемыми, так как они
очевидно обладают большей гибкостью в
использовании.
F# промежуточные вычисления
•
let midValue a b =
let dif = b - a
let mid = dif / 2
mid + a
• Отделяются пробелами
F# область видимости
•
let midValue a b =
let k = b - a
let k = k / 2
k+a
• Видимость только в пределах блока, но
переменная может быть переопределена
• let changingType () =
let k = 1
let k = "string"
F#
• Язык сильнотипизированный, но не требуется
указывать тип переменной самому. Тем не
менее это можно сделать
•
let square x = x*x
тип 'a -> 'a, где 'a может быть int, float, и
вообще говоря любым, для которого
перегружен оператор *.
•
let parent (x:XmlNode) = x.ParentNode
F# списки
• Простейший способ — определение
промежутка, который задается с
использованием (..), например:
let lst = [1 .. 10]
let seq = {'a'..'z'}
F# списки
• Также с помощью добавления еще одного
(..) можно задавать шаг выбора в
промежутке:
let lst = [1 .. 2 .. 10] // [1, 3, 5, 7, 9]
F# списки
• Кроме того, при создании списков можно
использовать циклы (циклы могут быть как
одинарными, так и вложенными в любой
степени)
let lst = [for i in 1..10 -> i*i] // [1, 4, 9,..]
F#
F#
• Математически, множество Мандельброта
определяется следующим образом.
Рассмотрим последовательность комплексных
чисел z(n+1) = z(n)^2+c, z(0)=0. Для различных c
эта последовательность либо сходится, либо
расходится Например, для c=0 z(i) = 0, а для
c=2 имеем расходящуюся последовательность.
Так вот, множество Мандельброта — это
множество тех c, для которых
последовательность сходится.
F#
• определим исходную функцию
let mandelf (c:Complex) (z:Complex) = z*z+c;;
• Для того, чтобы тип Complex стал доступен, в
начале придется указать преамбулу:
open Microsoft.FSharp.Math;;
open System;
F#
• Будем считать, что последователность
сходится, если |z(20)|<1.
• Для вычисления z(20) используем следующую
функцию многократной композиции,
рекурсивно применяющую указанную
функцию заданное число раз:
let rec rpt n f =
if n=0 then (fun x->x)
else f >> (rpt (n-1) f);;
F#
• Здесь знак >> обозначает композицию функций, а
запись fun x->x — тождественную функцию. Вообще
говоря, конструкция fun x -> ... позволяет нам задать
функциональную константу, т.е. выражение типа
функции от одного аргумента. Например,
следующие две конструкции — эквивалентны:
• let double x = x*2
• let double = fun x -> x*2
F#
• В нашем случае функция rpt принимает
один аргумент целого типа n (число
повторений), и некоторую функцию f, т.е.
имеет тип rpt: int -> ('a -> 'a) -> ('a -> 'a). Для
n=0 возвращается тождественная функция,
а для n>0 — композиция f и (n-1)-кратного
применения f. Запись 'a означает
полиморфный тип, т.е. вместо него может
быть любой допустимый тип данных.
F#
• rpt 20 (mandelf c) (Complex.zero).
• 20-кратное применение функции mandelf с,
которое затем мы применяем к 0
• let ismandel c = Complex.Abs(rpt 20 (mandelf
c) (Complex.zero))<1.0;;
F#
let scale (x:float,y:float) (u,v) n = float(n-u)/float(v-u)*(y-x)+x;;
for i = 1 to 60 do
for j = 1 to 60 do
let lscale = scale (-1.2,1.2) (1,60) in
let t = complex (lscale j) (lscale i) in
System.Console.Write(if ismandel t then "*" else " ");
System.Console.WriteLine("") ;;
F#
#light
open System.Drawing
open System.Windows.Forms
let form = let image = new Bitmap(400, 400)
let lscale = scale (-1.2,1.2) (0,image.Height-1)
for i = 0 to (image.Height-1) do
for j = 0 to (image.Width-1) do
let t = complex (lscale i) (lscale j) in
image.SetPixel(i,j,if ismandel t then Color.Black else Color.White)
let temp = new Form()
temp.Paint.Add(fun e -> e.Graphics.DrawImage(image, 0, 0))
temp
[<STAThread>]
do Application.Run(form);;
F#
Download