11/09/2013

Nullable<T>.Equals(T value)

Home

Po dłuższej urlopowej przerwie w blogowaniu zacznę od zagadki z serii co zostanie wypisane na ekran, którą podsunął mi kolega Przemek:
decimal d = 2;

Console.WriteLine("d == 2 = {0}", d == 2);
Console.WriteLine("d == (decimal)2 = {0}", d == (decimal)2);

Console.WriteLine("d.Equals(2) = {0}", d.Equals(2));
Console.WriteLine("d.Equals((decimal)2) = {0}", d.Equals((decimal)2));
Tutaj jeszcze nie ma haczyka i odpowiedź to 4XTrue. Zmieńmy jednak jedną liniję:

decimal? d = 2;

Tym razem odpowiedź jest mniej oczywista. Na ekran zostanie wypisane: True, True, False, True. Czym więc różni się pierwsze wywołanie Equals od drugiego?

W obu przypadkach wołana jest metoda wirtualna. W obu przypadkach metoda ta wołana jest dla struktury Nullable<T>. Zmienna d nie jest null'em, a więc to też nie jest problemem. Spójrzmy zatem jak zaimplementowano Equals dla Nullable<T>:
public override bool Equals(object other)
{
    if (!this.HasValue)
    {
        return (other == null);
    }
    if (other == null)
    {
        return false;
    }
    return this.value.Equals(other);
}
Nic skomplikowanego, jeśli zmienna jest ustawiona to ponownie wywoływana jest metoda Equals w tym przypadku Decimal.Equals Odpowiedzi musimy szukać więc dalej. Wszystkie typy numeryczne mają przeciążoną metodę Equals w następujący sposób:
public override bool Equals(object value)
public override bool Equals(decimal value)
Która z nich zostanie wywołana w tej sytuacji? Nullable<T>.Equals ma parametr typu object, a więc Decimal.Equals(object value) pasuje lepiej niż Decimal.Equals(decimal value). Ta pierwsza działa natomiast w ten sposób, że jeśli przekazany parametr nie jest typu decimal to zawsze zwraca false nie sprawdzając czy przekazany obiekt można bezpiecznie konwertować na decimal. I ot cała tajemnica ;)

Moim zdaniem działanie Nullable<T> nie jest teraz intuicyjne. Wzorując się na typach numerycznych, można by dopisać do Nullable<T> jeszcze jedną metodę:
public bool Equals(T other)
{
    if (!this.HasValue)
        return false;

    return this.value.Equals(other);
}
Z jakiegoś powodu tego nie zrobiono. Przeoczenie czy celowe działanie? Jestem ciekawy Waszych opinii.

10/08/2013

Konferencja wewnątrz-firmowa 2

Home

Ostatnio napisałem o zorganizowanej przez siebie konferencji wewnątrz-firmowej. Teraz należy napisać o czym można było posłuchać. Oto lista 12 przygotowanych prezentacji, wszystkie moim zdaniem stały na wysokim poziomie. O jakości prezentacji niech świadczą bardzo dobre wyniki ankiet. Średnia ocena to 8.3 w skali od 1 do 10!

Albert Skłodowski - F# as a functional language for the .NET platform

An introduction to functional programming in .NET using F# language. Write simple code to solve complex problems!

Damiarz Orzechowski - Is C++ dead? What was added into C++ x11 standard in comparison to C# and .NET features

Summary of history behind latest C++ standard. Presentation of highlights of latest standard and comparison of features between C++ and C#/.NET.

Daniel Celeda - Software Project Survival Guide

Quick induction to the main factors of a successful software project.“Software projects succeed or fail based on how carefully they are planned and how deliberately they are executed. The vast majority of software projects can be run in a deterministic way that virtually assures success. If a project’s stakeholders understand the major issues that determine project success, they can ensure that their project reaches a successful conclusion.”

Kamil Langowski - Paradoxes and idiosyncrasies of probability

Simple math problems that are outstandingly controversial, but are nonetheless facts.

Marcin Wierzbicki - Financial instruments for dummies

The talk covers basic subjects from financial engineering field, starting with present value, fixed income securities, bond pricing, immunization and arbitrage, and finishing... after 1 hour :)

Marek Ryński - An introduction to the basics of Web Application Security - theoretical lecture

