Jump to content

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

Оцени ову тему


Juanito

Препоручена порука

Има потребе за тим у пракси често. На пример, имаш алгоритам за сортирање коме прослеђујеш функцију која пореди елементе зависно од њиховог типа.

 

Мада је фукнционално програмирање пуно више од тога, читава парадигма.

Da sad sam se setio, mislim koristio sam takve funkcije nisam ih pravio i baš za  sortiranje.

Link to comment
Подели на овим сајтовима

Кад смо код сортирања, ево како се оно може одрадити у Swift-u.

let sortedNumbers = [4, 1, 5, 3, 2].sorted(<)

Како ово ради? Ако погледамо декларацију 'sorted'

func sorted(isOrderedBefore: (T, T) -> Bool) -> [T]

видимо да тај метод прима функцију која узима два елемента датог типа и враћа да ли први долази после другог. А оператор '<' je баш таква функција.

func <(lhs: Int, rhs: Int) -> Bool
Link to comment
Подели на овим сајтовима

Сјајна ти је тема, само настави!

 

Можда би LISP био једноставнији за почетнике, али своди се на исто.

 

Ал' ја не знам LISP. :)

 

Добра страна F# и Swift-a је што можеш да их користиш на платформама које користе милиони људи.

Link to comment
Подели на овим сајтовима

Честа операција над листама (низовима) је да некако трансформишемо сваки елемент. Ако размишљамо процедуранлно, то значи да треба да, користећи неку петљу, прођемо кроз низ и применимо функцију на сваки елемент. Ако размишљамо функционално, то значи да треба функцију да убацимо у низ да одради свој посао. Функција 'map' ради баш то - узима низ и функцију коју треба позвати за сваки елемент и враћа ”обрађени” низ.

 

Swift:

let squareRoots = map([1.0, 2,0, 3,0], sqrt)

// или

let squareRoots = [1.0, 2,0, 3,0].map(sqrt)

F#

let squareRoots = List.map sqrt [1.0; 2.0; 3.0]

// или

let squareRoots = [1.0; 2.0; 3.0] |> List.map sqrt

Као што рекох на почетку, низ овде није ништа специјално. Постоји читава класа типова на којима се може дефинисати 'map'  и сличне функције (на пример 'filter'). Један од таквих типова је Event. Парадигма у којој се догађајима (је л' се тако преводи Event?) приступа на овакав начин (реактивно програмирање) је много јака ствар... На пример примамо кликове мишем, филтрирамо само оне који су погодили лоптице, мапирамо боју лоптице у бодове и онда реагујемo исписујући нешто на екрану, на основу крајњег резултата претходних операција. Све у пар линија кода, на једном месту, без непотребне спрегнутости свега и свачега, лако за читање, лако за одржавање, лако за мењање, лако за тестирање.

Link to comment
Подели на овим сајтовима

Све у пар линија кода, на једном месту, без непотребне спрегнутости свега и свачега, лако за читање, лако за одржавање, лако за мењање, лако за тестирање.

Ja sam mozda staromodan, ali meni je citljivije kad ide iteracija kroz listu. A u sustini ispod haube se desava isto. S tim da se ovo ne moze debugovati, ako je recimo funkcija transformacije nesto komplikovanija. Zato mi je bilo nezgodno kad su u timu poceli forsirati LINQ, pa ja kad treba nesto da debugujem vratim na klasiku kod sebe lokalno.

Link to comment
Подели на овим сајтовима

Zato mi je bilo nezgodno kad su u timu poceli forsirati LINQ, pa ja kad treba nesto da debugujem vratim na klasiku kod sebe lokalno.

 

Па ето, једна од мана ФП је теже дебаговање. Та мана је често у сенци чињенице да је у многим функционалним језицима теже написати погрешан код, јер имају софтистициранији систем типова, који омогућава компајлеру да многе грешке одмах уочи и побуни се. Ово сам заиста приметио у пракси. У неким ситуацијама можеш да будеш скоро сигуран да ти је код исправан, само зато што је компајлиран, чак и ако га не разумеш у потпуности. Не кажем да је ово добро, само констатујем. :)

 

Друга мана је то што функционални код често има слабије перформансе. И LINQ понекад пати од тога, колико ми се чини. Као што је теже написати погрешан фунционални код, тако је лакше написати спорији. Али ово је битно само када су спорије перформансе заиста видљиве у пракси. Тада се критични делови кода могу написати процедурално и сакрити иза фунционалног API.

Link to comment
Подели на овим сајтовима

Da, bilo je diskusija o tome da je npr. LINQ sporiji od klasicne petlje

 

http://www.alexyork.net/blog/2008/09/14/performance-of-foreach-loops-vs-linq/

 

Ali takodje rekose da ce u novijim verzijama biti poboljsane performanse. Ali ovo nije striktno tema, pa da ne gusim.

Link to comment
Подели на овим сајтовима

Свака парадигма има своју примену. Процедурално/објектно програмирање је применљиво на најшири скуп свакодневних проблема, функционално, логичко итд. имају своје предности на одређеним скуповима проблема. Зато је добро имати заједнички "фрејмворк" на коме постоји могућност коришћења различитих парадигми као што је .NET.

 

SQL већим делом исто спада у класу "декларативних програмских језика", а манипулација подацима у савременим RDMBS је незамислива без њега.

  • Волим 1
