Tablica generyczna - jak to zrobić?

Tablica generyczna - jak to zrobić?
MI
  • Rejestracja:ponad 11 lat
  • Ostatnio:ponad 8 lat
  • Postów:28
0

Witam wszystkich forumowiczów. Mam pytanie odnośnie jednego z zadanek jakie chciałem rozwiązać. Niestety nie do końca wiem jak się za nie zabrać gdyż ciągle się uczę C#. Poniżej przedstawiam treść tego zadanka. Byłbym wdzięczny za jakąkolwiek pomoc z waszej strony.

Napisać klasę w języku C# obsługującą rzadką tablicę generyczną (np. o rozmiarze 2 000 000 000 elementów) wiedząc, że liczba faktycznie przechowywanych elementów nie przekracza 1000.

M8
  • Rejestracja:ponad 11 lat
  • Ostatnio:ponad 11 lat
  • Postów:59
0

http://stackoverflow.com/questions/12559730/creating-a-generic-array-at-runtime + mnóstwo innych bardziej lub mniej ciekawych linków w Google pod hasłem: "generic array C#".
Przyznaj się - nie chciało Ci się szukać nie?

MI
  • Rejestracja:ponad 11 lat
  • Ostatnio:ponad 8 lat
  • Postów:28
0

To owszem widziałem ale nie wiem jak dokładnie to wykonać, gdyż jak pisałem ciągle się uczę tego języka. Dlatego myślałem że ktoś mi w tym pomoże nie odsyłając wyłącznie do innych linków...

Lena(R)
  • Rejestracja:około 14 lat
  • Ostatnio:ponad 4 lata
  • Postów:98
0

A co oznacza "rzadka tablica generyczna"? Z naciskiem na "rzadka".
Kompletnie nie rozumiem treści zadania, co tak naprawdę tam trzeba zrobić.

n0name_l
  • Rejestracja:ponad 12 lat
  • Ostatnio:ponad 4 lata
  • Postów:2412
1

Jesli dobrze rozumiem, to nie wiesz jak stworzyc klase generyczne.

Tak w duzym skrocie:

Kopiuj
using System;
 
namespace Program
{
    public class Tabliczka<T> where T : new()
    {
        public T[] Tablica { get; set; }
        
        public Tabliczka(uint rozmiar)
        {
            Tablica = new T[rozmiar];
        }
    }
    
    public class Program
    {
        public static void Main()
        {
            Tabliczka<int> t = new Tabliczka<int>(30);
            Console.WriteLine("=> {0}", t.Tablica[4]);
            
            Tabliczka<float> f = new Tabliczka<float>(10);
            Console.WriteLine("=> {0}", f.Tablica[4]);
            
            Tabliczka<Program> p = new Tabliczka<Program>(10);
            Console.WriteLine("=> {0}", p.Tablica[4]);
        }
    }
}

http://ideone.com/jGolSo

W nawiasach trojkatnych jest oznaczany parametr generyczny, ktory jest aliasem dla typu. where oznacza zawezenie liczby przypadkow, w tym przykladzie do wszystkich typow, ktore maja konstruktor domyslny.

edytowany 2x, ostatnio: n0name_l
MI
  • Rejestracja:ponad 11 lat
  • Ostatnio:ponad 8 lat
  • Postów:28
0

Proszę o sprawdzenie czy coś takiego jest poprawne odnośnie tego zadanka? Wzorowałem się na przykładach forumowiczów oraz znalezionych w internecie.

Kopiuj
using System;
using System.Collections.Generic;
using System.Text;

namespace Generic_Tab
{
    public class GenericArray<T> where T : new()
    {   //W nawiasach trojkatnych jest oznaczany parametr generyczny, ktory jest aliasem dla typu. 
        //"where" oznacza zawezenie liczby przypadkow, w tym przykladzie do wszystkich typow,
        //ktore maja konstruktor domyslny. 
        
        private T[] array;
        
        //Ustawienie rozmiaru tablicy
        public GenericArray(int size)
        {
            array = new T[size];
        }
        
        //Pobranie elementu o indeksie "index" z tablicy
        public T getItem(int index)
        {
            return array[index];
        }
        
        //Ustawienie elementu w tablicy w miejscu o indeksie "index"
        public void setItem(int index, T value)
        {
            array[index] = value;
        }
    }

    class Test
    {
        //Przykład uzycia i obsługi tablicy generycznej
        static void Main(string[] args)
        {
            int size = 6;
            //--------------------------------------------------
            //Deklaracja tablicy int
            GenericArray<int> intArray = new GenericArray<int>(size);
            //Wstawienie wartości do tablicy
            for (int c = 0; c < size; c++)
            {
                intArray.setItem(c, c * c);
            }
            //Pobranie wartości do wyświetlenia
            Console.Write("\nTablica int  = [ ");
            for (int c = 0; c < size; c++)
            {
                Console.Write(intArray.getItem(c) + " ");
            }
            Console.Write("]\n");
            //--------------------------------------------------
            //Deklaracja tablicy char
            GenericArray<char> charArray = new GenericArray<char>(size);
            //Wstawienie charów do tablicy
            for (int c = 0; c < size; c++)
            {
                charArray.setItem(c, (char)(c + 97));
            }
            //Odczyt charów z tablicy na ekran
            Console.Write("Tablica char = [ ");
            for (int c = 0; c < size; c++)
            {
                Console.Write(charArray.getItem(c) + " ");
            }
            Console.Write("]\n");
            Console.ReadKey();
        }
    }
}
n0name_l
  • Rejestracja:ponad 12 lat
  • Ostatnio:ponad 4 lata
  • Postów:2412
0