A review of available mechanisms used to ensure confidentiality, integrity and availability of information in Web Applications. The talk will cover different approaches to authentication, highlighting strong and weak solutions. A few words will be said about smart cards and biometrics. Hacking, injection attacks and social engineering are only buzz words to attract your attention on this abstract, but none of these things will be discussed.

Michał Komorowski - RavenDB as an example of document oriented databases. A history of application in my pet project.

NoSQL databases are a relatively new idea which is becoming more and more popular. In this presentation, I’ll focus on document oriented databases. I’ll discuss the topic on the example of RavenDB and my pet project LanguageTrainer which supports learning foreign languages.

Michał Rzeszutko - Dependency Injection patterns and best practices

An introduction to the subject of DI. The talk covers basic subjects - what it is DI and what are the benefits of using it, as well as the more advanced ones – how to use it properly (patterns) and what to avoid (anti-patterns).

Mikołaj Barwicki - Do IT the Toyota Way?

Toyota is an enterprise built on unique philosophy focused on manufacturing and process quality that is summarized in a set of statements referred to as “Toyota Way”. Is this what made Toyota the world’s largest car manufacturer? Are these statements applicable to software development organizations?

Paweł Kupis - Aspect-oriented programming on the example of PostSharp.

Short introduction to aspect-oriented programming concept, fast review of .NET aspect frameworks and PostSharp in action.

Przemek Wasylko - Command-query responsibility segregation: can we successfully separate read from write parts of the system?

Exploration of design pattern where different (often in a radical way) model and approach is used to update information than model used to read information. I will try to show how this affects system architecture and user interface experience. But does it really make engineer’s life easier? And software less prone to errors?

Tomasz Moska - Configuring SQL Server Instance for optimal performance

The talk will cover some good practices regarding configuration of SQL Server instance.

04/08/2013

Konferencja wewnątrz-firmowa

Home

W jednym z ostatnich postów pisałem o zorganizowanym przez siebie Quiz'ie. Dzisiaj napiszę o innym pomyśle na powiew świeżości w pracy, czyli o konferencji wewnątrz-firmowej. Konferencję taką zorganizowałem pod koniec czerwca (jej pierwotnym pomysłodawcą był mój przełożony Mikołaj).

Trochę uprzedzając, był to strzał w dziesiątkę pod każdym względem, a więc czym prędzej biegnijcie do swoich przełożonych z propozycją zorganizowania takiego wydarzenia ;) Zacznijmy od spraw organziacyjnych, czyli jak się do tego zabrałem.
  • Na początek ustaliłem szczegóły z przełożonymi, czyli kiedy taka konferencja może się odbyć i ile czasu można na nią poświęcić. My wybraliśmy ostatni tydzień czerwca i na każdą prezentację przeznaczyliśmy godzinę. W konferencji mógł wsiąść udział każdy i mógł zobaczyć dowolną ilość prezentacji, przy czym było jasno powiedziane, że praca ma priorytet, czyli jeśli jest pilne zgłoszenie to trzeba je obsłużyć.
  • 4 miesiące przed konferencją rozesłałem prośbę o zgłaszanie tematów wraz z krótkim opisem. Na zastanowienie się dałem 1 miesiąc.
  • Co bardzo ważne, tematyka prezentacji mogła być dowolna, również nietechniczna. Jedynym warunkiem było, że prelegent:
    • Ma interesować się tematem.
    • Musi mieć przynajmniej trochę przekonania :), że dla innych temat również będzie interesujący.
  • W regularnych odstępach (to bardzo ważne) rozsyłałem przypomnienie, że zostało tyle, a tyle czasu na  zgłoszenia.
  • Po zebraniu propozycji (około 30 prezentacji, ale każdy mógł zgłosić więcej niż 1) zorganizowałem głosowanie. Na oddanie głosu były 2 tygodnie i znowu należy o tym przypominać.
  • Na konferencję wybrałem 12 najlepszych prezentacji, ale każdy prelegent miał przygotować tylko 1.
  • Poinformowałem kolegów o wyniku głosowania.
  • Aby prelegenci nie czekali do ostatniej chwili z przygotowaniami poprosiłem ich aby przesłali mi agendę miesiąc przed konferencją.
  • Zarezerwowałem salę, rzutnik, laptop itp.
  • Poprosiłem prelegentów o preferencje kiedy chcą wygłosić swoje prezentacje i przygotowałem harmonogram.
  • Dwa tygodnie przed konferencją rozesłałem do wszystkich w firmie zaproszenie na poszczególne prezentacje.
  • Tuż przed samą konferencją przygotowałem ankiety, wydrukowałem je i pociąłem.
  • Sprawdziłem również czy laptop działa, czy współpracuje z rzutnikiem i co jest na nim zainstalowane. Oczywiście poinformowałem również prelegentów czego mogą się spodziewać po sprzęcie. Pomimo to, zgodnie z prawem Murphy'ego, nie obyło się bez niewielkich problemów technicznych.
  • Na każdej prezentacji na stołach czekały długopisy i anonimowe ankiety. Ich wypełnienie było obowiązkowe.
  • Po konferencji zebrałem od prelegentów materiały i umieściłem je na Wiki.
  • Podliczyłem ankiety i rozesłałem je do prelegentów. Przy okazji zapytałem czy zgadzają się na publikację wyników. Wszyscy się zgodzili więc udostępniłem również wyniki ankiet.
