53
Разработка приложений на языке F# Андрей Терехов Microsoft Украина

Разработка приложений на языке F#

  • Upload
    milica

  • View
    107

  • Download
    0

Embed Size (px)

DESCRIPTION

Разработка приложений на языке F#. Андрей Терехов Microsoft Украина. Немного истории. Подробнее про F#. FORTRAN. Lisp. Scheme. ML. Common Lisp. Hope. SML. …. Miranda. Caml. Haskell. OCaml. C# 3.0. F#. Сложность. Глобальные проблемы программирования. СЛОЖНОСТЬ - PowerPoint PPT Presentation

Citation preview

Page 1: Разработка  приложений  на  языке  F#

Разработка приложений на языке F#

Андрей ТереховMicrosoft Украина

Page 2: Разработка  приложений  на  языке  F#

Немного истории

Page 3: Разработка  приложений  на  языке  F#

Подробнее про F#

Lisp

Scheme

Common Lisp

ML

Hope SML

Miranda

Haskell

Caml

OCaml

F#C# 3.0

FORTRAN

Page 4: Разработка  приложений  на  языке  F#

Сложность

Page 5: Разработка  приложений  на  языке  F#

Глобальные проблемы программирования

• СЛОЖНОСТЬ- Сложность окружающего мира влияет на сложность

программных систем- Параллелизм – необходимость писать параллельный

код резко увеличивает сложность- Сложная система => большее количество ошибок,

резко возрастает стоимость качественного кода+ Сложность удается частично скрыть с помощью

инструментов разработки (пример: языки высокого уровня)+ Software Factories, Domain-Specific Languages

• Актуальность этих проблем быстро растет со временем

Page 6: Разработка  приложений  на  языке  F#

Как бороться со сложностью?

• Наследование в ООП• Перенос сложных функций (в т.ч. распараллеливание) на

систему программирования или runtime• Domain-Specific Languages, Software Factories• Декларативное программирование• Функциональная абстракция

Абстракция

• Структурное программирование (процедуры/функции, пошаговая детализация)

• Компонентный подход• Объектная декомпозиция• Функциональная декомпозиция

Декомпозиция

Page 7: Разработка  приложений  на  языке  F#

Подходы к параллельным вычислениям

Программирование «вручную»• Locks, Semaphores, …, MPI

.NET Parallel Extensions / .NET 4.0• Parallel.For, …• Применимо только к

независимым участкам кода

Декларативное программирование• Parallel LINQ

CCR (Concurrency Coordination Runtime)

Транзакционная память

Функциональный подход!

Page 8: Разработка  приложений  на  языке  F#

«Классическое программирование»

• Императивное – мы говорим компьютеру, как решать задачу (что делать)

• Основной акцент – манипулирование ячейками памяти– Оператор присваивания

• Функции как способ декомпозиции задачи на более простые

Page 9: Разработка  приложений  на  языке  F#

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

• Парадигма программирования, которая рассматривает выполнение программы как вычисление математических функций (выражений)– Неизменяемые данные, нет состояния среды– Функции – first-class citizen

• Стиль программирования, позволяющий писать программы, свободные от ошибок