Link to comment
Подели на овим сајтовима

Функција 'map' узима генеричку функцију 'a -> 'b и листу од 'a и враћа листу од 'b.

List.map;;
val it : (('a -> 'b) -> 'a list -> 'b list) = <fun:[email protected]>

Поред List.map, F# дефинише Array.map, Seq.map, Option.map итд. Option је тип о коме ћу касније писати и који је потпуно различит од низова и листа, па опет има функцију map истог потписа:

> Option.map;;
val it : (('a -> 'b) -> 'a option -> 'b option) = <fun:[email protected]>

Шта мислите, да ли је могуће написати генерички IMappable индетрфејс, који декларише функцију map и тако генералисати све ове типове преко којих се може мапирати? :)

Link to comment
Подели на овим сајтовима

Док неки .NETлија не одговори ово за IMappable, ево како се функције, као типови првог реда, могу користити за енкапсулацију.

 

Swift:

let stamp: () -> Int = {
    var count = 0
    return { ++count }
}()

F#:

let stamp =
  let count = ref 0
  (fun () -> count := !count + 1 ; !count)

Тест:

stamp() // 1
stamp() // 2
stamp() // 3

Ово је понекад можда боље и једноставније од дефинисања потпуно нових типова за једнократну употребу.

 

stamp је вредност типа closure () -> Int, који ”хвата” променљиву count и сваки пут је враћа увећану за 1. Ово ref и ! су тренутно неопходни у F# коду, јер closure мора да копира count у heap, како би та променљива надживела scope у коме је дифинисана. У F# 4.0 ће компајлер те ствари аутоматски одрађивати.

Link to comment
Подели на овим сајтовима

Функција 'map' узима генеричку функцију 'a -> 'b и листу од 'a и враћа листу од 'b.

List.map;;
val it : (('a -> 'b) -> 'a list -> 'b list) = <fun:[email protected]>

Поред List.map, F# дефинише Array.map, Seq.map, Option.map итд. Option је тип о коме ћу касније писати и који је потпуно различит од низова и листа, па опет има функцију map истог потписа:

> Option.map;;
val it : (('a -> 'b) -> 'a option -> 'b option) = <fun:[email protected]>

Шта мислите, да ли је могуће написати генерички IMappable индетрфејс, који декларише функцију map и тако генералисати све ове типове преко којих се може мапирати? :)

Nisam nesto puno radio sa .NET, ali mislim da sa LINQ mozes uraditi nesto ovako bez potrebe da definises interface

 

var numbers = new List<double> { 1, 2, 3 };
var output = numbers.Select(d => Math.Pow(d, 2));
foreach (var d in output)
{
    System.Console.WriteLine(d);
}

Ako si na ovo mislio? Umesto Math.Pow moze biti bilo sta i da vraca bilo koji tip. Ne mora da bude nista striktno.

Link to comment
Подели на овим сајтовима

Ево шта хоћу да кажем:

 

Select, које је ништа друго него map, је дефинисано тако да узима IEnumerable<TSource>, Func<TSource, TResult> и враћа IEnumerable<TResult>.  И то ради за све што је IEnumerable, наравно. Али имамо, рецимо, Option<'T> у F#, које није IEnumerable, нема никакве везе са секвенцама и колекцијама, али дефинише map аналогног потписа:

public static IEnumerable<TResult> Select<TSource, TResult>(
    this IEnumerable<TSource> source,
    Func<TSource, TResult> selector
)

map : ('T -> 'U) -> Option<'T> -> Option<'U>

Да ли је ово могуће генерализовати преко генеричког интерфејса, тако да би и Option и List и ко зна шта још били IMappable? Тај интерфејс би морао да декларише метод  Map или Select, зови га како хоћеш, са следећим ограничењима: тип SomeType који имплементира Мap мора да буде генерички, са тачно једним генеричким параметром Т; Мap узима конкретно SomeType<T> и функцију f: T -> U и враћа SomeType<U>. Дакле то SomeType је исти тип и пре и после мапирања, само са различитом вредношћу генеричког параметра.

 

Овако нешто прво пада на памет:

interface IMappable<T>
{
    IMappable<U> Мap<U>(Func<T, U> mappingFunction);
} 

Али ово није оно што желимо. Рецимо, ако овај метод имплементирамо у List, нема ничег што би нас спречило да вратимо неко Array<U>, све док је и тo Array IMappable. Оно што бисмо желели да постигнемо је следеће: ако List имплементира IMappable, онда Μap метод мора врати List<U>, а не било које IMappable<U>. Да ли .NET generics могу ово да изразе?

Link to comment
Подели на овим сајтовима

Ако сам добро разумео, треба ти ово: http://msdn.microsoft.com/en-us/library/d5x73970.aspx

 

Значи, било би:

interface IMappable<T> where U : T
{
    IMappable<U> Мap<U>(Func<T, U> mappingFunction);
} 

Ваљда... :.mislise.

Link to comment
Подели на овим сајтовима

Придружите се разговору

Можете одговорити сада, а касније да се региструјете на Поуке.орг Ако имате налог, пријавите се сада да бисте објавили на свом налогу.

Guest
Имаш нешто да додаш? Одговори на ову тему

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Чланови који сада читају   0 чланова

    Нема регистрованих чланова који гледају ову страницу

×
×
  • Креирај ново...