Za wielkiego sensu ta klasa nie ma.

  1. Po co tworzysz 2 oddzielne metody, skoro moze to zrobic kompilator za Ciebie?
  2. Jaki jest sens tego ograniczenia, w przypadku gdy z niego nie korzystasz w zaden sposob?
somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:około 5 godzin
  • Lokalizacja:Wrocław
0
Lena(R) napisał(a):

A co oznacza "rzadka tablica generyczna"? Z naciskiem na "rzadka".
Kompletnie nie rozumiem treści zadania, co tak naprawdę tam trzeba zrobić.

Rzadka tablica, to informatyczna wersja rzadkiej macierzy. Większość elementów jest pusta (0 albo null).

Ja bym sugerował zacząć od myślenia, bo to jest problem algorytmiczny, a nie implementacyjny. Nie utworzy się przecież tak po prostu tablicy tej wielkości, bo zabraknie pamięci operacyjnej.

edytowany 1x, ostatnio: somekind
MI
  • Rejestracja:ponad 11 lat
  • Ostatnio:ponad 8 lat
  • Postów:28
0

**n0name_l **to jak Ty byś to rozwiązał? Mógłbyś zademonstrować, byłbym wdzięczny?

somekind masz rację. Ale jak w takim razie byś się za to zabrał? Czytałem wcześniej o reprezentacji macierzy rzadkich w postaci (x,y,z), tzn dla macierzy np 1000x1000 mając jeden element niezerowy np M[345,754]=65 to można zapisać to jako M(345,754,65) żeby nie pamiętać miliona elementów zerowych tylko jeden niezerowy. Czy to o to chodzi?

n0name_l
1. Po ludzku, auto-properties, nic wiecej w tej klasie nie masz ... 2. Zwykle hashtable na dobra sprawe wystarcza.
1

Moja wizja tablicy rzadkiej jest taka - robisz Hashtable w klasie generycznej która przechowuje(wstawiane są do niej) tylko wartości różne od default(T), czyli rożne od wartości domyślnej typu T. Przy pobieraniu elementu o zadanym indeksie, sprawdzasz czy taki klucz istnieje w Hashtable, jeżeli tak, to pobierasz wartość z niej, w przeciwnym wypadku zwracasz wartość domyślną - default(T).

Co do samego działania tablicy generycznej dobrym rozwiązaniem będzie zastosowanie indexera konstruując odpowiednio metody get i set. Tutaj przykład jak to zrobić:
http://msdn.microsoft.com/en-us/library/vstudio/6x16t2tx.aspx

MI
  • Rejestracja:ponad 11 lat
  • Ostatnio:ponad 8 lat
  • Postów:28
0

Frizz a czy mógłbyś mi to zrobić według tego co opisujesz? Pytam, gdyż chciałbym się tego nauczyć a nie bardzo wiem jak to po Twojemu ugryźć...

FR
  • Rejestracja:ponad 11 lat
  • Ostatnio:ponad 11 lat
  • Postów:2
0

Chciałem Cię trochę nakierować co byś sam pogłówkował, ale okej. Łap półgotowca :P. Zmodyfikowałem Twoją klasę aby realizowała ideę tablicy rzadkiej. Powinno działać, jednak oprócz metod getItem i setItem dodałbym indexer o którym pisałem wcześniej, żeby można było korzystać z tej klasy jak z normalnej tablicy. Dodatkowo uważam że warto by zaimplementować interfejs IEnumerable, który pozwoliłby latać po niej pętlą foreach i używać na niej zapytań LINQ. Zachęcam do spróbowania :).

Kopiuj
     public class GenericArray<T> 
    {   
        private Hashtable array;
        private int size;
       
        public GenericArray(int size)
        {
            array = new Hashtable();
            this.size = size;
        }

        public T getItem(int i)
        {
            return array.ContainsKey(i) ? (T)array[i] : default(T);
        }

        public void setItem(int i, T value)
        {
            if (i >= size || i < 0)
                throw new IndexOutOfRangeException();
            if (!value.Equals(default(T)))
            {
                array[i] = value;
            }
            else if (array.Contains(i))
            {
                array.Remove(i);
            }
        }
    }
Zobacz pozostałe 2 komentarze
FR
Owszem, cała idea tablicy rzadkiej w tym wykonaniu opiera się o Hashtable, i to jest tylko opakowanie które znacząco ułatwia korzystanie z takiej tablicy. Poza tym takie było zadanie - stworzyć klasę generyczną co zostało tu zrobione
n0name_l
No okej, ale dlaczego chociazby nie uzyles Dictionary<> w celu unikniecia tego smiesznego rzutowania? Do tego po co trzymasz rozmiar jako int?
FR
Można również użyć Dictionary<>, jednak szperając po forach zagranicznych natrafiłem na opinie że w takim rozwiązaniu działa wolniej niż Hashtable. Nie sprawdzałem tego osobiście więc nie mogę potwierdzić. Co jest złego w trzymaniu rozmiaru jako int?
n0name_l
1. Ja jedynie odwiedzilem msdn i informacja, ze Dictionary jest zaimplementowane jako hashatable wystarcza do stwierdzenia, ze bedzie szybsze (jednak ofc moge sie mylic) 2. Trzymanie rozmiaru jako int to podstawowy blad logiczny, wielu poczatkujacych tak robi poniewaz uzywa inta do wszystkiego. A rozmiar jako tako nie moze byc ujemny, wiec ten typ jest calkowicie bezsensowny i powoduje dodatkowy warunek w ifie ;>
FR
Żeby się dowiedzieć czy Dictionary jest szybsze trzeba by to sprawdzić. Przypuszczam że wyniki okażą się bardzo podobne. Oba rozwiązania wydają się poprawne, i jeśli oba są tak samo szybkie to wcale nie uważam żeby potrzeba rzutowania przy pobieraniu elementu dyskwalifikowała użycie Hashtable. Co do int'a to szukasz dziury w całym :P. Czemu zatem np. Count w listach jest intem a nie uintem, przecież ilość elementów nie może być ujemna.
MI
  • Rejestracja:ponad 11 lat
  • Ostatnio:ponad 8 lat
  • Postów:28
