niedziela, grudnia 05, 2010

Androidujemy sobie ;-) ale tak niemrawo

Tak sobie od niechcenia piszę na Androida "tablicę" stałych fizycznych i matematycznych i co rusz trafiam na problemy ;-)

Przed chwilą miałem taki, jak sprawić by wartości stałych trzymać w zewnętrznym pliku, ale by nazwy dla nich móc trzymać w pliku z zasobami?

Czyli w pliku ze stałymi trzymam coś takiego:
speed_light|c|299792458|m/s

A w pliku z łańcuchami dla angielskiego coś takiego:
<string name="speed_light">speed of light (in vacuum)</string>


Ale w kodzie gdy używam getString(int) to jako wartość łańcucha muszę wrzucić int nie łańcuch... i jak przekształcić to moje speed_light w wartość int odpowiadającą stringowi przypisanemu do speed_light?
Nie wiem czy istnieje jakaś Androidowa wersja, ja to na razie zrobiłem takim oto hackiem ;-)
// very ugly hack
Field field = R.string.class.getField(name);
name = getString(field.getInt(null));

Czyli użyłem refleksji...
Nie jestem z tego dumny bo to brzydkie. Ale działa ;-) [OK, w celu optymalizacji można wyciągnąć wszystkie pola z R.strings i wrzucić do Map<String,Integer>, ale nadal będzie to brzydkie, z tym że ciut szybsze].

Kolejny problem jest już nie Androidowy.
Fajnie by było móc dodać ficzer do konwersji do innych jednostek, ale tu jest problem...
Załóżmy, że jakoś by wprowadzono, że tą prędkość światła trzeba przeliczyć na mile na dzień...
Ja wiem, że trzeba to zrobić tak, że znam prędkość światła w m/s, czyli najpierw przeliczam ją na mile na sekundę, czyli mnożę razy 0.000621371192, mam więc 186282.397 mil na sekundę, teraz mnożę całą tą liczbę razy 86400 (czyli liczbę sekund w dobie) i dostaję 1.60947991×1010 mil na dobę.

Ale jak komputer ma to wykminić? ;-)

Jeżeli to m/s będzie zapisane jako m*s^(-1), a jednostka docelowa jako miles*day^(-1) to jeśli znane będą przeliczniki "1 m w milach" i "1 s w dniach" to można by...
Bo wtedy wynik powinien być taki:
Wartość w milach/dzień = Wartość w m/s * "1 m w milach"/"1 s w dniach"
Ale napisanie takiego parsera to już nie jest hop-siup... tzn. chyba ;-)

Nie mam na to pomysłu na razie :-(

Nawet jak wymyślę sposób dojdzie do tego problem zaokrąglania chyba, bo nie mam gwarancji, że każde takie automatyczne przekształcenie pójdzie po najbardziej "optymalnej" trasie w której wynik końcowy będzie najbardziej podobny do wyjściowego jeśli chodzi o dokładność. Można użyć BigDecimal'a, ale nadal będzie to chyba nie do końca pewne.

No i jest jeszcze ostatni problem ;-) Jak ładnie wyświetlić jednostki?
Np. przyśpieszenie to m/s2, co w "komputerowy" sposób najlepiej zapisać jako m*s^(-2).
To znów w prosty sposób można przekształcić w kod, który wyświetli coś takiego m*s-2. Bawienie się w piętrowość będzie już trudniejsze.

Ale jakąś tam zabawkę mam do pisania ;-)

Z innych pomysłów, co do których jak na razie nie mam chyba wystarczających umiejętności to program na Androida, który pozwalałby robić zdjęcia panoramiczne, ale z wykorzystaniem akcelerometru i kompasu [ogólnie tego co pozwala ustalić położenie aparatu]. Bo wydaje mi się, że to by mogło pomóc w przekształceniu istniejącego zdjęcia w taki sposób by pasowało do sąsiadów.


Podobne postybeta
Amerykański zegarek jest trudny ;-)
3 "kuchenne" sekrety
SleepAdvisor - komórka pomaga w wyspaniu się ;-)
Trygonometria trudna
Androidowe boje... ale nie takie morskie ;-)