Przed godziną zero wszystko było więc zapięte na ostatni guzik. Konferencja spodobała się chyba wszystkim. Ja jestem z niej bardzo zadowolony, zarówno jako organizator jak i prelegent. To wspaniała okazja do integracji zespołu, nauki nowych rzeczy i potrenowania swoich umiejętności prelegenta w sprzyjającym środowisku.

Za jakiś czas napiszę o czym można było posłuchać na konferencji.

28/07/2013

Ciekawa metoda obliczenia pierwiastka kwadratowego

Home

Istnieje wiele metod wyznaczania pierwiastka kwadratowego z liczb naturalnych. Ja napiszę o jednej, która mnie ostatnio urzekła i pokazuje piękno matematyki.

Otóż, wynikiem zastosowania tej metody jest ułamek, który stanowi przybliżenie pierwiastka z danej liczby. Dlaczego tylko przybliżenie? Pierwiastki kwadratowe z liczb naturalnych, nie będących kwadratem innej liczby naturalnej, są liczbami niewymiernymi. Liczb niewymiernych nie da się natomiast zapisać w postaci ułamka.

Metoda ta opiera się o tzw. ułamki łańcuchowe (ang. continued fraction). W skrócie chodzi o to, aby pierwiastek kwadratowy z danej liczby przedstawić w następujący sposób:
sqrt(S) = a0 + 1 / (a1 + 1 / (a2 + 1 / (a3 + 1 / (...))))
Nim dłuższa sekwencja tym otrzymamy dokładniejsze przybliżenie. Co bardzo ciekawe, dla każdej liczby niewymiernej sekwencja wartości a0, a1, a2... w pewnym momencie stworzy cykl. Na przykład dla sqrt(2) sekwencja ta ma postać:
[1, 1, 2, 1, 2, ...]
Natomiast dla sqrt(53):
[7, 3, 1, 1, 3, 14, 3, 1, 1, 3, 14, ...].
Jak wyznaczyć taką sekwencję? Algorytm nie jest bardzo skomplikowany i można go zapisać w kilkunastu liniach kodu. Nie będą go jednak tutaj przytaczał, ponieważ doskonały opis znajduje się już na Wikipedii. Zachęcam do wypróbowania swoich sił i jego zaimplementowania.

Skorzystać możemy również z silnika Wolframalpha. Komenda continued fraction sqrt(X) zwróci nam rozwinięcie pierwiastka z danej liczby X w postaci ułamka łańcuchowego. Natomiast komenda convergents[sqrt(X),N] obliczy N pierwszych przybliżeń pierwiastka z danej liczby X w oparciu o opisaną metodę.

Metoda ta z pewnością może przydać się w obliczeniach naukowych, kiedy bardzo istotna jest precyzja obliczeń i operacje na zmiennym przecinku są niedopuszczalne. W takim przypadku powinniśmy wyznaczyć odpowiednio dokładne przybliżenie sqrt(x) w postaci ułamka.

Na przykład ułamek sqrt(3) ~= 13623482 / 7865521 da nam tyle samo poprawnie obliczonych cyfr po przecinku co Math.Pow(3, -0.5). W tym przypadku sekwencja a0, a1, a2... miała 25 elementów. Każdy dodatkowy element sekwencji da nam większą precyzję. W skrajnym przypadku możemy skorzystać ze struktury BigInteger do reprezentacji licznika i mianownika.