0

Frizz wielkie dzięki za ten kawałek kodu. Postaram się zrozumieć jak to działa i dodać do niego indexera o którym wspominałeś, bo widzę że to dobre rozwiązanie byłoby. Jak będę miał gotowe to wrzucę tu kod żebyś ewentualnie mógł sprawdzić czy dobrze zrobiłem. A o tym interfejsie IEnumerable to dopiero będę musiał poczytać bo jeszcze z tego nie korzystałem, podobnie z LINQ - ale wszystko jeszcze przede mną ;)

DU
Zaimplementuj tez IEnumerable albo jeszcze lepiej ICllection wtedy bedzie wypas i na LiNq bedzie dzialac :)
MI
  • Rejestracja:ponad 11 lat
  • Ostatnio:ponad 8 lat
  • Postów:28
0

Kurcze coś nie mogę zrobić tego IEnumerable. Wyczytałem że

foreach requires IEnumerable, not IEnumerable<T>
. Pomożecie jeszcze troszkę?

Azarien
to jest półprawda, bo kiedyś sprawdzałem i foreach użyje IEnumerable<T> jeśli jest zaimplementowane (ale wystarczy samo IEnumerable żeby się kod kompilował)
MI
No to ale u mnie się nie kompiluje... Nie mam już dziś pomysłu jak to rozwiązać. Możesz naprowadzić jak to zrobić żeby zadziałało?
n0name_l
  • Rejestracja:ponad 12 lat
  • Ostatnio:ponad 4 lata
  • Postów:2412
0

Tak na szybko napisalem:

Kopiuj
using System;
using System.Collections;
using System.Collections.Generic;
 
namespace Program
{
  class Array<T> : IEnumerable
  {
    private Dictionary<uint, T> array = new Dictionary<uint, T>();
        
    public T this[uint index]
    {
      get { return array[index];  }
      set { array[index] = value; }
    }
        
    public IEnumerator GetEnumerator()
    {
      foreach(KeyValuePair<uint, T> keyValuePair in array)
      {
        yield return keyValuePair.Value;
      }
    }
  }
    
  class Program
  {
    static void Main()
    {
      Array<char> array = new Array<char>();
      array[10] = 'X';
      array[3] = '8';
            
      foreach(char elem in array)
      {
        Console.WriteLine(elem);
      }
    }
  }
}

http://ideone.com/WutXCJ

Metode te mozna jeszcze zaimplementowac, tak:

Kopiuj
foreach(T val in array.Values)
{
  yield return val;
}

badz tak:

Kopiuj
return array.Values.GetEnumerator();
edytowany 3x, ostatnio: n0name_l
Azarien
  • Rejestracja:ponad 21 lat
  • Ostatnio:około 10 godzin
1

To jest właśnie przykład, w którym IEnumerable<T> będzie lepsze, bo IEnumerable powoduje boksowanie wartości char (konwersję na object i z powrotem), a IEnumerable<T> stałoby się IEnumerable<char> bez konwersji podczas pracy pętli foreach.

MI
  • Rejestracja:ponad 11 lat
  • Ostatnio:ponad 8 lat
  • Postów:28
0

Kombinowałem, szukałem po internecie i w końcu udało się w taki sposób że zadziałało. Czy ktoś mi może powiedzieć czy to dobrze, bo trochę kodu wyszło z tym IEcollecgtion.

Kopiuj
namespace Generic_Tab
{
 
public class GenericArray<T> : ICollection<T>
    {
        private Hashtable array;        //Deklaracja tablicy typu Hashtable
        private int size;

        public GenericArray(int size)
        {
            array = new Hashtable();    //Utworzenie tablicy konstruktorem
            this.size = size;
        }
        public T this[int i]
        {
            get
            {
                return array.ContainsKey(i) ? (T)array[i] : default(T);
            }
            set
            {
                if (i >= size || i < 0)
                    throw new IndexOutOfRangeException();
                if (!value.Equals(default(T)))
                {
                    array[i] = value;
                }
                else if (array.Contains(i))
                {
                    array.Remove(i);
                }
            }
        }


        protected bool _IsReadOnly;
        // Number of elements in the collection
        public virtual int Count
        {
            get
            {
                return array.Count;
            }
        }

        // Flag sets whether or not this collection is read-only
        public virtual bool IsReadOnly
        {
            get
            {
                return _IsReadOnly;
            }
        }

        // Add a business object to the collection
        public virtual void Add(T ArrayObject)
        {
            array.Add(array,size );	// Znalazłem wersję array.Add(ArrayObject) ale wywalało błąd "1 argumrnt"
        }

        // Remove first instance of a business object from the collection 
        public virtual bool Remove(T ArrayObject)
        {
            bool result = false;

            //loop through the inner array's indices
            for (int i = 0; i < array.Count; i++)
            {
                //store current index being checked
                T obj = (T)array[i];

                //compare the BusinessObjectBase UniqueId property
                if (obj.GetHashCode() == ArrayObject.GetHashCode())
                {
                    //remove item from inner ArrayList at index i
                    array.Remove(i);
                    result = true;
                    break;
                }
            }
            return result;
        }