• Языки программирования (F#, LISP, ML, Haskell, …)

Page 10: Разработка  приложений  на  языке  F#

Особенности ФП

• Отсутствие операторов присваивания и побочных эффектов

• Функции-как-данные – между функциями и данными не делается явного различия, в чистом ФП «все есть функция»

• Декларативное программирование• Высокая функциональная абстракция• Более короткий и выразительный код

– За счет автоматического вывода типов– За счет отсутствия операторов присваивания

• Прозрачная семантика, близость к математическому понятию функции– Возможность рассуждать о программах, доказывать их

свойства

Page 11: Разработка  приложений  на  языке  F#

Особенности ФП

• Функциональное программирование имеет очень четкую математическую основу– Рассуждение о программах: доказательство корректности,

…• Определение последовательности действий –

рекурсивно– При умелом программировании не ведет к падению

эффективности (компилятор сводит к итерации)• Встроенные структуры данных (tuples, списки,

discriminated unions) с компактным синтаксисом• Отсутствует оператор присваивания

– let имеет другую семантику – связывание имен– Будучи один раз связанным, имя не может менять свое

значение (в рамках области видимости)– А это значит – нет побочных эффектов!– Раз в императивной программе 90% - это операторы

присваивания, то функциональные программы на 90% короче!

Page 12: Разработка  приложений  на  языке  F#

Демо• Синтаксис• Связывание имен• Типизация• Применение

функций• Реализация циклов в

функциональном стиле

Знакомство с F#

Page 13: Разработка  приложений  на  языке  F#

Пример: вычисление числа π

S/A=Pi*R2/4/R2=H/M

public double area(double p){ var R = new Random(); int max = 10000; int hits = 0; for (var i = 0; i < max; i++) { var x = R.NextDouble() * p; var y = R.NextDouble() * p; if (x * x + y * y <= p * p) hits++; } return 4 * p * p * hits / max;}

Page 14: Разработка  приложений  на  языке  F#

Вычисление π на F#

let rand max n = Seq.generate (fun () -> new System.Random(n)) (fun r -> Some(r.NextDouble()*max)) (fun _ -> ());;

let MonteCarlo hit max iters = let hits = (float)( Seq.zip (rand max 1) (rand max 3) |> Seq.take iters |> Seq.filter hit |> Seq.length) in 4.0*max*max*hits/((float)iters);;

let area radius = MonteCarlo (fun (x,y) -> x*x+y*y<=radius*radius) radius 100000;;

let Pi = (area 10.0)/100.0;;

Page 15: Разработка  приложений  на  языке  F#

Декомпозиция

• Какие способы комбинирования функций доступны в традиционном программировании?function myexp(x:real):real;var s : real; i : integer;begin s:=0; for i:=0 to 10 do s:=s+taylor(x,i);end;

function taylor(x : real, i:integer):real;begin taylor:=power(x,i)/fact(i);end;

Вызов

Композиция

Page 16: Разработка  приложений  на  языке  F#

Функциональная декомпозиция

MyExp

Taylor

Pow Fact

Page 17: Разработка  приложений  на  языке  F#

Декомпозиция и абстракция в функциональном стиле

• Более богатые возможности композиции за счет рассмотрения «функций-как-данных»

• iter – функциональная абстракция, лежащая в основе вычисления myexp, pow, fact и др.

• iter – может быть получено как частный случай абстракцииlet iter f a b i = fold_left f i [a..b];;

let rec iter f a b i = if a>b then i else f (iter f (a+1) b i) a;; let pow x n = iter (fun y i -> y*x) 1 n 1.0;;let fact n = iter (fun y i -> y*(float i)) 1 n 1.0;;

let taylor x n = pow x n / fact n;;

let myexp x = iter (fun y n -> y+taylor x n) 0 15 0.0;;

f(f(f(…f(i,b),b-1)),…,a+1),a)

Page 18: Разработка  приложений  на  языке  F#

Функциональная декомпозиция

• При этом:– Технология мемоизации и ленивых вычислений могут

увеличить эффективность вычисления факториала и степени до линейной, за счет запоминания предыдущих результатов вычислений

– Функционально-декомпозированный (более простой для человека) алгоритм будет обладать такой же эффективностью, что и более сложный алгоритм вычисления суммы в одном цикле (домножение предыдущего слагаемого на фактор)

iter

fold_leftMyExp

Taylor

Pow Fact

Page 19: Разработка  приложений  на  языке  F#

Простота vs. эффективность

• Сравните:function sum_even(L:List):integer;var s : integer;begin s:=0; foreach (var x in L) do if x mod 2 = 0 then s:=s+x; sum_even:=s;end;

let sum_even L = sum(List.filter(fun x->x%2=0) L);;

Page 20: Разработка  приложений  на  языке  F#

Плюсы/минусы

• Императивный подходНа первый взгляд – большая эффективность по памяти (не создаются списки), по времени (один проход)Нет декомпозиции задачи, невозможно повторно использовать код

