Alati funkcionalnog programera

Nakon prošlonedeljnog uvoda u funkcionalno programiranje, Marko nam donosi drugi deo

Marko Pavlović
14/07/2016

Kao što se objektno orijentisano programiranje oslanja na ugrađene objekte za rad sa kolekcijama podataka, tako se funkcionalno programiranje (vidi uvodni post) oslanja, naravno na funkcije.

One su realizovane kao funkcije višeg reda, i zahvaljujući tome se veoma lako kombinuju. To znači da postoji samo mali broj osnovnih funkcija od kojih se mogu dobiti sve ostale. Mi ćemo zbog toga posebnu pažnju obratiti na:

Sort

Pravi klasik! Sort funkcija generalno prihvata dva argumenta:

Ovo je primer inverzije kontrole, gde funkcija višeg reda sama određuje kada treba pozvati funkciju poređenja. Algoritam sortiranja je dat kao deo standardne biblioteke većine programskih jezika, što znači da mi ne moramo da razmišljamo o samom načinu sortiranja.

Neki od osnovnih algoritama sortiranja su:

Čest je slučaj da glavna sort funkcija dinamički može da izabere koji će algoritam sortiranja primeniti, u zavisnosti od veličine niza, raspoloživih resursa itd…

Na primer

Ako je data lista osoba kao što je:

  • Ruby
  • Elixir
  • JS
list = [
  {name: "Bob", age: 25},
  {name: "Alice", age: 27}
]
list = [
  [name: "Bob", age: 25],
  [name: "Alice", age: 27]
]
var list = [
  {name: "Bob", age: 25},
  {name: "Alice", age: 27}
]

možemo je sortirati ovako:

  • Ruby
  • Elixir
  • JS
list.sort do |p1, p2|
  p1.name <=> p2.name
end
Enum.sort(list, fn(p1, p2) ->
  p1[:name] < p2[:name]
end)
list.sort((p1, p2) => 
  p1.name.localeCompare(p2.name));

čime se dobija:

  • Ruby
  • Elixir
  • JS
list = [
  {name: "Alice", age: 27},
  {name: "Bob", age: 25}
]
list = [
  [name: "Alice", age: 27],
  [name: "Bob", age: 25]
]
var list = [
  {name: "Alice", age: 27},
  {name: "Bob", age: 25}
]

Map

Ova funkcija prihvata dva argumenta:

Način rada map funkcije je sledeći: ona prolazi kroz datu listu i za svaki element u listi poziva datu funkciju. Rezultat poziva te funkcije dodaje u novu listu i na kraju prolaska vraća tu novu lisut kao rezultat.

Na primer

Ako je data lista brojeva:

list = [1, 2, 3, 4, 5]

Možemo dobiti listu kvadrata tih brojeva na sledeći način:

  • Ruby
  • Elixir
  • JS
list.map do |num|
  num * num
end
Enum.map(list, fn(num) ->
  num * num
end)
list.map(num => num * num)

Što daje:

[1, 4, 9, 16, 25]

Funkcija map je veoma korisna kada želimo da izvršimo istu obradu nad svim elementima jedne liste. Na primer:

Filter

Funkcija filter prihvata dva argumenta:

Ono što funkcija filter radi može se jednostavno opisati – prolazi kroz listu elemenata i za svaki poziva datu funkciju. Ako je rezultat tog poziva true, taj element se dodaje u rezultujuću listu, u suprotnom taj element se preskače. Konačni rezultat je lista koja se sastoji samo od elemenata za koje je data funkcija vratila true.

Na primer

Ako uzmemo listu brojeva iz prethodnog primera:

list = [5, 2, 3, 4, 1]

Možemo da primenimo filter funkciju na sledeći način:

  • Ruby
  • Elixir
  • JS
# In Ruby filter is called select
list.select do |num|
  num < 3
end
Enum.filter(list, fn(num) ->
  num < 3
end)
list.filter(num => num < 3)

I tada dobijamo:

[2, 1]

Filter funkcija može biti veoma korisna u sledećim situacijama:

Reduce

Ova funkcija je posebna po tome što za razliku od ostalih ovde navedenih funkcija ona prihvata listu elemenata, ali je njen rezultat je jedna vrednost. Argumenti reduce funkcije su:

Kako radi

Na primer

Hajde da vidimo kako možemo iskoristiti reduce funkciju da saberemo listu brojeva:

[1, 2, 3, 4, 5]

Evo šta treba uraditi:

Evo dijagrama koji ilustruje ceo postupak:

Ilustracija rada reduce funkcije

Evo i primera koda:

  • Ruby
  • Elixir
  • JS
list.reduce(0) do |acc, value|
  acc + value
end
Enum.reduce(list, 0, fn(value, acc) ->
  acc + value
end)
list.reduce((acc, value) => acc + value, 0)

Take while

Funkcija take while prihvata dva argumenta:

Način rada je sledeći: počinje obilazak liste od prvog elementa i jednostavno poziva datu funkciju za svaki element. Sve dok dobija rezultat true ona dodaje trenutni element u rezultujuću listu. Prvi put kada dobije false, obilazak liste prestaje i vraća se rezultujuća lista.

Na primer

Ako je data lista brojeva:

list = [5, 3, 2, 4, 1]

Možemo primeniti take_while funkciju na sledeći način:

  • Ruby
  • Elixir
  • JS
list.take_while do |num|
  num * 5 > 13
end
Enum.take_while(list, fn(num) ->
  num * 5 > 13
end)
// In JavaScript there is no built-in takeWhile function,
// but we can use one from the underscore.js library
_.takeWhile(list, num => 5 * num > 13);

Čime dobijamo:

[5, 3]

Take while funkcija je korisna kada radimo sa sortiranim listama i želimo da dobijemo samo elemente koji zadovoljavaju određeni kriterijum. U tom slučaju take while ima bolje performanse od filter funkcije.

Prednosti

Marko Pavlović

Objavio/la članak.

četvrtak, 14. Jul, 2016.

IT Industrija

🔥 Najčitanije

Goran S. Milovanović

ponedeljak, 18. Jul, 2016.

Bravo za uvod u funcionalno programiranje! - Veoma važno u Data Science, posebno za one koji žele da specijalizuju R.

Marko Pavlović

subota, 16. Jul, 2016.

Hvala na lepim komentarima! :) Potpuno se slazem da treba da napisati JS primere za ES2015. Radim na tome, do sutra će biti objavljeno. :)

Богдан

četvrtak, 14. Jul, 2016.

Супер чланак! ПРидружујем се захтеву за ажурирањем ЈЅ кода у ЕЅ6 стандард. Сада би се reduce писало овако: list.reduce( (acc, value) => acc + value )

Bojan

četvrtak, 14. Jul, 2016.

Mirko, mislis valjda lambda expressions? Lambda calculus je ipak malo siri pojam :)

Mirko

četvrtak, 14. Jul, 2016.

Super tekst! :D Da li bi mogao da update-ujes JS na ES2015, sa obzirom da je to sada standard, a sadrzi lamda calculus by default? :)