        // Returns true/false based on whether or not it finds
        // the requested object in the collection.
        public bool Contains(T ArrayObject)
        {
            //loop through the inner ArrayList
            foreach (T obj in array)
            {
                //compare the BusinessObjectBase UniqueId property
                if (obj.GetHashCode() == ArrayObject.GetHashCode())
                {
                    //if it matches return true
                    return true;
                }
            }
            //no match
            return false;
        }

        // Copy objects from this collection into another array
        public virtual void CopyTo(T[] ObjectArray, int index)
        {
            throw new Exception(
              "This Method is not valid for this implementation.");
        }

        // Clear the collection of all it's elements
        public virtual void Clear()
        {
            array.Clear();
        }

        // Returns custom generic enumerator for this BusinessObjectCollection
        public virtual IEnumerator<T> GetEnumerator()
        {
            //return a custom enumerator object instantiated
            //to use this BusinessObjectCollection 
            return new ArrayEnumerator<T>(this);
        }

        // Explicit non-generic interface implementation for IEnumerable
        // extended and required by ICollection (implemented by ICollection<T>)
        IEnumerator IEnumerable.GetEnumerator()
        {
            return new ArrayEnumerator<T>(this);
        }
		
		//Klasa do obsługi IEnumeratora
        public class ArrayEnumerator<T> : IEnumerator<T> 
        {
            protected GenericArray<T> _collection; //enumerated collection
            protected int index; //current index
            protected T _current; //current enumerated object in the collection

            // Default constructor
            public ArrayEnumerator()
            {
                //nothing
            }

            // Paramaterized constructor which takes
            // the collection which this enumerator will enumerate
            public ArrayEnumerator(GenericArray<T> collection)
            {
                _collection = collection;
                index = -1;
                _current = default(T);
            }

            // Current Enumerated object in the inner collection
            public virtual T Current
            {
                get
                {
                    return _current;
                }
            }

            // Explicit non-generic interface implementation for IEnumerator
            // (extended and required by IEnumerator<T>)
            object IEnumerator.Current
            {
                get
                {
                    return _current;
                }
            }

            // Dispose method
            public virtual void Dispose()
            {
                _collection = null;
                _current = default(T);
                index = -1;
            }

            // Move to next element in the inner collection
            public virtual bool MoveNext()
            {
                //make sure we are within the bounds of the collection
                if (index++ >= _collection.Count)
                {
                    //if not return false
                    return false;
                }
                else
                {
                    //if we are, then set the current element
                    //to the next object in the collection
                    _current = _collection[index];
                }
                //return true
                return true;
            }

            // Reset the enumerator
            public virtual void Reset()
            {
                _current = default(T); //reset current object
                index = -1;
            }
        }
    }

    class Test
    {
        //Przykład uzycia i obsługi tablicy generycznej
        static void Main(string[] args)
        {
            int size = 6;
            //--------------------------------------------------
            //Deklaracja tablicy int
            GenericArray<int> intArray = new GenericArray<int>(size);
            //Wstawienie wartości do tablicy
            for (int c = 0; c < size; c++)
            {
                intArray[c]=c*c;
            }
            //Pobranie wartości do wyświetlenia
            Console.Write("\nTablica int  = [ ");
            foreach (int s in intArray)
            {
                Console.Write(s + " ");
            }
         Console.Write("]\n");
            //--------------------------------------------------
            //Deklaracja tablicy char
            GenericArray<char> charArray = new GenericArray<char>(size);
            //Wstawienie charów do tablicy
            for (int c = 0; c < size; c++)
            {
                charArray[c] = ((char)(c + 97));
            }
            //Odczyt charów z tablicy na ekran
            Console.Write("Tablica char = [ ");
            foreach (char s in charArray)
            {
                Console.Write(s + " ");
            }
            Console.Write("]\n");
            Console.ReadKey();
        }
    }
}
edytowany 1x, ostatnio: Mistzzz
FR
  • Rejestracja:ponad 11 lat
  • Ostatnio:ponad 11 lat
  • Postów:2
0

Trochę nie widzę sensu implementacji ICollection, skoro Twoja klasa działa jak tablica a nie kolekcja. Zła wartość zwracana przez właściwość Count - ilość elementów w Twojej tablicy to nie rozmiar Hashtable. W Hashtable są przechowywane tylko wartości inne od domyślnych, a rzeczywistych elementów może być przecież więcej. Śmiał bym nawet twierdzić że ilość elementów to rozmiar tablicy. Przez to w enumeratorze w metodzie MoveNext jest błąd i w rzeczywistości może nie przejść po wszystkich elementach po których przejść powinien. Ja bym zrezygnował z interfejsu ICollection i zamiast tego dał sam IEnumerable<T>. Albo zmień trochę koncepcję swojej klasy żeby działa jak lista a nie jak tablica.

n0name_l
Tablica nie jest kolekcja?
DU
Wszystko zalezeyjak chemy klasy uzywac. Jezeli kod uzywajacy naszej implementacji ma zarzadzacelementami ale nie widzziec nic o tym ze jest to nasza klasa czy np List to wtedy interfejs ICICollection sie przydaje. Nalezy sie tylko zastanowic nad tym co piszem Frizzz. Metoda Remowe tez jest do zastanowienia bo powinna normalnie "przesowac elemnety" Proponuje dazyc do implementacji takiej ktorej testy przejda tak samo dla obiekty naszej klasy jak i dla instacji List <T>
Azarien
  • Rejestracja:ponad 21 lat
  • Ostatnio:około 10 godzin
0

Kopiujesz kod bez zrozumienia. Miałeś już dobre rozwiązanie enumeratora z yield return, a skopiowałeś przestarzały i przekombinowany kod z osobną klasą enumeratora.

MI
  • Rejestracja:ponad 11 lat
  • Ostatnio:ponad 8 lat
  • Postów:28