• Функциональный подходВысокий уровень абстракции -> решение для других фигур получается заменой функцииПроще для математика? Для программиста?Пусть компилятор заботится об эффективности!• Большая эффективность при параллельных

вычислениях (возможность распараллеливания, поскольку списковые функции не имеют зависимостей по данным)

• При использовании ленивых вычислений / LINQ – получаем однопроходный алгоритм, эквивалентный императивному!

Page 21: Разработка  приложений  на  языке  F#

Другой пример: сортировка Хоараvoid quickSort (int a[], int l, int r){ int i = l; int j = r; int x = a[(l + r) / 2]; do { while (a[i] < x) i++; while (x < a[j]) j--; if (i <= j) { int temp = a[i]; a[i++] = a[j]; a[j--] = temp; } } while (i <= j); if (l < j) quickSort (a, l, j); if (i < r) quickSort (a, i, r); }

let rec qsort = function   [] -> [] | h::t ->     qsort([for x in t do if x<=h then yield x]) @ [h] @ qsort([for x in t do if x>h then yield x]);;

Page 22: Разработка  приложений  на  языке  F#

Особенности функционального подхода

Множество способов комбинирования дает дополнительное преимущество в борьбе со сложностью Можно эксплуатировать как декомпозицию, так и

функциональную абстракцию Отсутствие побочных эффектов резко снижает

затраты на тестирование и отладку Декларативный стиль перекладывает

существенную часть решения на компилятор (пример: суммирование четных элементов списка)

Функциональный код явно описывает зависимости по данным, позволяя более эффективно распараллеливать код

Функциональный подход приводит к более компактному коду, но требует больших размышлений и специальных навыков

Page 23: Разработка  приложений  на  языке  F#

Множество Мандельброта

Page 24: Разработка  приложений  на  языке  F#

Определение

• zn+1(c)= zn2(c)+c, z0(c)=0; zC

• M = { c C | lim zn(c)<∞}

• M’= { c C | |z20(0)|<1 }

Page 25: Разработка  приложений  на  языке  F#

Реализация на F#

let mandelf (c:Complex) (z:Complex) = z*z+c;;

let ismandel c = Complex.Abs(rpt (mandelf c) 20 Complex.zero)<1.0;;

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 Write(if ismandel t then "*" else " "); WriteLine("");;

Page 26: Разработка  приложений  на  языке  F#

WinForms

#lightopen System.Drawingopen System.Windows.Forms

let form = let image = new Bitmap(400, 400) let lscale = scale (-1.0,1.0) (0,400) 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);;

Page 27: Разработка  приложений  на  языке  F#

Вычисления «по требованию»

• По умолчанию – энергичная стратегия вычислений

• Lazy / Force• Вычисления по необходимостиopen System.IOlet rec allFiles(dir) = seq { for file in Directory.GetFiles(dir) do yield file for sub in Directory.GetDirectories(dir) do yield! allFiles(sub) }

allFiles(@"C:\WINDOWS") |> Seq.take 100 |> show

Page 28: Разработка  приложений  на  языке  F#

F# - это:

Мультипарадигмальный язык•Функционально-императивный•С акцентом на функциональном программировании

Компактный код•Автоматический вывод типов – при статической типизации!•Встроенные структуры данных, своя библиотека обработки

Интероперабельность с .NET•Все возможности .NET Framework•Двухсторонняя интероперабельность с пользовательским кодом

Эффективный язык•Статическая типизация•Оптимизаторы порождают качественный .NET-код (оптимизация хвостовой рекурсии)•Ленивые вычисления

Page 29: Разработка  приложений  на  языке  F#

C# 3.0 – императивно-функциональный язык!

•var s = new int[] {1, 2, 3};Вывод типов

•var x = new { Name=“Вася”, Age=30 };Анонимные типы (tuples)

•var double = x => x*2;Функциональные константы

•Func<List<int>,int> sum = X => X.Aggregate((x,y)=>(x+y), 0);

Функции высших порядков

•Expression<Predicate<Student>> test = s => s.Group==806;

