Juhász Péter szakmai blogja

The more complex the better

Conditional ForEach<T>T

leave a comment »

Balássy György tegnapi blogbejegyzésében láttam meg a következő problémát. Ha szeretjük a LINQ-t és egy kollekciónak csak bizonyos elemein szeretnénk végrehajtani egy műveletet, akkor ahhoz először ki kell választanunk az adott elemeket (1. ciklus), majd végre kell rajtuk hajtani a kívánt műveletet (2. ciklus). Ez 2 darab ciklust jelent, azonban ehhez elég lenne csak egy.

Van egy listánk:

List<int> numbers = new List<int>() { 1, 2, 3, 4, 5, 6, 7 };

Irassuk ki csak a páros számokat az első módszerrel:

numbers.FindAll(n => n % 2 == 0).ForEach(n => Console.WriteLine(n));

Nézzünk egy sokkal szebb és teljesítménykímélőbb megoldás, egy új kiegészítő metódust:

public static void ForEach<T>(this List<T> list, Predicate<T> predicate, Action<T> action)
{
    Contract.Requires(list != null);
    Contract.Requires(predicate != null);
    Contract.Requires(action != null);

    foreach (T item in list)
    {
        if (predicate(item))
            action(item);
    }
}

Én szivem szerint IEnumerable<T>-t írtam volna a List<T> helyett, de a Framework tervezését kell követni. Valaki nem tudja, hogy miért közvetlen a List<T> kapta meg ezt a metódust és miért nem kiegészítőként egy IEnumerable<T> vagy legalább IList<T>?

Written by BlueCode

július 28, 2009 at 08:32

Posted in Development

Tagged with , , ,

Abstract operators

leave a comment »

Nem csak az osztályok, hanem a metódusok egymásból származtatása is érdekes tud lenni. A Framework belsejében rengeteg ilyet találhatunk. A kódunk mégjobban struktúrált és használható lesz.

A bemutatást egy nagyon egyszerű, általános problémával szeretném szemléltetni. Célunk a következő megvalósítása lenne:

int sum = BigOperators.Sum(1, 100); // 5050

Ehhez nem is kellene többet írni, mint:

public static void Sum(int start, int end)
{
    int result = 0;

    for (int i = start; i <= end; i++)
        result += i;

    return result;
}

Valószínűleg majdnem mindenki így oldalá meg a feladatot. De közelítsük meg a témát jóval mélyebbről. Írjunk egy olyan metódust, amely nem csak int, hanem bármilyen típusú változókkal tud műveleteket végezni. Nem csak összeadni, hanem szorozni is, illetve bármilyen műveletet. Bonyolítsuk meg teljesen! Íme a bázismetódusunk, amely teljesen testreszabható és amelyre az összes többi visszavezet majd:

internal static T Aggregate<T>(T start, Func<T, bool> stop, Func<T, T> step, Func<T, T, T> accumulate)
{
    T result = start;

    for (T current = step(start); !stop(current); current = step(current))
        result = accumulate(result, current);

    return result;
}

Vigyük el ezt az egymást követő értékek, a számok irányába. A stop paraméter egy másik metódust vár, amely eldönti a pillanatnyi értékről, hogy az az utolsó-e és így a ciklus futása befejeződjön-e. Ezt kicseréljük egy lambda kifejezésre, amely lehetővé teszi egy pontos felső korlát megadását:

internal static T RangeAggregate<T>(T start, T end, Func<T, T> step, Func<T, T, T> accumulate) where T : IComparable<T>
{
    return Aggregate<T>(start, v => v.CompareTo(end) > 0, step, accumulate);
}

Így már megadható egy alsó és felső korlát is. Az értékeket a step paraméterként megadott metódus lépteti, tehát teljesen tetszőleges. Most írjunk be egy típust és készítsünk egy olyan metódust, amely a számokat összegzi.

public static int Sum(int start, int end, int step)
{
    return RangeAggregate<int>(start, end, v => v + step, (r, v) => r + v);
}

Végül az utolsó simítás:

public static int Sum(int start, int end)
{
    return Sum(start, end, 1);
}

Készen vagyunk. Nem csak a típust, de a műveleteken át, minden mást absztrakttá tettünk.

Written by BlueCode

július 25, 2009 at 13:45

Posted in Development

Tagged with , ,

Visual Studio 2010 Beta 1

leave a comment »