0

Owszem skopiowałem kod i przerobiłem na swój. Nie do końca go rozumiałem, a chciałbym żeby zrozumieć i się nauczyć. No ale zaczęło mi działać i dlatego zapytałem czy dobrze zrobiłem, żeby wiedzieć czy dobrze zrobiłem i żeby przeanalizować sobie poprawny kod. Nie miałem z tym wcześniej do czynienia a będę potrzebował w innym projekcie wykorzystać dlatego w ten sposób się próbuję uczyć. Ale dzięki Wam wielkie za uwagi na ten temat :)
Dobre rozwiązanie to masz na myśli kod podany przez n0name_l z użyciem yield return z użyciem IDictionary? We wcześniejszej wersji programu koledzy z forum zasugerowali użycie Hashtable i dodanie IEnumerable. Próbowałem to rozwiązać używając IEnumerable<T> ale wyczytałem że foreach potrzebuje IEnumerable bez <T> i się jakoś zamotałem z tym że nie wiedziałem jak to rozwiązać. Dlatego próbowałem to zrobić na ICollection, co się udało ze skutkiem jak powyżej. Czym w ogóle różni się użycie Dictionary od Hashtable? Czyli mówisz że lepiej wykorzystać kod podany przez n0name_l?

n0name_l
Dodaj do mojego kodu implementacje IEnumerable&lt;T&gt; jak pisal @Azarien i bedzie chyba okej.
DU
Klasa Dictionary operuje na object jako klucz i wartosc, natomiast Dictionary Jest generyczna (cos jakszablon klasy w C++). Tak samest z IEnumerable i IEnumerable <T> pierwszy operuje na object jako elemnty drugi na tym co okreslamy jako typ T. W zadaniu okreslono ze implemntacja tablicy ma byc generycznat wiec powinnismy zaimplementowac generyczny interfjes wyliczeniowy wiec dobrym pomyslem jest uzycie generycznego hashtable. Od .net 2.0 nawet int [] jest typu IEnumerable<int> wiec dobra praktyka jest aby kazda klasa kolekcji implementowala ten interfejs + IEnumerable
DU
Mala korekta hastable operuje na obiektach Dictionary jest generyczne. Wciaz sie ucze pisac na tablecie ;) sorki
madmike
@dubcio! do postów z takimi wypowiedziami - komentarze są do krótkich uwag...
MI
@dubcio czyli rozumiem że lepiej powinienem użyć Dictionary w takim wypadku?
MI
  • Rejestracja:ponad 11 lat
  • Ostatnio:ponad 8 lat
  • Postów:28
0

Udało mi się zrobić z Hashtable oraz na Dictionary:

Kopiuj
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
 
namespace Generic_Tab
{
    class GenericArray<T> : IEnumerable<T>
    {
        //------- Hashtable -------------------------------------------------
        private Hashtable array;        //Deklaracja tablicy typu Hashtable
        private uint size;
 
        public GenericArray(uint size)
        {
            array = new Hashtable();    //Utworzenie tablicy konstruktorem
            this.size = size;
        }
        public T this[uint i]
        {
            get
            {
                return array.ContainsKey(i) ? (T)array[i] : default(T);
            }
            set
            {
                if (i >= size || i < 0)
                    throw new IndexOutOfRangeException();
                if (!value.Equals(default(T)))
                {
                    array[i] = value;
                }
                else if (array.Contains(i))
                {
                    array.Remove(i);
                }
            }
        }
        public IEnumerator<T> GetEnumerator()
        {
            foreach (DictionaryEntry entry in array)
            {
                yield return (T)entry.Value ;
            }
        }
        IEnumerator IEnumerable.GetEnumerator()
        {
            foreach (DictionaryEntry entry in array)
            {
                yield return (T)entry.Value;
            }
        }
        
        //-------- Dictionary ---------------------------------------------------
        /*private Dictionary<uint, T> array;
        private uint size;
 
        public GenericArray(uint size)
        {
            array = new Dictionary<uint, T>();    //Utworzenie tablicy konstruktorem
            this.size = size;
        }
 
        public T this[uint index]
        {
            get 
            {
                return array.ContainsKey(index) ? (T)array[index] : default(T); 
            }
            set 
            {
                if (index >= size || index < 0)
                    throw new IndexOutOfRangeException();
                if (!value.Equals(default(T)))
                {
                    array[index] = value;
                }
                else if (array.ContainsKey(index))
                {
                    array.Remove(index);
                }
            }
        }
 
        public IEnumerator<T> GetEnumerator()
        {
            foreach (KeyValuePair<uint, T> keyValuePair in array)
            {
                yield return keyValuePair.Value;
            }
        }
        IEnumerator IEnumerable.GetEnumerator()
        {
            foreach (KeyValuePair<uint, T> keyValuePair in array)
            {
                yield return keyValuePair.Value;
            }
        }*/
        //----------------------------------------------------------------
    }
 
    class Test
    {
        //Przykład uzycia i obsługi tablicy generycznej
        static void Main(string[] args)
        {
            uint size = 6;
            //--------------------------------------------------
            GenericArray<int> intArray = new GenericArray<int>(size);
            
            for (uint c = 0; c < 4; c++)            //Wstawienie wartości do tablicy
            {
                intArray[c] = (int)(c*c);
            }
            intArray[4] = 0;
            intArray[5] = 7;
 
            Console.Write("\nTablica int  = [ ");   //Pobranie wartości do wyświetlenia
            foreach (int s in intArray)
            {
                Console.Write(s + " ");
            }
            Console.Write("]\n");
 
            //--------------------------------------------------
            GenericArray<char> charArray = new GenericArray<char>(size);
 
            for (uint c = 0; c < size; c++)              //Wstawienie charów do tablicy
            {
                charArray[c] = (char)(c + 97);
            }
 
            Console.Write("Tablica char = [ ");
            foreach (char s in charArray)
            {
                Console.Write(s + " ");
            }
            Console.Write("]\n");
            Console.ReadKey();
        }
    }
}

