140 likes | 338 Views
Функциональное программирование. Факультет инноваций и высоких технологий Московский физико-технический институт. Лекция 1 9. Последовательности и ленивые вычисления в F#. Мемоизация. F# Sequences. F# предоставляет механизм для реализации таких вычислений – sequences.
E N D
Функциональное программирование Факультет инноваций и высоких технологий Московский физико-технический институт
Лекция 19 Последовательности и ленивые вычисления в F#. Мемоизация.
F# Sequences • F# предоставляет механизм для реализации таких вычислений – sequences let fibs = Seq.unfold (fun (u,v) -> Some(u,(u+v,u))) (1,1);; Seq.take 10 fibs;; fibs;; let squares = Seq.init_infinite(fun n -> n*n);; let squares10 = Seq.init_finite 10 (fun n -> n*n);; • Seqне кеширует результаты вычислений(аналог IEnumerable) • LazyList – аналогичен Seq, но кеширует результаты
Пример: вычисление Pi 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; }
Пример: Метод Монте-Карло 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;;
Пример: чтение из файла #light open System.IO let ReadLines fn = seq { use inp = File.OpenText fn in while not(inp.EndOfStream) do yield (inp.ReadLine()) };; let table = ReadLines "csvsample.txt" |> Seq.map (fun s -> s.Split([|','|]));; let nstuds = table |> Seq.fold (fun x l -> if l.[0]="Y" then x+1 else x) 0;;
Частотный словарь let FreqDict S = Seq.fold ( fun (ht:Map<_,int>) v -> if Map.mem v ht then Map.add v ((Map.find v ht)+1) ht else Map.add v 1 ht) (Map.empty) S;; ReadLines "prince.txt" |> Seq.map_concat (fun s -> s.Split([|',';' ';':';'!';'.'|])) |> FreqDict |> Map.to_list |> List.sort(fun (k1,v1) (k2,v2) -> -compare v1 v2) |> List.filter(fun (k,v) -> k.Length>3) |> Seq.take 10;;
Ленивые вычисления • F# - язык с энергичной стратегией вычислений • Ленивость реализуется с помощью • Seq / LazyList • lazy / force • let expr = lazy expr – формирует значение типа Lazy<‘a> • force : Lazy<T> → T – форсирует вычисление значения
Ленивый факториал let rec lazy_fact n = let _ = printfn "Apply %d" n in match n with 1 -> lazy 1 | n -> lazy (n*Lazy.force (lazy_fact(n-1)));; let rec lazy_fact n = lazy( let _ = printfn "Apply %d" n in match n with 1 -> 1 | n -> n*lazy_fact(n-1).Force());; > let f = lazy_fact 5;; val f : Lazy<int> Apply 5 > f;; val it : Lazy<int> = {status = Delayed;} > Lazy.force f;; Apply 4 Apply 3 Apply 2 Apply 1 val it : int = 120 > f;; val it : Lazy<int> = {status = Value;}
Последовательности и ленивые вычисления • Ленивая последовательность типа Sequence может быть смоделирована на энергичном языке с использованием примитивов lazy и force. type 'a SeqCell = Nil | Cons of 'a * 'a Stream and 'a Stream = Lazy<'a SeqCell>;; let sample1 = lazy(Cons(1,lazy(Cons(2,lazy(Nil)))));; let sample2 = lazy(Cons(3,lazy(Cons(4,lazy(Nil)))));; let rec concat s t = lazy( match Lazy.force s with Nil -> Lazy.force t | Cons(x,y) -> Cons(x,concat y t));;
Мемоизация • Техника явной мемоизации может быть введена при энергичных вычислениях let rec fib n = if n<2 then 1 else fib (n-1) + fib(n-2);; open System.Collections.Generic let mfib = let d = new Dictionary<int,int>() let rec fib n = if d.ContainsKey(n) then d.[n] else let res = if n<2 then 1 else fib (n-1) + fib(n-2) d.Add(n,res) res fun n -> fib n;; > time (fun() -> fib 20);; Execution took 00:00:00.4000 > time (fun() -> mfib 20);; Execution took 00:00:00.0020
Генерализация мемоизации • В ленивых языках с автоматической мемоизацией рекурсивное вычисление чисел Фибоначчи приобретает линейную сложность! let memoize (f: 'a -> 'b) = let t = newDictionary<'a,'b>() fun n -> if t.ContainsKey(n) then t.[n] else let res = f n t.Add(n,res) res;; let rec fibFast = memoize ( fun n -> if n < 2 then 1 else fibFast(n-1) + fibFast(n-2));;
Мораль • Чистое лямбда-исчисление может рассматриваться как язык программирования • Оно определяет некоторый счетный класс вычислимых функций • После нескольких синтаксических улучшений язык становится почти удобным • В функциональном программировании для хранения данных и частичных вычислений важно понятие замыкания: функции + контекста на момент ее определения • Ленивые вычисления – позволяют работать с бесконечными структурами данных + делают некоторые алгоритмы более эффективными