Функциональное программирование Лямбда-исчисление Черча. Аппликация и абстракция. Бэта-редукция. Нетипизированное и типизированное лямбда-исчисление. Функции высшего порядка в функциональных языках программирования, 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#