http://ideone.com/3cKhoq#

Jednak po wydrukowaniu na ekran wyświetla mi elementy z tablicy od końca, zamiast

Tablica int = [ 1 4 9 7 ]
to
Tablica int = [ 7 9 4 1 ]
Czym to może być spowodowane?

Wersja z Dictionary jest zakomentowana. Po odkomentowaniu działa prawidłowo, wszystko jest wyświetlane w takiej samej kolejności, tj.

Tablica int = [ 1 4 9 7 ]

edytowany 3x, ostatnio: Mistzzz
Zobacz pozostały 1 komentarz
MI
Ok no to rozumiem. Powiedz mi jeszcze jedno, bo mam zrobione że jeżeli element jest typu default(T) to go nie wstawiam do tablicy i tak też się dzieje. Przy odczycie z tablicy sprawdzam czy istnieje Key(index) i jeśli tak to zwracam wartość Value, a jeśli nie to zwracam default(T). Uzywając wyświetlenia w sposób Console.Write(intArray[4]); dla indexu w Value 0 wyświetla na ekranie 0, natomiast w pętli foreach nie robi tego tylko wyświetla elementy będące w tablicy. Jak to rozwiązać żeby w pętli wyświetlało default(T), bo właśnie nad tym myślę teraz?
MI
Generalnie mam e metodzie get zaimplementowane to o czym wyżej pisałem. No ale nie wiem jak skorzystać z tego korzystając z foreach i IEnumerable<T>.
DU
Wrzuciłem w kolejnym poście implementację i jakby zebranie tego o czym pisałem w rożnych miejscach tego wątku. Trochę specjalnie zostawiłem kilka luk :) ale patrząc na twój dotychczasowy kod szybko się z nimi uporasz :) to takie zabezpieczenie przed kopiuj/wklej ;) Swoją drogą ciekawe zadanie czy to z jakiejś rekrutacji? Studiów?... niezłe do nauki obsługi kolekcji w jakimś języku.
MI
Zassałem to co wrzuciłeś, wielkie dzięki. Postaram się to przeanalizować i dokończyć samemu, jeśli będę miał jakieś pytania to się tutaj odezwę. Widzę że robiłeś to w VS2010, ja używam jeszcze 2005 (bo mam za słaby sprzęt, a do nowego się przymierzam dopiero) i nie mam LINQ doinstalowanego jeszcze, więc nie będą działać komendy z LINQa. Ale to już jutro się tym zajmę. Zadanko dostałem od kumpla, a on kiedyś dostał je na jakiejś rekrutacji. Chcę się nauczyć C# lepiej bo znam tylko podstawy, a poza tym chcę zrobić aplikację Klient-Server w C# :)
DU
No to z VS2005 bedzie ciezko z odpaleniem. Moze 2008 pojdzie a ma LINQ (contains i any ni bedzie dzialac) w wystarczajacym na start zakresie. Powodzenia i jak co to uderzaj.
DU
  • Rejestracja:ponad 11 lat
  • Ostatnio:ponad 11 lat
  • Postów:25
0

Zamiast tak odpisywać w komentarzach postanowiłem dodać swój wpis i kawałek kodu.

Podczas pisania założyłem że:

  • index nie może być mniejszy niż 0
  • pierwszy element wskazuje ma index 0
  • skoro ma być to tablica rzadka wiec iterowanie powinno również zwracać domyślne wartości dla niezdefiniowanych elementów
  • iterowanie jest wykonywane do elementu o najwyższym indeksie
  • można zdefiniować jaka jest domyślna wartość, jeżeli nie brana jest domyślna dla typu elementów kolekcji
    Zastosowanie IEnumerable<T> ma na celu ujednolicenie sposobu poruszania się po kolekcji (element po elemencie, od początku do końca) co wykorzystano w klasach LINQ.
