skrócenie instrukcji switch i dyskusja o hash:)

1

Wstęp: dla uproszczenia - przykładowe używane obiekty

string Zmienna = "Obraz2";
Image Obraz1 = Image.FromFile("D:\\Obraz1.bmp");
Image Obraz2 = Image.FromFile("D:\\Obraz2.bmp");

Treść właściwa: chciałbym wyświetlić na ekranie obrazek na pozycji, powiedzmy, 50, 50, ale tak żeby nazwa tego obrazka była jednakowa z wartością zmiennej. Używam do tego instrukcji switch:

switch (Zmienna)      
                {
                             case "Obraz1": g.DrawImage(Obraz1, 50, 50); break;
                             case "Obraz2": g.DrawImage(Obraz2, 50, 50); break;
                }

Tylko że tych możliwości jest w moim programie w rzeczywistości o wiele więcej, co powoduje straszny, niepotrzebny bałagan. Czy jest opcja żeby wyświetlić tą instrukcję jako:
g.DrawImage(|Obraz nazywający się tak jak wartość zmiennej 'Zmienna'|, 50, 50);
Jeśli ktoś wie i pomoże mi będę bardzo wdzięczny.

PS.Szukałem rozwiązania w dokumentacji, ale nie znalazłem (nie mówię że nie ma - to tylko ja tak kiepsko szukam).

0

użyj tablicy haszującej zamiast switcha

0
Dictionary<string, Image> imageDictionary = new Dictionary<string, Image>();

imageDictionary["o1"] = Image.FromFile("Obraz1.bmp");
imageDictionary["o2"] = Image.FromFile("Obraz2.bmp");
...

string zmienna = "o1";
g.DrawImage(imageDictionary[zmienna], 50, 50);

Przy okazji zmniejsza to złożoność przeszukiwania z O(n) do O(logn).

0

Hass, jeśli już to O(1), Dictionary to haszmapa.

1

Hmm... może tyle już powinienem wiedzieć, ale:

kod "od razu po wklejeniu" wyrzucał mi parę błędów, więc zmieniłem, jak radził manfredek imageDictionary["o1"] = Properties.Resources.O1; (Invalid token '[' in class, struct, or interface member declaration | Identifier expected | Invalid token '"o1"' in class, struct, or interface member declaration | Invalid token ';' in class, struct, or interface member declaration) na imageDictionary O(1) = Properties.Resources.O1; (Type expected | Invalid token ';' in class, struct, or interface member declaration). W nawiasach podałem wyświetlane przez kompilator błędy.

Zrobiłem więc poprawkę na własną rękę - imageDictionary o1 = Properties.Resources.O1; tym razem wyświetla się jeszcze inny błąd -'Program.Form1.ImageDictionary' is a 'field' but is used like a 'type' .
Show Error Help jak zwykle nie mówi nic czego bym się nie domyślił (The compiler detected a situation in which a construct was used in some erroneous way or a disallowed operation was tried on a construct. - tyle to wiem).

Co więc zrobić? Zapytałem MDSN (Google zresztą też) o "dictionary" i doszedłem do czegoś takiego:

        Dictionary<string, string> openWith = 
            new Dictionary<string, string>();

        // Add some elements to the dictionary. There are no 
        // duplicate keys, but some of the values are duplicates.
        openWith.Add("txt", "notepad.exe");
        openWith.Add("bmp", "paint.exe");
        openWith.Add("dib", "paint.exe");
        openWith.Add("rtf", "wordpad.exe");

Tylko że nieszczęsne imageDictionary nie chce się do tego zastosować:
Invalid token '(' in class, struct, or interface member declaration | Invalid token ')' in class, struct, or interface member declaration | Invalid token ',' in class, struct, or interface member declaration.

Co robię źle? Dodam że pracuję na Microsoft visual C# 2008 expres edition.

Proszę o jakąś pomoc czy chociaż wskazówkę :)

1

Aaaach... Nareszcie :)
W takich przypadkach mówi się "najprostsze rozwiązania są najlepsze" :P

Najpierw skopiowałem cały kod z MSDN. Działało, czyli to nie wina potłuczonego kompilatora tylko potłuczonego programisty. [glowa]

Otóż to całe imageDictionary trzeba wpisywać w private void Form1_Paint (tam gdzie zamierzam jej używać) a nie zaraz za public partial class Form1 : Form (gdzie ja zazwyczaj wpisuję wszystkie zmienne)!!! Czego ustalenie zajęło mi 2 godziny!!!

Więc Happy End?
Nie. [glowa]

Jeśli ktoś uważnie przeczytał ten post i skonfrontował go z moimi oczekiwaniami zauważy że to mi nic nie daje (prawie). Chciałem bowiem "odchudzić" Form1_Paint, a to mi się udało tylko częściowo (tam gdzie kiedyś zajmowało miejsce switch, teraz zajmuje Dictionary).

Edit: No dobra, trochę histeryzuję, bo miejsca rzeczywiście zajmuje mniej, kod jest czytelniejszy, i zmiennych obrazkowych też nie muszę tworzyć. Tym niemniej...