11 komentarzy:

  1. Moze Resources.getIdentifier() pomoze na ugly hack?

    http://developer.android.com/reference/android/content/res/Resources.html#getIdentifier(java.lang.String, java.lang.String, java.lang.String)

    OdpowiedzUsuń
  2. Brzmi zachęcająco :-)

    [dodane trochę później]
    http://daniel-codes.blogspot.com/2009/12/dynamically-retrieving-resources-in.html
    Wychodzi na to, że wolniejsze to jest od hacka :-(
    Inna sprawa, że jeszcze mi się nie udało tego z pobieraniem identyfikatora użyć ;-)

    Zresztą jak się przyjrzeć kodowi odpowiedzialnemu za to Resources to on strasznie zamotany jest i rzeczywiście może działać wolniej od refleksji.

    A jak już uda mi się użyć tej Androidowej metody to sobie sam czas zmierzę ;-)

    OdpowiedzUsuń
  3. To nie jest post anonimowy (piszę go logując się przy pomocy gmail) i jest bardzo sensowny:

    Każdą wartość przechowujesz razem z wektorem wykładników podstawowych jednostek SI

    [(m,v(m)) (s,v(s)) (g,v(g)) (N,v(N)) ...]

    (tzn. takich, których nie definiuje się jako zależności pomiędzy innymi jednostkami jak np. Ps=N/(m^2)).

    - 1 metr to będzie 1*[(m,1) (s,0) (g,0) (N,0) ...]

    - 200 gram to będzie 200*[(m,0) (s,0) (g,200) (N,0) ...]

    - 40 Ps to będzie 40*[(m,-2) (s,0) (g,0) (N,1) ...]

    Proste? Teraz wiesz np, że 1 mila to ok. 1600 [m], a jeden dzień to ok 80000 [s].
    Skoro 1[m/s] to

    1*[(m,1) (s,-1) (g,0) (N,0) ...]

    to chcąc przeliczyć na mile na dzień, konwertujesz na liczbę w postaci [mila dzień ...] mnożąc skalar przed wektorem przez w/w relacje podniesione do przeciwności odpowiednich potęg, czyli:
    1*(1600^(-1))*(80000^(1)) a w wektorze podmieniając jednostki czyli wychodzi, że
    50[(mila,1) (dzień,-1) (g, 0) (N,0)] czyli ok 50 [mila/dzień].

    W przypadku Pascali. Mamy 4 pascale i chcemy wiedzieć ile to funtów na cal kwadratowy. 4 [Ps] to 4*[(m,-2) (s,0) (g,0) (N,1) ...].
    1 funt to ok 4.5 newtonów, a 1 cal to około 2.5 centymetra tj. 0.025 metra (czyli ok 1/40). Liczymy i mamy:
    4*(1/1600)*0.22[(cal,-2) (s,0) (funt,1) ...]=0.00055[(cal,-2) (s,0) (funt,1) ...]
    Czyli 4 paskale to około 0.00055 funta na cal kwadratowy. Zgadza się? No oczywiście, że się zgadza.

    Z dedykacjami dla wielce oświeconego mgr. fizyki, lead developera w wielkiej, amerykańskiej korporacji od oszołoma zesłanego z braku lepszych perspektyw do pewnej marnej, żałosnej polskiej firemki.

    OdpowiedzUsuń
  4. @anonimowy - ja to chcę inaczej zrobić. Trzymać jednostki długości, jednostki czasu i inne takie razem. By móc łatwo proponować alternatywne "miary", czyli jak mam prędkość w m/s to bardzo łatwo mogę zaproponować w calach/s, milach/s i tak dalej.
    Jednostki takie jak pascale albo joule czy waty chcę trzymać jako ich definicję i po prostu "dzielić".
    Twoje podejście też jest ciekawe.

    Btw. czy Ty masz jakieś kompleksy? Bo na to wychodzi.

    OdpowiedzUsuń
  5. Przecież moja metoda właśnie to robi! Moje rozwiązanie "trzyma" tylko podstawowe jednostki, a paskale wyraża przy pomocy newtonów i metrów. Przeczytaj jeszcze raz to, co napisałem, albo jeszcze z 10 razy, bo jest to metoda opisująca dokładnie tą funkcjonalność o którą ci chodzi.
    Masz podane na tacy co przechowywać w pamięci i tyle. Dodatkowo musisz gdzieś trzymać przeliczniki jednostek niestandardowych wyrażonych w jednostkach SI czyli, że np 1mila=1600 metrów. Czego chcesz więcej?

    OdpowiedzUsuń
  6. Co do kompleksów to nie ja zacząłem używać argumentu związanego z miejscem pracy.

    OdpowiedzUsuń
  7. Nie, nie chcesz tak samo. Chcesz trzymać wektor jednostek podstawowych dla każdej wartości. To ma zaletę w razie oznaczenia poszczególnych jednostek byłyby takie same [nie będą bo zamiast m będę trzymał np. dist_m i gdzieś tam będę miał zapisane, że akurat po polskiemu to jest metr, a gdzieś indzie że dist_mile = 1851.66 dist_m.
    Dzięki temu przyśpieszenie będzie zapisane jao g=9.81 dist_m*dist_s^(-2).
    Bardziej poręczne i mniej podatne na błędy w trakcie wprowadzania niż wektory. Mniej miejsca też zajmie.

    OdpowiedzUsuń
  8. @anonimowy - ale Ty to strasznie przeżywasz, nie ja.
    Czyli kompleksy masz. Co by sugerowało, że jednak moja ocena była słuszna.

    OdpowiedzUsuń
  9. ?????????

    Robisz klasę w której trzymasz takie pola jak:

    długość, czas, masa etc.

    Każde pole jest liczbą reprezentującą wykładnik.

    Pole wykładnik to zwykłe pole liczbowe i chyba nie muszę go objaśniać.

    To w jaki sposób będziesz przetrzymywał jednostkę to już twoja sprawa,
    możesz używać antywzorca Anemic Domain Model i przechowywać
    tylko wartości wykładników, a dane na temat danej jedostki przechowywać na zewnątrz.
    Możesz przechowywać ją jako osobne pole tworząc specjalnie osobną klasę z singletonowymi obiektami dla każdej jednostki.
    Klasa ta wiedziałaby jaki jest jej przelicznik względem jednostki tej samej miary w
    układzie SI (więc jednostki takie jak metr, albo sekunda miałyby tą wartość równą 1, a mila miałaby 1600).
    Trzymasz też symbol danej jednostki (ten jest niezmienny).

    Następnie robisz słownik mapujący wektory na identyfikatory stringów w zasobach opisujących
    jednostkę w różnych językach. Każdy wektor jednostek ma referencję do instalacji tego słownika przez co potrafi, o ile istnieje, zwrócić odpowiednie oznaczenie w odpowiednim języku. Lub używasz wzorca IoC z osobnymi słownikami dla osobnych języków pobieranych ze względu na bieżącą kulturę.
    Jeżeli standardowe oznaczenie jednostki nie istnieje (np. nikt nie ma osobnej nazwy dla m/(s^2)) to metoda ta
    generuje oznaczenie jednostki w jaki sposób chcesz (goły string, albo coś ładniejszego przy pomocy jakiejś biblioteki).

    Chyba nie muszę tłumaczyć, że powinieneś także zaimplementować operacje na wektorach, takie jak np mnożenie. Implementacja taka staje się trywialna. Wykładniki jednostek dodajesz i tyle. Dzielenie na odwrót.

    zakładam, że najlepiej by było jakbyś zawsze trzymał jednostki w formacie SI, a konwertował je na inne tylko w celach prezentacji. Jeżeli się uprzesz, że jednostka może być dowolna to twoja sprawa i będziesz musiał robić dodatkowe przeliczenia w trakcie działań na wektorach.

    To jest podatne na błędy? Mam jedno pytanie. Czy pisałeś kiedyś jakąś aplikację inżynierską jak np. sterowanie samochodem, naprężenia na zbrojeniach, przenikalność termiczna, albo cokolwiek innego??

    OdpowiedzUsuń
  10. Wprowadzanie danych podatne na błędy, co masz wyżej napisane. Bo wystarczy się przesunąć o 1 i już zaczną wychodzić bzdury.
    Łatwiej mi wpisać dist_m*time_s^(-2) [btw. wyżej napisałem dist_s ;-)] niż wektory.

    Same wektory lepiej byłoby generować na podstawie wpisanych jednostek niż wpisywać je w tej postaci "z palca".
    Bo sam program to pikuś, przeliczanie jednostek to ciut bardziej złożony pikuś, najbardziej mozolne będzie wpisywanie danych i to musi być najprostsze do wykonania bo inaczej cała reszta będzie nieważna.
    W ostateczności gotowy program może nie oferować konwersji, ale dane musi w sobie mieć i to w takiej postaci by były w razie czego możliwe do łatwego wykorzystania.
    Dlatego nie jestem nawet pewien tego czy na początku nie przechowywać ich jako zapisanych po prostu jako m*s^(-2). Jak będę potrzebował konwersji to z automatu można przerobić większość z nich na dist_m i podobne, by później wyłapać ręcznie błędne konwersje.

    A chyba się zgodzisz, że łatwiej wpisać m*s^(-2) czy nawet wredniejsze m/s^2 niż [(m,0),(s,-2)]. Fakt, że [(m,0),(s,-2)] może być prostsze do wykorzystania, ale trudniej je wpisać.

    Nie, a co?

    OdpowiedzUsuń
  11. Ja o kozie ty o wozie. Co innego przechowywanie jednostek, a co innego wprowadzanie ich z palca. Jeżeli chodzi o przechowywanie jednostek to metoda którą ci zaproponowałem jest używana dosyć powszechnie w oprogramowaniu inżynierskim, a przynajmniej tym ze stajni Autodesk i paru innych.
    Jeżeli chodzi o wprowadzanie jednostek to inna sprawa. Wystarczy, że byś napisał normalny parser LL(1) i prosty skaner. Ustalasz zestaw tokenów (mogą być zlokalizowane) np.

    metr: m ;
    sekunda: s ;
    ...
    liczba: [-+]?\d+(\.\d+)? ;
    ws: (' '|'\n'|'\r'|'\t')+

    itd.
    Do tego piszesz prosty parser LL(1) do takiej gramatyki:

    wyrazenie: mnozenie (('+' | '-' )mnozenie)*;
    mnozenie: stala (('*' | '/') stala )*;
    stala: liczba | symbol;

    Dla kogoś z doświadczeniem, mniej niż 10min. Dla kogoś, kto nigdy nie pisał ręcznie parsera, ale ma łeb na karku ok. pół godziny.

    OdpowiedzUsuń