Kopiuj
    public class GenericArray<T>
        : IEnumerable<T> // co zonacza że również IEnumerable dla kompatybilności z .NET 1.1
    {
        public GenericArray()
            : this(default(T)) // domyślna wartość dla typu (np. int -> 0, dla klass null) 
        {
        }

        // Pozwala określić domyślną wartość dla elemntów pustych
        public GenericArray(T defaultValue)
        {
            _defaultValue = defaultValue;
        }
        private readonly T _defaultValue;
        private readonly IDictionary<int, T> _items = new Dictionary<int, T>();
        private int _itemsCount; // ile mamy elementów (nie jest to ilość elementów w _items)

        public T this[int index]
        {
            get
            {
                return _items.ContainsKey(index)
                           ? _items[index] // jeżeli nasza wewnętrza tabblica przechwuje wartość dal wskazanj pozycji
                           : _defaultValue; // jezeli jej nie ma
            }
            set
            {
                if (index <= 0)
                {
                    throw new IndexOutOfRangeException();
                }

                // Sprawdzamy czy wartość jest różna od domyślnej.
                if (!EqualityComparer<T>.Default.Equals(_defaultValue, value))
                {
                    _items[index] = value;
                    return;
                }

                // Usuwamy jeśli jest
                if (_items.ContainsKey(index))
                {
                    _items.Remove(index);
                }
            }
        }


        public IEnumerator<T> GetEnumerator() // uzycie yield return "automatycznie tworzy" enumerator
        {
            var index = 0;
            foreach (var item in _items.OrderBy(k => k.Key)) // Wykorzytsanie LINQ do sortowania IDictionarty po kluczu
            {
                while (index < item.Key)
                {
                    index++;
                    yield return _defaultValue;
                }
                index++;
                yield return item.Value;
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator(); // IEnumerator<T> tez implemntuje IEnumerator :)
        }
    }

Wykorzystałem tu kilka elementów .NET

  • !EqualityComparer<T>.Default w ten sposób nie interesuje mnie jak porównywane są elementy czy typ T.
  • OrderBy to metoda LINQ do sortowania. W końcu Dictionary to tez kolekcja a każda kolekcja jest IEnumerable<jakiś typ>, to jakiś typ to KeyValuePair<TKey, TValue> ... jeszcze więcej generyków
  • yield return ułatwia zwracanie elementów kolekcji (gdy metoda ma zwracać IEnumerable<jakis typ="typ">). Kompilator generuje dla nas kod enumeratora
    Poniżej chciałem pokazać kilka przykładów użycia LINQ (.NET 4.0). Żeby nie było ściemy do operacji na kolekcji używam osobnej zmiennej tylko typu IEnumerable<int>:
    var instance = new GenericArray<int>(); // domyślna wartośc pusta
Kopiuj
            var instance = new GenericArray<int>(); // domyślna kolekcja pusta
            var instanceEnumerable = (IEnumerable<int>)instance;

            // brak elemntów
            AssertThat(instanceEnumerable.Count() == 0);
            AssertThat(!instanceEnumerable.Any()); // to samo co wyżej ale bez zliczania wszytkich elementów

            instance[9] = 10;
            AssertThat(instanceEnumerable.Count() == 10);
            AssertThat(instanceEnumerable.Any());
            AssertThat(instance[9] == 10);
            AssertThat(instance[4] == 0);
            AssertThat(instanceEnumerable.ElementAt(9) == 10);
            AssertThat(instanceEnumerable.Skip(9).First() == 10);
            AssertThat(instanceEnumerable.ElementAt(4) == 0);
            AssertThat(instanceEnumerable.Skip(4).First() == 0);
            AssertThat(instanceEnumerable.Contains(10)); // Dzieki LINQ
            AssertThat(instanceEnumerable.Last() == 10);

            instance[4] = 5;
            AssertThat(instanceEnumerable.Count() == 10);

            var list = instance.ToList(); // Zapisanie danych w nowej tablicy (wszystkich, nawet pustych)
            AssertThat(list.Count == 10);
            AssertThat(list[9] == 10);
            AssertThat(list[4] == 5);

            instance[9] = 0;
            AssertThat(instanceEnumerable.Count() == 5);
            AssertThat(instanceEnumerable.Any());
            AssertThat(instance[4] == 5);

            instance[4] = 0;
            AssertThat(instanceEnumerable.Count() == 0);
            AssertThat(!instanceEnumerable.Any());

gdzie meteoda AsssertThat to taki prosty silnik testujący aby nie wprowadzać żadnego farmeworka i nie gmatwać przykładu:

Kopiuj
        public void AssertThat(bool isValid)
        {
            if (!isValid)
            {
                const string message = "Assertion failed.";

                Console.WriteLine(message);
                throw new Exception(message);
            }
        }

Implementacja ma pewne luki, ale one wynikają z założeń. Np. taka operacja przejdzie nawet na nowo stworzonej instancji.

Kopiuj
AssertThat(instance[999] == 0);

ale

Kopiuj
instance.Count() = 0);

No tak Count() (metoda LINQ nie ta z ICollection) zwraca nam 0 a tu mamy 1000 element. Tak działa przedstawiona implementacja. Nie ma definicji rozmiaru tablicy a Count() przechodzi po wszystkich elementach i je zlicza.
No własnie... IEnumerable<T> pozwala nam przechodzić po kolekcji elementów niezależnie od implementacji... ale tylko tyle nie pozwala nam zarządzać ta kolekcją. Do tego jest służy interfejs ICollection<T> pozwalając nam Dodawać, Usuwać, podmieniać czy dowiedzieć się ile mamy elementów. O ile chcielibyśmy aby nasza klasa mogła zastąpić na przykład klasę List<T> wtedy powinniśmy się nad zastanowić nad jej implementacją. Wspominając o tym interfejsie bardziej chciałem wskazać dalszy kierunek rozwoju implementacji a przy okazji rozważań o zachowaniu implementacji (np to zliczanie). "Dobra praktyka" mówi tylko o tym że każdy typ reprezentujący kolekcję powinien implementować IEnumerable<T>.

Mistzzz'u, dobrze ze piszesz kod pokazujący testowanie, to też "dobra praktyka". Może zainteresuje Cię nUnit lub xUnit albo MSTest. To jak je napisałem to takie małe demo aby je nieco zautomatyzować automatyzować ;).