Kora este elérhetővé vált mindenki számára a várva-várt Visual Studio 2010 első beta verziója.

Többféle letöltési lehetőséget kínálnak, köztük egy web installert is:
http://blogs.msdn.com/onoj/archive/2009/05/19/visual-studio-2010-beta-1-download-options.aspx

Első benyomás:
Az új felület nagyon szép lett, illik a témához. A kódszerkesztőben is van jópár új feature; azonban még nagyon lassú, illetve a kiegészítő listán is sokat kell még dolgozniuk.

Most, hogy már van .NET Framework 4 beta, hamarosan jönnek C# 4.0 kódok is. :)

Written by BlueCode

május 20, 2009 at 21:25

Posted in Development

Tagged with

FunctionCache<T, TResult>

leave a comment »

A maximális teljesítmény eléréséhez általában elengedhetetlen a gyorsítótár. Nézzük meg, hogyan lehet ezt megoldani úgy, hogy ne csak a megoldás, hanem a használat is a lehető legegyszerűbb legyen.

/// <summary>
/// Represents a class, that caches the return value of a specified function.
/// </summary>
public class FunctionCache<T, TResult>
{
    /// <summary>
    /// Initializes a new instance of the FunctionCache&lt;T, TResult&gt; class.
    /// </summary>
    /// <param name="func"></param>
    public FunctionCache(Func<T, TResult> func)
    {
        this.Function = func;
        this.Cache = new Dictionary<T, TResult>();
    }

    protected IDictionary<T, TResult> Cache;

    /// <summary>
    /// Gets the function to be cached.
    /// </summary>
    public Func<T, TResult> Function { get; protected set; }

    /// <summary>
    /// Invokes the function and adds its return value to the cache.
    /// </summary>
    /// <param name="arg"></param>
    /// <returns></returns>
    public TResult Invoke(T arg)
    {
        if (!this.Cache.ContainsKey(arg))
            this.Cache.Add(arg, this.Function(arg));

        return this.Cache[arg];
    }

    /// <summary>
    /// Clears cache.
    /// </summary>
    public void Reset()
    {
        this.Cache.Clear();
    }
}

Written by BlueCode

május 18, 2009 at 15:35

Posted in Development

Tagged with ,

Lambda expressions and Fibonacci

leave a comment »

Ma arra szeretnék kitérni, hogy C#-ban mennyire hasznosak a Lambda kifejezések és mennyire lerövidíthető egyes feladatok megoldása velük. A példánkban a Fibonacci számsor segítségével mutatom be, hogy hogy lehet meghatározni az n-edik számot mindössze egy sorból.

Kezdjük a hagyományos módszerrel, ahogy a többség csinálná:

static uint Fibonacci(uint n)
{
    if (n <= 1)
        return n;
    else
        return Fibonacci(n - 1) + (n - 2);
}

Most ezt alakítsuk át egy kicsit más formába, hogy jobban hasonlítson egy matematikai függvényre:

Func<uint, uint> Fibonacci = delegate(uint n)
{
    if (n <= 1)
        return n;
    else
        return Fibonacci(n - 1) + Fibonacci(n - 2);
};

Na ha ez nem is rövidebb, de már jobb a formája. A következő lépésben Fibonacci számait egy Lambda kifejezéssel fogom megadni (és az if-et átírom más alakra):

Func<uint, uint> Fibonacci = n => n <= 1 ? n : Fibonacci(n - 1) + Fibonacci(n - 2);

Ez lenne a legtömörebb és legszebb alakja… de sajnos csak lenne. Ugyanis a kifejezés definíciójában egy olyan változóra hivatkozok (saját magára) rekurzívan, ami még nincsen definiálva. Ezért sajnos hozzá kell adni még egy sort, amiben null kezdőértékkel bevezetjük a Fibonacci nevű "változót".

Végezetül a 7 sorból lett 2, ami valójában csak 1. :) Teljesítmény szempontjából persze ajánlottabb nem rekurzív képletet (Binet’s formula) használni a számok meghatározásához.

Written by BlueCode

május 13, 2009 at 16:38

Posted in Development

Tagged with ,

Tree transversal

leave a comment »

Az előző bejegyzésben konstruált TreeNode<T> nagyon egyszerű módon bejárható, ha kiegészítjük az alábbi metódussal:

/// <summary>
/// Transverse a tree node.
/// </summary>
/// <param name="action">Performs the specified action on each node.</param>
public void TraversePreorder(Action<TreeNode<T>> action)
{
    action(this);

    foreach (TreeNode<T> node in this)
        node.TraversePreorder(action);
}

Példakód, amely a konzolra kirajzolja a fánkat:

myTreeNode.TransversePreorder(
  (t) => { Console.WriteLine(new string(' ', t.Depth) + t.Value.ToString()); }
);

Written by BlueCode

május 5, 2009 at 20:02

Posted in Development

Tagged with , ,

Depth of a TreeNode<T> (recursive)

leave a comment »

Hogyan határozzuk meg egy faág mélységét egy fában a legegyszerűbb módon? A megoldás egy rekurzív tulajdonság:

/// <summary>
/// Represents a tree node.
/// </summary>
/// <typeparam name="T"></typeparam>
public class TreeNode<T> : IEnumerable<TreeNode<T>>
{
    // ...

    /// <summary>
    /// Gets the parent node.
    /// </summary>
    public TreeNode<T> Parent { get; internal set; }

    /// <summary>
    /// Gets a value indicating whether is this the root of the tree.
    /// </summary>
    public bool IsRoot
    {
        get { return this.Parent == null; }
    }

    /// <summary>
    /// Gets the depth of the node.
    /// </summary>
    public int Depth
    {
        get
        {
            return (this.IsRoot ? 0 : 1 + this.Parent.Depth);
        }
    }

    // ...
}

Written by BlueCode

május 3, 2009 at 18:58

Posted in Development

Tagged with ,

CyclicList<T>

leave a comment »

Az alábbi osztály egy ciklikus listát valósít meg. Az elemek periodikusan ismétlődnek a végtelenségig (System.Int32 típusú indexer határain belül). Az index lehet negatív szám is.

/// <summary>
/// Represents a strongly typed cyclic list of objects that can be accessed by index.
/// </summary>
/// <typeparam name="T"></typeparam>
public class CyclicList<T> : List<T>
{
    /// <summary>
    /// Initializes a new instance of the CyclicList&lt;T&gt;.
    /// </summary>
    public CyclicList() : base() { }
    public CyclicList(int capacity)
        : base(capacity)
    { }
    public CyclicList(IEnumerable<T> collection)
        : base(collection)
    { }

    /// <summary>
    /// Gets or sets the element at the specified index.
    /// </summary>
    /// <param name="index"></param>
    /// <returns></returns>
    public new T this[int index]
    {
        get { return base[this.Mod(index)]; }
        set { base[this.Mod(index)] = value; }
    }

    #region Methods
    /// <summary>
    /// Removes the element at the specified index.
    /// </summary>
    /// <param name="index"></param>
    /// <returns></returns>
    public new void RemoveAt(int index)
    {
        base.RemoveAt(this.Mod(index));
    }

    // ...
    #endregion

    private int Mod(int index)
    {
        int rem = index % base.Count;
        return (rem < 0 ? rem + base.Count : rem);
    }
}

Written by BlueCode

május 2, 2009 at 12:08

Posted in Development

Tagged with ,

Min, Max, Clamp

leave a comment »

Kezdjük néhány egyszerű metódus generikus változatával…

Melyik a kisebb?

public static T Min<T>(T a, T b) where T : IComparable<T>
{
    return (a.CompareTo(b) < 0 ? a : b);
}

 

Melyik a nagyobb?

public static T Max<T>(T a, T b) where T : IComparable<T>
{
    return (a.CompareTo(b) > 0 ? a : b);
}

 

Egy adott érték határok közé szorítása:

public static T Clamp<T>(T min, T max, T value) where T : IComparable<T>
{
    return (
        value.CompareTo(min) < 0 ? min :
            (value.CompareTo(max) > 0 ? max : value)
    );
}

public static T Clamp<T>(Range<T> range, T value) where T : IComparable<T>
{
    return Clamp<T>(range.Start, range.End, value);
}

(A Range osztály definícióját mindenki képzelje hozzá!)

Written by BlueCode

május 1, 2009 at 19:34

Posted in Development

Tagged with ,

Első post

leave a comment »

Ebben a blogban .NET fejlesztésről lesz szó. Reflektorfényben a C# nyelv képességei lesznek; egyszerű és bonyolult problémák gyönyőrű megoldásai.

Written by BlueCode

május 1, 2009 at 18:19

Posted in Uncategorized