Projektowanie, Programowanie, Codzienność – BeniaminZaborski.com

4 Marzec 2014

Poszukiwacze kamieni szlachetnych – lambda

Filed under: Poszukiwacze kamieni szlachetnych — Tags: , , , — Beniamin Zaborski @ 20:08

W C# mamy wyrażenia lambda, które są anonimowymi funkcjami za pomocą których można tworzyć np. delegaty. Raczej powinny być znane każdemu programiście C# szczególnie, że dość intensywnie używamy ich w LINQ. Spójrzmy na przykład:

delegate bool AreEqualDelegate(int a, int b);
AreEqualDelegate AreEqual = (a, b) => a == b;
bool result = AreEqual(7, 4);

Wywołanie AreEqual z parametrami 7 i 4 zwróci nam oczywiście wartość false.

Jak się sprawa ma w Ruby? A no jak się domyślacie w Ruby także są wyrażenia lambda. Spróbujmy napisać analogiczny do powyższego kod w Ruby:

are_equal = lambda {|a, b| a == b }
result = are_equal.call(7, 4)

are_equal jest tutaj odpowiednikiem delegatu AreEqualDelegate w C# i jak wszystko w Ruby jest obiektem. Ale jakim obiektem? Sprawdźmy to:

are_equal.class

metoda class zwróci nam Proc. Proc to klasa reprezentująca blok kodu w Ruby, gdyż to {|a, b| a == b } to jest właśnie blok kodu.

Do czego nam te lambdy? A no właściwie możemy ich używać w takim samym zakresie jak w C#. Dzięki wyrażeniom lambda możemy np. wprowadzić pewien dodatkowy poziom abstrakcji w naszym kodzie. Spójrzmy na przykład:

class Customer
 attr_accessor :name, :number

 def validate(validator)
   validator.call(self)
 end
end
c = Customer.new
c.name = "Rubers Company"
c.number = "CUST000001"
validator = lambda {|customer| customer.name.length < 50 }
result = c.validate(validator)

Mamy tutaj prostą klasę Customer posiadającą dwie właściwości name i number oraz metodę validate. Metoda ta sama w sobie nie określa jak ma wyglądać walidacja, a jest ona dostarczana z zewnątrz właśnie dzięki wyrażeniom lambda/blokom kodu. W dalszej części widać walidator który weryfikuje długość pola name w klasie Customer i jest on przekazany do metody validate.

Tu muszę wspomnieć o ciekawym rozwiązaniu w Ruby, a mianowicie yield. Dzięki yield metoda validate w klasie Customer może wyglądać tak:

def validate
  yield(self)
end

a wywołanie metody validate tak:

result = c.validate {|customer| customer.name.length < 50 }

Jak widzimy yield wywołuje blok kodu przekazany z zewnątrz w sposób mniej jawny dzięki czemu mogliśmy się pozbyć parametru metody validate i bezpośredniej referencji do walidatora. Oba przedstawione warianty kodu z validator.call i yield są równoważne. Podkreślić należy jednak to, że wersja z yield zawsze będzie wydajniejsza.

Na koniec dodam, że istnieje alternatywny zapis bloku kodu w Ruby do {} z użyciem słów kluczowych do … end. Oto przykład:

are_equal = lambda do |a, b|
  a == b
end

Dodaj komentarz »

Brak komentarzy.

RSS feed for comments on this post. TrackBack URI

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Log Out / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Log Out / Zmień )

Facebook photo

Komentujesz korzystając z konta Facebook. Log Out / Zmień )

Google+ photo

Komentujesz korzystając z konta Google+. Log Out / Zmień )

Connecting to %s

Blog na WordPress.com.

%d bloggers like this: