Projektowanie, Programowanie, Codzienność – BeniaminZaborski.com

4 marca 2014

Poszukiwacze kamieni szlachetnych – lambda

Filed under: Poszukiwacze kamieni szlachetnych — Tagi: , , , — 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
Reklamy

1 marca 2014

Poszukiwacze kamieni szlachetnych – wszystko jest obiektem

Filed under: Poszukiwacze kamieni szlachetnych — Tagi: , , — Beniamin Zaborski @ 17:48

Ruby to język obiektowy, bardzo obiektowy:). Ruby jest tak bardzo obiektowy, że dosłownie wszystko tutaj jest obiektem, nawet liczba 1. Przyjrzyjmy się temu bliżej. Skoro 1 jest obiektem to można przypuszczać, że posiada jakieś metody. A i owszem. Nasza jedynka potrafi nawet nam powiedzieć jakie posiada metody. Spróbujmy czegoś takiego (najlepiej w IRB):

1.methods

Otrzymamy listę wszystkich metod naszego obiektu. methods to nic innego jak metoda, która zwraca listę dostępnych metod na obiekcie. Tak wygląda wywołanie metody bez parametrów w Ruby.

Na liście metod możemy znaleźć np. metodę o nazwie equal?. Ten znak zapytania może wygląda nieco dziwnie dla programisty C#, ale to tylko część nazwy metody. Taka jest konwencja nazewnicza dla metod zwracających wartość typu logicznego true/false. Wywołajmy zatem tą metodę z parametrem:

1.equal?(1)

W rezultacie dostaniemy true. Jest OK, lecimy dalej.

Może się zastanawiacie czemu nazwy metod są poprzedzone znakiem „:” ? Nie zastanawiajcie się i na razie przyjmijmy dla uproszczenia że tak musi być (dla ciekawskich rzucam hasło symbole).

Na liście metod zwróconych przez metodę methods zapewne zauważyliście kilka dziwnie wyglądających i może nie do końca tam pasujących, np. +, -, [], itp. Wszystko jest OK. Ruby to język bardzo obiektowy, pamiętacie? „+” to metoda realizująca dodawanie. Użyjmy jej:

1.+(2)

Na pierwszy rzut oka (dla programisty C#) wygląda dziwacznie, ale „+” to tylko specyficzna nazwa metody, równie dobra jak add.

Dzięki cukrowi syntaktycznemu można powyższe zapisać także następująco:

1+2

Dotyczy to nie tylko metody „+”, ale także kilku innych.

Ciągi znaków w Ruby to także obiekty. Zdefiniować je możemy na kilka sposobów, np.:

"Z Ruby jest przednia zabawa"
'Z Ruby jest przednia zabawa'

Obie powyższe instrukcje spowodują utworzenie obiektu string. Jest jednak między nimi pewna różnica. Ogólnie można powiedzieć, że wersja z apostrofami jest nieco uboższa, tj. nie obsługuje escape-owania i interpolacji. W wersji z cudzysłowami możemy zatem zrobić tak:

name = 'Jan'
"Cześć #{name}"

Jak to wygląda w C#? C# to także język obiektowy, ale np. typ int (będący referencją do System.Int32) jest de facto strukturą, która nie posiada tylu metod co klasy Fixnum czy Integer w Ruby. Listę metod dostępną dla danego obiektu można w C# pobrać za pomocą mechanizmu refleksji, a typ string w C# także jest obiektem jak w Ruby.

Oczywiście C# nie jest wcale „gorszym” językiem programowania, gdyż wszystko to co w Ruby da się zrealizować w C# tylko po prostu czasami trochę inaczej. Jeśli miałbym to jakoś podsumować, to chyba nie rozminę się zbytnio z prawdą jak powiem że Ruby koncepcyjnie jest nieco „bardziej obiektowy” niż C#.

Blog na WordPress.com.