Expression Trees (метапрограммир.)

Page 30: Разработка  приложений  на  языке  F#

LINQ

• Технология Language Integrated Query представляет собой трансляцию SQL-подобного синтаксиса в выражение в функциональном стиле

• Выражение представляет собой отложенное вычисление / преобразование функциональных вызовов к синтаксису источника данных

• Идеи из ФП: ленивые вычисления, мета-программирование

var res = from x in L where x.Age>16 orderby x.Age select x.Name;

var res = L.Where(x => x.Age>16).OrderyBy(x=>x.Age).Select(x => x.Name);

Page 31: Разработка  приложений  на  языке  F#

ФУНКЦИОНАЛЬНОЕ ПРОГРАММИРОВАНИЕ И ПАРАЛЛЕЛЬНЫЕ ВЫЧИСЛЕНИЯ

Page 32: Разработка  приложений  на  языке  F#

Параллельные вычисления

Page 33: Разработка  приложений  на  языке  F#

Основные проблемы асинхронных и параллельных вычислений

Общая память

Инверсия управленияПараллелизм ввода-выводаМасштабирование

Page 34: Разработка  приложений  на  языке  F#

Общая память

Общая память

Инверсия управленияПараллелизм ввода-выводаМасштабирование

o Сложность поддержки и тестированияo Сложно распараллеливать!o Фундаментальные проблемы locking:

o Явная разработка параллельного кодаo Всем модулям системы надо учитывать

параллельность

Page 35: Разработка  приложений  на  языке  F#

Asynchronous Workflows

let task1 = async { return 10+10 }let task2 = async { return 20+20 }Async.Parallel [ task1; task2 ];;

let map' func items = let tasks = seq { for i in items -> async { return (func i) } } Async.RunSynchronously (Async.Parallel tasks);;

let rec fib n = if n < 2 then 1 else fib (n-2) + fib(n-1);;

List.map (fun x -> fib(x)) [1..30];;map' (fun x -> fib(x)) [1..30];;

Page 36: Разработка  приложений  на  языке  F#

Инверсия управления

Общая памятьИнверсия управления

Параллелизм ввода-выводаМасштабирование

o Привычка писать последовательный кодo Асинхронное программирование ведёт к

разделению начала и конца действияo Сложность

o Совмещение нескольких асинхр.операцийo Исключения и отмена

Page 37: Разработка  приложений  на  языке  F#

using System;using System.IO;using System.Threading; public class BulkImageProcAsync{    public const String ImageBaseName = "tmpImage-";    public const int numImages = 200;    public const int numPixels = 512 * 512;     // ProcessImage has a simple O(N) loop, and you can vary the number    // of times you repeat that loop to make the application more CPU-    // bound or more IO-bound.    public static int processImageRepeats = 20;     // Threads must decrement NumImagesToFinish, and protect    // their access to it through a mutex.    public static int NumImagesToFinish = numImages;    public static Object[] NumImagesMutex = new Object[0];    // WaitObject is signalled when all image processing is done.    public static Object[] WaitObject = new Object[0];    public class ImageStateObject    {        public byte[] pixels;        public int imageNum;        public FileStream fs;    }  

    public static void ReadInImageCallback(IAsyncResult asyncResult)    {        ImageStateObject state = (ImageStateObject)asyncResult.AsyncState;        Stream stream = state.fs;        int bytesRead = stream.EndRead(asyncResult);        if (bytesRead != numPixels)            throw new Exception(String.Format                ("In ReadInImageCallback, got the wrong number of " +                "bytes from the image: {0}.", bytesRead));        ProcessImage(state.pixels, state.imageNum);        stream.Close();         // Now write out the image.          // Using asynchronous I/O here appears not to be best practice.        // It ends up swamping the threadpool, because the threadpool        // threads are blocked on I/O requests that were just queued to        // the threadpool.         FileStream fs = new FileStream(ImageBaseName + state.imageNum +            ".done", FileMode.Create, FileAccess.Write, FileShare.None,            4096, false);        fs.Write(state.pixels, 0, numPixels);        fs.Close();         // This application model uses too much memory.        // Releasing memory as soon as possible is a good idea,         // especially global state.        state.pixels = null;        fs = null;        // Record that an image is finished now.        lock (NumImagesMutex)        {            NumImagesToFinish--;            if (NumImagesToFinish == 0)            {                Monitor.Enter(WaitObject);                Monitor.Pulse(WaitObject);                Monitor.Exit(WaitObject);            }        }    }

        public static void ProcessImagesInBulk()    {        Console.WriteLine("Processing images...  ");        long t0 = Environment.TickCount;        NumImagesToFinish = numImages;        AsyncCallback readImageCallback = new            AsyncCallback(ReadInImageCallback);        for (int i = 0; i < numImages; i++)        {            ImageStateObject state = new ImageStateObject();            state.pixels = new byte[numPixels];            state.imageNum = i;            // Very large items are read only once, so you can make the             // buffer on the FileStream very small to save memory.            FileStream fs = new FileStream(ImageBaseName + i + ".tmp",                FileMode.Open, FileAccess.Read, FileShare.Read, 1, true);            state.fs = fs;            fs.BeginRead(state.pixels, 0, numPixels, readImageCallback,                state);        }         // Determine whether all images are done being processed.          // If not, block until all are finished.        bool mustBlock = false;        lock (NumImagesMutex)        {            if (NumImagesToFinish > 0)                mustBlock = true;        }        if (mustBlock)        {            Console.WriteLine("All worker threads are queued. " +                " Blocking until they complete. numLeft: {0}",                NumImagesToFinish);            Monitor.Enter(WaitObject);            Monitor.Wait(WaitObject);            Monitor.Exit(WaitObject);        }        long t1 = Environment.TickCount;        Console.WriteLine("Total time processing images: {0}ms",            (t1 - t0));    }}

let ProcessImageAsync () = async { let inStream = File.OpenRead(sprintf "Image%d.tmp" i) let! pixels = inStream.ReadAsync(numPixels) let pixels' = TransformImage(pixels,i) let outStream = File.OpenWrite(sprintf "Image%d.done" i) do! outStream.WriteAsync(pixels') do Console.WriteLine "done!" } let ProcessImagesAsyncWorkflow() = Async.Parallel [ for i in 1 .. numImages -> ProcessImageAsync i ] 

Обработка изображений

Page 38: Разработка  приложений  на  языке  F#

Asynchronous Workflowsopen System.Netopen Microsoft.FSharp.Control.WebExtensions

let urlList = [ "Microsoft.com", "http://www.microsoft.com/" "MSDN", "http://msdn.microsoft.com/" "Bing", "http://www.bing.com" ]

let fetchAsync(name, url:string) = async { try let uri = new System.Uri(url) let webClient = new WebClient() let! html = webClient.AsyncDownloadString(uri) printfn "Read %d characters for %s" html.Length name with | ex -> printfn "%s" (ex.Message); }

let runAll() = urlList |> Seq.map fetchAsync |> Async.Parallel |> Async.RunSynchronously |> ignore

runAll()

Page 39: Разработка  приложений  на  языке  F#

Параллелизм ввода-вывода

Общая памятьИнверсия управленияПараллелизм ввода-вывода

Масштабирование

o Ввод-вывод часто становится узким местомo Веб-сервисыo Данные на диске

o Ресурсы ввода-вывода естественным образом параллельныo Большие возможности для ускорения

Page 40: Разработка  приложений  на  языке  F#

Масштабирование

Общая памятьИнверсия управленияПараллелизм ввода-выводаМасштабированиеo На несколько компьютеровo Многокомпьютерные ресурсы

o Собственные кластерыo Облачные вычисления / Windows Azure

o Ноo Общая память не масштабируется

Page 41: Разработка  приложений  на  языке  F#

Recap: Some Concurrency Challenges

Общая памятьИнверсия управленияПараллелизм в/вМасштабирование

immutabilityasync { … }async { … }agents

Page 42: Разработка  приложений  на  языке  F#

ПЕРСПЕКТИВЫ ПРИМЕНЕНИЯ ФП

Page 43: Разработка  приложений  на  языке  F#

Где сейчас используется ФП?

• Mainstream языки программирования:– F#– C# 3.0, следующий стандарт C++ – Java.next (Clojure, Groovy, JRuby, Scala)– LINQ– XSLT

• Excel Spreadsheets

Page 44: Разработка  приложений  на  языке  F#

ФП в реальных проектах

• emacs (диалект LISP’а)• HeVeA – LaTeX to HTML конвертер

(Objective Caml)• Genome Assembly Viewer (F#, 500 строк)• ПО для телефонных станций Ericsson

(Erlang)• Проекты в рамках Microsoft и MSR– F# Compiler– Driver code verification– AdCenter Challenge

Page 45: Разработка  приложений  на  языке  F#

• Наиболее прибыльная часть поиска

• Продажа «веб-пространства» на www.live.com и www.msn.com.

• “Оплаченные ссылки” (цены выставляются по аукциону)

• Внутреннее соревнование с упором на оплаченные ссылки

The adCenter Challenge

Page 46: Разработка  приложений  на  языке  F#

Внутреннее соревнование

4 месяца на программирование1 месяц на обучениеЗадача:

На основе обучающих данных за несколько недель (просмотры страниц) предсказывать вероятность перехода по ссылке

Ресурсы: 4 (2 x 2) 64-bit CPU machine 16 Гб ОП 200 Гб НЖМД

Page 47: Разработка  приложений  на  языке  F#

Масштаб проблемы

• Объем входных данных7,000,000,000 записей, 6

терабайт• Время ЦП на обучение:

2 недели × 7 дней × 86,400 сек/день =

1,209,600 секунд• Требования к алгоритму

обучения:– 5,787 записей / сек– 172.8 μs на одну запись

Page 48: Разработка  приложений  на  языке  F#

Решение

• 4 недели кодирования, 4 эксперта в области Machine Learning

• 100 миллионов вероятностных переменных

• Обработано 6 терабайт обучающих данных

• Обработка в реальном времени!

Page 49: Разработка  приложений  на  языке  F#

Наблюдения

Быстрое кодирование•Вывод типов – меньше печатать, больше думать

Agile-стиль•Думаем в терминах предметной области, не языка

Скриптинг•Интерактивное «исследование» данных и тестирование алгоритмов•Совместно с Excel

Производительность•Немедленное масштабирование на огромные массивы данных

Экономный расход памяти•Огромные структуры данных на 16 Гб

Выразительный синтаксис•Краткий код позволяет легко осуществлять рефакторинг и реиспользование

Символьная обработка•Метапрограммирование

Интеграция с .NET•В том числе Excel, SQL Server

Page 50: Разработка  приложений  на  языке  F#

Какие задачи хорошо решаются на функциональных языках?

• Обработка данных– Синтаксический разбор– Компиляторы, преобразования программ– Data Mining– Биоинформатика

• Вычислительные задачи• Параллельные задачи• Традиционное мнение: сложно строить UI– Смотрим пример!

Page 51: Разработка  приложений  на  языке  F#

Источники

Page 52: Разработка  приложений  на  языке  F#

Источники• Филд А., Харрисон П. Функциональное

программирование, М.: Мир, 1993• J.Ullman, Elements of ML Programming, 2nd edition,

Prentice Hall, 1998• Thompson S. Haskell: The Craft of Functional

Programming, 2nd edition, Addison-Wesley, 1999• R.Pickering, Foundations of F#, A-Press, 2008• D.Syme, A.Granicz, A.Cisternio. Expert F#. A-Press,

2008• J.Harrop, F# for Scientists, Wiley, 2008• Д.В. Сошников «Функциональное

программирование», видеокурс: http://www.intuit.ru/department/pl/funcprog

• http://www.codeplex.com/fsharpsamples

Page 53: Разработка  приложений  на  языке  F#

Андрей ТереховДиректор департамента стратегических технологийMicrosoft Украина[email protected]

Вопросы?