Jeśli temat Was zainteresował to polecam rozwiązanie zadania 64, 65 lub 66 ze strony Project Euler.

21/07/2013

Przykład przeglądu kodu i co z tego wynikło

Home

Jakiś czas temu robiłem przegląd kodu i zwróciłem uwagę na taki fragment implementacji:
public class SrvObject
{
   public int Id { get; set; }
   public string Code { get; set; }
   ...
}
...
private Dictionary<int, SrvObject> _cache = new Dictionary<int, SrvObject>();
...
if (_cache.Values.Any(x => x.Code == codeToFind))
{
   var obj = _cache.Values.FirstOrDefault(x => x.Code == codeToFind);
   ...
}
else
{
   var obj = ReadObject(codeToFind);
   _cache.Add(obj.Id, obj);
   ...
}
Zacznijmy od tego, że mamy klasę, która modeluje jakąś encję z bazy danych i ta encja posiada zarówno identyfikator (wewnętrzny dla systemu) oraz kod, który można na przykład wyświetlić użytkownikowi.

Programista użył słownika, z kluczem w postaci identyfikatora, aby buforować już odczytane z bazy danych encje. Pierwsza rzecz jaka rzuca się w oczy to fakt, że kluczem jest identyfikator, a my wyszukujemy na podstawie kodu co wymaga iterowania po wszystkich zgromadzonych w słowniku obiektach. Wynika to z tego, że w większości przypadków posługujemy się identyfikatorem, a tylko czasami kodem i wspomniany kod został dodany później.

Załóżmy przez chwilę, że jest to ok. Druga rzecz na jaką należy zwrócić uwagę to fakt użycia metody Any i zaraz potem użycie FirstOrDefault. Jest to zbyteczne, kod ten robi dwa razy to samo, wystarczy zastosować tylko FirstOrDefault.

Wróćmy teraz do przeszukiwania słownika na podstawie kodu. Przy pokazanej implementacji musimy w najgorszym wypadku sprawdzić wszystkie obiekty w słowniku co ma złożoność liniową. Przy dużej liczbie obiektów w słowniku i częstych odwołaniach do niego jest to nieefektywne. W takim wypadku należy wprowadzić drugi słownik, w którym kluczem będzie kod. W zależności od sytuacji będziemy używać jednego albo drugiego słownika. Jeśli nie znajdziemy w którymś słowniku szukanego obiektu to dodajemy go do OBU słówników.

Przeprowadziłem mały test obu rozwiązań. Najpierw napisałem trzy metody wyszukujące encje:
  • Znajdź na podstawie identyfikatora
  • Znajdź na podstawie kodu bez dodatkowego słownika
  • Znajdź na podstawie kodu z dodatkowym słownikiem
Początkowo słowniki są puste i generuję pewną liczbę N losowych identyfikatorów i kodów. Następnie znajduję te identyfikatory i kody korzystając z jednego słownika (Znajdź na podstawie identyfikatora + Znajdź na podstawie kodu bez dodatkowego słownika) oraz z dwóch słowników (Znajdź na podstawie identyfikatora + Znajdź na podstawie kodu z dodatkowym słownikiem). Oto wyniki:

Liczba N Liczba unikalnych identyfikatorów Czas (ms) dla dwóch słowników Czas (ms) dla jednego słownika
100 63 3 4
500 320 2 4
1000 639 2 6
5000 3169 2 96
10000 6326 4 370
50000 31579 14 9190

Czas mierzyłem dla trybu Release. Jak widać nawet dla małej liczby elementów dwa słowniki są szybsze i to pomimo konieczności zarządzania jednym słownikiem więcej. Dla dużej liczby elementów przewaga jest miażdżąca. Jest to kolejny przykład, jak ważne są dobrze dobrane struktury danych.

Na koniec zwrócę jeszcze uwagę, że dwa słowniki oznaczają większe zapotrzebowanie na pamięć. W moich eksperymentach dwa słowniki dla 10 tyś elementów zużyły ~0.5MB więcej pamięci niż 1 słownik, dla 50 tyś elementów ~1.3 MB więcej, a dla 100 tyś elementów ~3.3 MB więcej.