Edit2: Chociaż jest jeszcze jedna wada, którą wykryłem podczas debugowanie - program za każdym wywołaniem Form1_Paint musi utworzyć wszystkie Dictionaries, a to powoduje NIEZIEMSKIE miganie programu. Bardziej niż ustawa nakazuje.

Ma ktoś pomysł co z tym zrobić?

0

Jak Ty mogłeś wstawić coś takiego do Form_Paint :/

I jak kod nie jest lepszy, skoro jest. Nie dość, że czytelniej, to nie ma niepotrzebnego switcha.

I co niby nie działa?

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace ImageDictionaryTest
{
    public partial class Form1 : Form
    {
        //słownik jest polem prywatnym klasy, no nie?
        //od razu go inicjalizujemy
        private Dictionary<string, Image> imageDictionary = new Dictionary<string, Image>();

        public Form1()
        {
            InitializeComponent();
            //np. w konstruktorze formatki ładujemy grafiki do słownika
            this.imageDictionary["o1"] = Image.FromFile(@"C:\Temp\o1.jpg");
            this.imageDictionary["o2"] = Image.FromFile(@"C:\Temp\o2.jpg");
        }

        //prosty przykład - button1 rysuje pierwszy obrazek, button2 drugi
        private void button1_Click(object sender, EventArgs e)
        {
            Graphics g = this.CreateGraphics();
            g.DrawImage(this.imageDictionary["o1"], 50, 50);
        }

        private void button2_Click(object sender, EventArgs e)
        {
            Graphics g = this.CreateGraphics();
            g.DrawImage(this.imageDictionary["o2"], 50, 50);
        }
    }
}

Cała filozofia.

1

Hmm, masz rację, dzięki.

Próbowałem zrobić coś like that, ale nie wpadłem na to aby oddzielić "private Dictionary<string, Image> imageDictionary = new Dictionary<string, Image>();" od this.imageDictionary["o1"] = Image.FromFile(@"C:\Temp\o1.jpg"), trzymając się twoich nazw.

Wklejenie całości tam gdzie dałeś new Dictionary<string, Image>(); skutkowało lawiną błędów, a tam gdzie this.imageDictionary["o1"] - niedostępnością w innych rejonach programu :)

Tym niemniej - dzięki. Wszystkim. ;-P

0
manfredek napisał(a)

Hass, jeśli już to O(1), Dictionary to haszmapa.
Podaj mi algorytm realizujący tablicę haszującą z czasem wstawiania i pobierania O(1).

MSDN napisał(a)

Retrieving a value by using its key is very fast, close to O(1)
Czyli nie O(1) :>

0

@Hass: O oznacza zlozonosc. Wyszukiwanie w prawie kazdej tablicy haszujacej uzywajacej sensownej funkcji ma zlozonosc O(1). Wyrazenie 'zblizone do O(1)' jest z natury nieprecyzyjne. Zlozonosc jest albo taka sama (czyli O(1) wlasnie) albo inna (np. O(n)). W tym wypadku chodzilo pewnie o to, ze obciazenie rzeczywiscie jest prawie rowne 1. Bo zauwaz, ze stala ilosc 1000 operacji to tez O(1) ;).

0

poreflektorujcie sobie Dictionary i sprawdzcie sobie jak sie ma to ich O(1).
on bazuje na (ref)equals i gethashcode()->int definiowanych w Object. kazdy jeden obiekt wrzucany sam sobie definiuje funkcje hasujaca, wiec sami jestescie stanie napisac w pare minut taki obiekt, ktory jak wrzucicie w Dictionary i to O() bedzie = n.

bliskie O(1) oznacza zlozonosc statystyczna. dla pesymistycznego przypadku, bedzie duzo wyzsza. nie istnieje OGÓLNY algorytm hashowania danych dajacy gwarantowane O(1) ani nawet bliskie 1. optymistyczne, albo statystyczne, albo - dla konkretnych znanych danych -- istneja. Dlatego tez Dictionary<> NIE jest O(1), gdyz on jest ogolny. Dictionary<string> nie jest O(1) gdyz dane wsadzane w niego nie pozwalaja stworzyc perfekcyjnego hasha (dane)->int, gdyz mozliwa wielkosc bitowa stringa >> bitowosci int'a na ktorym zapisany jest hash. ale juz taki Dictionary<short> albo Dictionary<char> jak najbardziej MOZE byc O(1).

0

Tablice haszujące z definicji opierają się na funkcji haszującej. Funkcja haszująca służy do tego, by przekształcić dużą ilość danych w skrót. W konsekwencji, zbiór skrótów jest mniejszy niż zbiór dopuszczalnych wartości i dlatego w tablicach haszujących występują kolizje, które to niszczą wspaniałą złożoność O(1).

Tak jak mówi quetzalcoatl - można skonstruować tablicę haszującą opartą na kluczu o małym zakresie wartości (np. char). Wtedy, wykluczając kolizje, można uzyskać O(1) dla wszystkich operacji. No ale to przypomina bardziej zwykłą tablicę o stałej wielkości z prostą różnowartościową funkcją klucz->indeks (ciężko to wtedy nazwać funkcją haszującą).

1 użytkowników online, w tym zalogowanych: 0, gości: 1