edytowany 1x, ostatnio: dubcio
Zobacz pozostałe 3 komentarze
DU
@Frizzz to tak specjalnie zostawiłem bez ograniczenia co napisałem gdzieś w komentarzu do wcześniejszego posta. Przykład generalnie można rozwijać i rozwijać. Nad pomysłem który przedstawiasz też rozmyślałem. Jednak przy implementacji doszedłem do wniosku że niewiele to pomoże. Bo wtedy musiałbym iterując sprawdzać każdy element czy jest czy go nie ma. Co przy np 1000 elementów to 1000 wywołań. tablica jest rzadka wiec bardzo dużo elementów jest tak naprawdę pustych.
DU
@Frizz tak mi się jeszcze przypomniało w .NET 5 mają wejść nowe implementacje IDictionary. Np taka co ma sortowane klucze. Czyli OrderBy było by wtedy niepotrzebne. A narzut na sortowanie byłby w momencie dodawania/usuwania kluczy.
DU
O i jest byk w kodzie :) w seterze powinno być sprawdzenie index < 0, zerowy element to pierwszy element :)
MI
W takim razie jak podaję rozmiar tablicy np 10, wstawiam kilka elementów np 5, pozostałe 5 są default. W jaki sposób wyświetlić całą tablicę z elementami !default i =default w pętli foreach? Używam VS2005 więc LINQ odpada na chwilę obecną.
DU
w moim przykładzie nie określasz rozmiaru tablicy. Ale zakładając ze jest albo ustawiając element 10 (index 9) na wartość nie domyślną wyglada t tak: var array = new GenericArray<int>(); // dodajemy elementy foreach (var item in array) { Console.WriteLine(item) }
Azarien
  • Rejestracja:ponad 21 lat
  • Ostatnio:około 10 godzin
1

yield return
ułatwia zwracanie elementów kolekcji (gdy metoda ma zwracać IEnumerable<jakis typ="typ">). Kompilator generuje dla nas kod enumeratora

yield return sprawia wrażenie jakby wypychało jedną wartość do docelowej kolekcji, czyli czegoś w rodzaju

Kopiuj
result[i++] = wartość;

ale robi co innego: wychodzi z funkcji (w końcu to return...) zwracając podaną wartość, po czym wykonuje się jedna iteracja pętli foreach na tej wartości.
na początku następnej iteracji foreacha funkcja iteratora jest wznawiana od miejsca gdzie było yield return.
Czyli dla przykładowej kolekcji {2, 3, 4, 5} i takiej pętli:

Kopiuj
foreach (int i in tab)
{
    Console.WriteLine(i);
}

prawdziwa kolejność instrukcji byłaby następująca:

Kopiuj
yield return 2;
Console.WriteLine(2);
yield return 3;
Console.WriteLine(3);
yield return 4;
Console.WriteLine(4);
yield return 5;
Console.WriteLine(5);

Można powiedzieć że iterator działa „na żywo” podczas pracy pętli foreach, a nie że najpierw zbiera elementy na których potem będzie jechać pętla.

DU
Dobrze to wyłożyłeś. Ja użyłem skrótu myślowego bo w zasadzie efekt jest ten sam. Pamiętając o tym że mamy poruszanie się po elementach z wyliczaniem ich niejako na bieżąco.
MI
  • Rejestracja:ponad 11 lat
  • Ostatnio:ponad 8 lat
  • Postów:28
0

No to po kilku dniach przerwy udało mi się zrobić program tak aby działał i wszystko wyświetlał jak należy. Poniżej kod:

Kopiuj
//-------- Dictionary ---------------------------------------------------
        private readonly Dictionary<uint, T> _array;
        private readonly T _defaultValue;
        private uint _size;
 
        public GenericArray(uint size, T defaultValue)
        {
            _array = new Dictionary<uint, T>(); //Utworzenie tablicy konstruktorem
            this._size = size;
            _defaultValue = defaultValue;       //Określenie wartości dla elementów pustych
        }
 
        public T this[uint index]
        {
            get 
            {
                return _array.ContainsKey(index) ? (T)_array[index] : _defaultValue; 
            }
            set 
            {
                if (index >= _size)
                    throw new IndexOutOfRangeException();
 
                // Czy wartość jest różna od domyślnej, jeśli tak to wstawiamy
                if (!EqualityComparer<T>.Default.Equals(_defaultValue, value)) 
                {
                    _array[index] = value;
                    return;
                }
 
                // Jeśli jest jakaś wartość to ją usuwamy
                else if (_array.ContainsKey(index))
                {
                    _array.Remove(index);
                }
            }
        }
 
        public IEnumerator<T> GetEnumerator()
        {
            SortedDictionary<uint, T> sortKey = new SortedDictionary<uint,T>(_array);
            uint index = 0;
            uint help = 0;
 
            foreach (KeyValuePair<uint, T> item in sortKey)
            {
                while (index < item.Key)
                {
                    index++;
                    help = item.Key ;
                    yield return _defaultValue; //Zwrócenie wartości domyślnych typu T (niezapisane w tablicy)
                }
                index++;
                help = item.Key;
                yield return item.Value;        //Zwrócenie wartości z tablicy generycznej (zapisanych w niej)
            }
 
            for (index = help + 1; index < _size; index++)
            {                                   //Zwrócenie wartości domyslnych typu T niezapisanych w tablicy
                yield return _defaultValue;     //dla przypadku, gdy ostatni niedomyśly element wcale nie jest
            }                                   //ostatnim elementem w tej tablicy 
        }
 
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
        //----------------------------------------------------------------
DU
Gratuluju... teraz juz łatwo będzie dorobić ICollection jeśli chiałbys poza enumeratorami mieć uogulniony sposób operacji na danych w kolekcji.
MI
Heh, dzięki... Podrzuciłbyś jakiś dobry, godny polecenia i wzorowania się przykład z użyciem ICollection?
DU
Niestety nie mam nic takiego. Sugeruje spojrzec na to w nastepujacy sposób. Majc klase z przeladowanym operatorem [] osiagnelismy wymagania. Jednak chcac uzywac tej klasy bedziemym z nia zwiazani... to niezbyt uzyteczne. Dlatego tez dodajc IEnumerable pozollilismy iterowac po naszej tablicy. IColection pozwoli zarzadzac zawartoscia bez koniecznosci wiazania sie z implementacja. Tam sa takie metody jak add, remove, get a to wszytko jest juz w operatorze []. Sproboj zaimplementowac to pociagniemy to dalej ;)
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)