Nazwa dla lekkich i ciężkich operacji

0

Otóż piszę funkcję która musi obsłużyć 10 przypadków

boolean check(Object o)

Dla 8 na 10 przypadków, operacja sprawdzająca jest bardzo szybka (kilkaset operacji na ms). Dla pozostałych 2óch na 10 przypadków szybka operacja sprawdzająca jest czasem nie wystarczająca, więc trzeba zrobić wolną operacją sprawdzającą. Kod powinien wyglądać tak:

boolean check(Object o) {
  if (szybka(o)) {
    return true;
  }
  return wolna(o);
}
  • Operacja szybka() potrafi tylko powiedzieć czy coś spełnia warunek:
    • true znaczy "na pewno spełnia"
    • false znaczy "nie wiadomo czy spełnia"
  • Operacja wolna() sprawdza już to na 100%:
    • true znaczy "na pewno spełnia"
    • false znaczy "na 100% nie spełnia"

No własnie, i nie wiem jak nazwać te metody, tak żeby było z samych nazw wiadomo:

  • Że szybka() jest optymalna, ale potrafi tylko powiedzieć "na pewno tak, ale nie wiadomo czy nie".
  • Że wolna() jest zasobożerna, ale potrafi na 100% ocenić poprawność.

Jakieś pomysły na nazwy?

1

@au7h: szybkaNiedokladnaOptymalna(); ostatni człon zawiera się już w pierwszym więc można skrócić nazwę.

W sumie pierwsza to wstępny test więc może preTest() i fullTest()?

0
Delor napisał(a):

W sumie pierwsza to wstępny test więc może preTest() i fullTest()?

Nazwy nie pokazują że fullTest() jest 200-500x bardziej zasobożerna niż preTest().

0

isYadaYadaWithFalseNegatives oraz isYadaYada..

0
Afish napisał(a):

isYadaYadaWithFalseNegatives oraz isYadaYada..

Nazwa nie pokazuje czemu ktoś miałby użyć pierwszej (jest szybka).

0
boolean check(Object o) {
  if (fastConditionTrueConfirmation(o)) {
    return true;
  }
  return slowConditionTrueFalseConfirmation(o);
}

```
0
boolean check(Object o) {
    return common_case_fast_check(o) or rare_case_slow_check(o);
}
0
Spearhead napisał(a):
boolean check(Object o) {
    return common_case_fast_check(o) or rare_case_slow_check(o);
}

Nie ma informacji o tym że pierwszy umie tylko potwierdzić, ale nie wykluczyć; ani o tym że drugi umie potwierdzić/wykluczyć na 100%.

3

shallow_check
deep_check

1
  1. W interfejsie publicznym: check()
  2. W implementacji (prywatnej):
    • check_common_cases()
    • check_edge_cases()
0

fastSensitiveCheck i slowSensitiveAndSpecificCheck - https://en.wikipedia.org/wiki/Sensitivity_and_specificity

Można dodać ewentualnie NotSpecific do pierwszego.

1

Pytanie czy ta szybka a niepewna w ogóle będzie przydatna, czy tylko tworzysz ją jako wadliwą optymalizację? :)

2
  • heurestic_check
  • check
0

Co jest ważne dla czytelnika? To CO funkcja robi, czy JAK to robi? Może po zmianach przez innego developera, już nie będzie taka "quick" / "slow" :-)

0

A to są publiczne metody czy tylko wewnętrzna implementacja?

8
TomRiddle napisał(a):
  • Operacja szybka() potrafi tylko powiedzieć czy coś spełnia warunek:
    • true znaczy "na pewno spełnia"
    • false znaczy "nie wiadomo czy spełnia"
  • Operacja wolna() sprawdza już to na 100%:
    • true znaczy "na pewno spełnia"
    • false znaczy "na 100% nie spełnia"

Żadna z propozycji podanych w tym temacie mi się nie podoba.

Shallow, deep, heuristic, "sensitive and specific"... Ilu programistów poprawnie zrekonstruuje z tych terminów tę rozpiskę, którą podałeś powyżej? :)

"With false negatives" jest lepsze, ale rozwlekłe, wymaga chwili namysłu. Na dodatek ma wydźwięk, który - przynajmniej dla mnie - sugeruje nieprawidłowe działanie metody.

Kiedy znalezienie dobrych nazw stanowi problem, czasem warto zrobić krok do tyłu i zastanowić się, czy nie trzeba zmienić podejścia. Może problem wynika z prób upakowania trzech możliwych odpowiedzi w wartości boolean, których znaczenie zmienia się kontekstowo?

Może powinno być np. tak, że szybka metoda zwraca Optional<Boolean>. A wolna - Boolean.

Ważną informację przenosimy w ten sposób do sygnatur tych metod. Staje się oczywiste, że metoda szybka udziela odpowiedzi niepewnej. Dopilnuje tego sam silnik typów.

I odpada problem, że false miewa dwa różne znaczenia zależnie od kontekstu. Co wcześniej mogło zdezorientować.

Znalezienie nazwy staje się wtedy łatwiejsze, bo:

nie wiem jak nazwać te metody, tak żeby było z samych nazw wiadomo:

  • Że szybka() jest optymalna, ale potrafi tylko powiedzieć "na pewno tak, ale nie wiadomo czy nie".
  • Że wolna() jest zasobożerna, ale potrafi na 100% ocenić poprawność.

Od tej pory nie masz już tego "ale". Teraz nazwa musi oddać tylko pierwszą różnicę. Druga wynika z sygnatury metody.

Możliwe nazwy:

Optional<Boolean> quicklyConfirm(Object o);

Boolean exhaustivelyConfirm(Object o);

A wtedy:

boolean check(Object o) {
  if (szybka(o)) {
    return true;
  }
  return wolna(o);
}

Zmieni się w:

boolean check(Object o) {
  return quicklyConfirm(o)
          .orElseGet(() -> exhaustivelyConfirm(o));
}

Wytknąć można jedno niedociągnięcie - kwestia dyskusji i gustu, czy w praktyce istotne. W praktyce quicklyConfirm może zwrócić: Optional.of(true) albo Optional.empty()*.

Rezultat Optional.of(false) jest niemożliwy. Ale to nie wynika już ani z nazwy, ani z sygnatury. Dotarliśmy do limitu tego, co może wyrazić boolean. Nawet udekorowany trójwartościowością.

Jeśli jesteśmy nieco pedantyczni i nam to przeszkadza...

...możemy pokusić się o jeszcze ściślejsze podejście.

Na przykład - używając Javy:

public class ConfirmationResult {
    public interface ConfirmationStatus {
        Optional<Boolean> value();
    }

    public enum Quick implements ConfirmationStatus {
        CONFIRMED,
        UNKNOWN;
        
        @Override
        public Optional<Boolean> value() {
            if (this == CONFIRMED) {
                return Optional.of(true);
            }
            return Optional.empty();
        }
    }
    
    public enum Exhaustive implements ConfirmationStatus {
        CONFIRMED,
        REJECTED;

        @Override
        public Optional<Boolean> value() {
            return Optional.of(this == CONFIRMED);
        }
    }
}

I teraz:

ConfirmationResult.Quick quicklyConfirm(Object o);

ConfirmationResult.Exhaustive exhaustivelyConfirm(Object o);

Oba enumy wspierają ten sam interfejs, więc mają wspólny mianownik: ConfirmationStatus, więc można tę wartość podawać - i wyciągnąć z niej Optional<Boolean> - nie interesując się kompletnie, czy jest to instancja Quick, czy Exhaustive.

A zarazem nie sposób się w żaden sposób pomylić. Wszystko jest udokumentowane na poziomie typów i definicji. if (exhaustivelyConfirm(o) == ConfirmationResult.Quick.UNKNOWN) nawet się nie zbuduje. if (quicklyConfirm(o) == ConfirmationResult.Exhaustive.REJECTED) nawet się nie zbuduje.

Można, jeśli potrzebujemy, napisać sobie nawet szybką "porównywarkę" dla instancji ConfirmationStatus, która umożliwiłaby łatwe porównania między wartościami obu enumów. Jest to Java, więc niestety wszędzie włazi nam pełno boilerplate'u. Ale z tym męczy się pół świata i na to już nie ma prostej rady.


  • Na potrzeby poglądowe używam jakiejś bibliotecznej wersji Optionala, bo akurat jest dodana do projektu, który mam otwarty w IDE. Więc tak mi prościej. Your naming may vary.
0

Chciałbym uroczyście podziękować @V-2 za duży wkład wysiłku w miarodajną odpowiedź! Dałbym super-łapkę, gdyby była taka możliwość.

V-2 napisał(a):
TomRiddle napisał(a):
  • Operacja szybka() potrafi tylko powiedzieć czy coś spełnia warunek:
    • true znaczy "na pewno spełnia"
    • false znaczy "nie wiadomo czy spełnia"
  • Operacja wolna() sprawdza już to na 100%:
    • true znaczy "na pewno spełnia"
    • false znaczy "na 100% nie spełnia"

Żadna z propozycji podanych w tym temacie mi się nie podoba.

Shallow, deep, heuristic, "sensitive and specific"... Ilu programistów poprawnie zrekonstruuje z tych terminów tę rozpiskę, którą podałeś powyżej? :)

"With false negatives" jest lepsze, ale rozwlekłe, wymaga chwili namysłu. Na dodatek ma wydźwięk, który - przynajmniej dla mnie - sugeruje nieprawidłowe działanie metody.

Kiedy znalezienie dobrych nazw stanowi problem, czasem warto zrobić krok do tyłu i zastanowić się, czy nie trzeba zmienić podejścia. Może problem wynika z prób upakowania trzech możliwych odpowiedzi w wartości boolean, których znaczenie zmienia się kontekstowo?

W zupełności się zgadzam.

Może powinno być np. tak, że szybka metoda zwraca Optional<Boolean>. A wolna - Boolean.

Ważną informację przenosimy w ten sposób do sygnatur tych metod. Staje się oczywiste, że metoda szybka udziela odpowiedzi niepewnej. Dopilnuje tego sam silnik typów.

Pytanie czy jest sens zmieniać proste sygnatury boolean a(), boolean b() na bardziej skomplikowane, po to żeby były bardziej wymowne? Dodatkowo z tym optionalem, może być albo optional udany true, albo nie udany. Nie ma optional'a udanego false - jaki by to miało sens?

A zarazem nie sposób się w żaden sposób pomylić. Wszystko jest udokumentowane na poziomie typów i definicji. if (exhaustivelyConfirm(o) == ConfirmationResult.Quick.UNKNOWN) nawet się nie zbuduje. if (quicklyConfirm(o) == ConfirmationResult.Exhaustive.REJECTED) nawet się nie zbuduje.

Też o tym myślałem, żeby składania języka sama wymuszała poprawne użycie wyniku, i uniemożliwiała użycie tego false jako "false", zamiast jako "nie wiadomo czy false". To by było bardzo fajne! gdyby nie to że bardzo skomplikowane :/


Ale z tymi enumami, stary to pojechałeś :D Super over-engineering, dla dwóch metod. Niestety, nie chcę tak komplikować jednego prostego algorytmu.

Niemniej - bardzo doceniam to że włożyłeś wysiłek w przemyślenie, analizę problemu i kilka sensownych propozycji. Również doceniam to że ważna była dla Ciebie forma, widać to po 10x edytowany tekst :D

Dziękuję.

0
TomRiddle napisał(a):
Afish napisał(a):

isYadaYadaWithFalseNegatives oraz isYadaYada..

Nazwa nie pokazuje czemu ktoś miałby użyć pierwszej (jest szybka).

isYadaYadaFastCheckWithFalseNegatives.

Od takich rzeczy jest dokumentacja, bo:

  • Co znaczy „szybko”? Jak to zostało sprawdzone i według jakich kryteriów?
  • Skąd wiadomo, że „szybko” jest ciągle poprawne? A może od czasu testów coś się zmieniło i już nie jest szybko.
  • Dlaczego nie zawsze jest poprawny wynik? W jakich sytuacjach jest niepoprawny? Jak często ta sytuacja występuje?
  • Dlaczego w ogóle używać szybkiej metody? Może zysk nie jest wart trzymania dwóch algorytmów?
0
Afish napisał(a):

Od takich rzeczy jest dokumentacja, bo:

No przykro mi, ale nie tak mnie uczono programować. Jeśli mogę coś wyrazić dobrze w nazwie funkcji, to wolałbym to zrobić, niż olewać problem i pchać jakieś mało mówiące wyjaśnienie do zewnętrznego źródła, jakim jest dokumentacja. Polecam przeczytać "Clean Code".

  • Co znaczy „szybko”? Jak to zostało sprawdzone i według jakich kryteriów?

Szybko znaczy hashMap.containsKey('key').

  • Skąd wiadomo, że „szybko” jest ciągle poprawne? A może od czasu testów coś się zmieniło i już nie jest szybko.

Jestem przekonany że wyciąganie czegoś z hash mapy po kluczu jest wystarczająco szybkie :D

  • Dlaczego nie zawsze jest poprawny wynik? W jakich sytuacjach jest niepoprawny? Jak często ta sytuacja występuje?

Nie mówiłem że jest "niepoprawny". Mówiłem że jest niepewny.

Jeśli klucz w mapie jest, znaczy że to co sprawdzam istnieje na 100%. Jeśli nie ma w mapie - to znaczy że może jest, może nie ma i trzeba wykonać ciężą operację żeby się upewnić. Około 80% przypadków jest już pre-loaded w mapie.

  • Dlaczego w ogóle używać szybkiej metody? Może zysk nie jest wart trzymania dwóch algorytmów?

Bo to szybki special-case, i warto zaoszczędzić trochę procesora/zasobów.

Fetchowanie tego drugi raz korzysta z zasobów IO systemowych (i czasami z socketów) - bez sensu jest wykonywanie tej drugiej operacji zawsze.

0
TomRiddle napisał(a):

Chciałbym uroczyście podziękować @V-2 za duży wkład wysiłku w miarodajną odpowiedź! Dałbym super-łapkę, gdyby była taka możliwość.

Cieszę się że mogłem pomóc, albo zainspirować; dzięki

Może powinno być np. tak, że szybka metoda zwraca Optional<Boolean>. A wolna - Boolean.

Ważną informację przenosimy w ten sposób do sygnatur tych metod. Staje się oczywiste, że metoda szybka udziela odpowiedzi niepewnej. Dopilnuje tego sam silnik typów.

Pytanie czy jest sens zmieniać proste sygnatury boolean a(), boolean b() na bardziej skomplikowane, po to żeby były bardziej wymowne?

Coś za coś... Nie widzę możliwości spełnienia wszystkich wymogów naraz. Zwięzłe nazwy / zrozumiałe na pierwszy rzut oka API / najprostsze możliwe sygnatury... pick two :)

Sygnatury w "skromniejszej" wersji rozwiązania - czyli boolean i Optional<Boolean> - nie są zresztą aż tak skomplikowane.

Dodatkowo z tym optionalem, może być albo optional udany true, albo nie udany. Nie ma optional'a udanego false - jaki by to miało sens?

Optional(false) znaczy "zweryfikowany negatywnie", "wykluczony". Kot nie żyje.

Być może nazwy należałoby oprzeć na czasowniku np. "verify" zamiast "confirm".

Ale z tymi enumami, stary to pojechałeś :D Super over-engineering, dla dwóch metod. Niestety, nie chcę tak komplikować jednego prostego algorytmu.

Nie jestem takim purystą, pokazałem tylko możliwość :) To podejście znane z programowania funkcyjnego: możliwie jak najwięcej wyspecyfikować za pomocą typów. Java jest niestety bardzo rozwlekłym językiem. Są takie, w których dałoby się to samo wyrazić znacznie zwięźlej.

Jeśli znajdziesz zgrabniejsze rozwiązanie, wrzuć proszę do tematu (z ciekawości)

0
V-2 napisał(a):

Jeśli znajdziesz zgrabniejsze rozwiązanie, wrzuć proszę do tematu (z ciekawości)

Raczej zostawię ify i znajdę dobre nazwy funkcji, zamiast zmieniać sygnatury :) Ale jeszcze raz dziękuję za wkład w odpowiedź.

Głównie dlatego że Optional.of(true)/Optional.empty() jest semantycznie równy true/false, więc nie specjalnie chciałbym go użyć.

PS: Tak, zdaję sobie sprawę że of(true)/of(false)/empty() jest semantycznie równy true/false/null - ten sam case :D Ale coś mi mówi że z dwoma przypadkami to ma mniejszy sens. Nie potrafię wyjaśnić czemu.

0
Spearhead napisał(a):

Takie rzeczy dopisz w dokumentacji funkcji.

Yyyyy.... albo mogę dobrze nazwać funkcję?

fasadin napisał(a):

shallow_check
deep_check

Nazwa shallowCheck nie mówi trochę o tym czy to jest false negative czy false positive.

yarel napisał(a):
  1. W interfejsie publicznym: check()
  2. W implementacji (prywatnej):
    • check_common_cases()
    • check_edge_cases()

Nazwy zupełnie nie mówią ani o tym która jest szybsza, ani o tym która jest pewna i nie pewna.

danek napisał(a):

A to są publiczne metody czy tylko wewnętrzna implementacja?

A co za różnica? Funkcja ma być nazwana dobrze.

yarel napisał(a):

Co jest ważne dla czytelnika? To CO funkcja robi, czy JAK to robi? Może po zmianach przez innego developera, już nie będzie taka "quick" / "slow" :-)

Na różnych poziomach abstrakcji funkcje będą miały różny poziom szczegółowości w nazwach i sygnaturze. Ten przypadek jest raczej nisko, więc siłą rzeczy nazwy funkcji muszą odzwierciedlać szczegóły implementacyjne.

Azarien napisał(a):

Pytanie czy ta szybka a niepewna w ogóle będzie przydatna, czy tylko tworzysz ją jako wadliwą optymalizację? :)

Dobre :D W 80% case'a ta "wadliwa optymalizacja" wystarczy żeby potwierdzić przypadki. Potwierdzanie jest tutaj dużo częstszym przypadkiem niż odrzucanie.

Michał Sikora napisał(a):

fastSensitiveCheck i slowSensitiveAndSpecificCheck - https://en.wikipedia.org/wiki/Sensitivity_and_specificity

Można dodać ewentualnie NotSpecific do pierwszego.

"Sensitive"/"Specific" nie mówią specjalnie dużo o tym czy funkcja dopuszcza false negative'y.

1

@TomRiddle Nie szkoda CI czasu na takie dywagacje, może po prostu:

boolean check(Object o) {
  return quickCheck(o) ? true : exactCheck(o);
}
0
TomRiddle napisał(a):
V-2 napisał(a):

Jeśli znajdziesz zgrabniejsze rozwiązanie, wrzuć proszę do tematu (z ciekawości)

Raczej zostawię ify i znajdę dobre nazwy funkcji, zamiast zmieniać sygnatury :) Ale jeszcze raz dziękuję za wkład w odpowiedź.

Głównie dlatego że Optional.of(true)/Optional.empty() jest semantycznie równy true/false, więc nie specjalnie chciałbym go użyć.

Moim zdaniem nie jest ;) Po co wtedy byłby Boolean?

Logicznie rzecz biorąc, semantycznie równoważne true / false powinno być Optional.of(Void) i Optional.empty().

Niestety w Javie Optional.of(Void) jest chyba równy Optional.empty().

Ogólnie jest to jeden z tych przypadków, gdy brakuje union types.

1
TomRiddle napisał(a):

No przykro mi, ale nie tak mnie uczono programować. Jeśli mogę coś wyrazić dobrze w nazwie funkcji, to wolałbym to zrobić, niż olewać problem i pchać jakieś mało mówiące wyjaśnienie do zewnętrznego źródła, jakim jest dokumentacja. Polecam przeczytać "Clean Code".

No właśnie nie wyrażasz tego dobrze. Napisałeś całego posta z odpowiedziami na kilka prostych pytań, ale żadna z proponowanych nazw funkcji tego nie robi.

Szybko znaczy hashMap.containsKey('key').

A dlaczego druga metoda jest wolna? I mam teraz sprawdzać implementację obu metod, czy może lepiej będzie wcisnąć skrót w IDE i zobaczyć dokumentację z wyjaśnieniem?

Jestem przekonany że wyciąganie czegoś z hash mapy po kluczu jest wystarczająco szybkie :D

Czyli nie mierzyłeś, która metoda jest szybsza? A może różnica jest pomijalna? A może hashmapa powoduje duże zużycie pamięci i lepiej pozbyć się jej całkowicie? A może od czasu napisania tej metody ktoś zmienił hashmapę na inny słownik (lub na coś całkowicie innego) i już nie jest szybkie?

Jak patrzysz tu i teraz, to nie potrzebujesz nic, bo jesteś autorem kodu i wiesz, co on robi, ale jak za tydzień przyjdzie ktoś inny, to nagle Twoje „oczywistości” będą nieczytelne.

Nie mówiłem że jest "niepoprawny". Mówiłem że jest niepewny.
Jeśli klucz w mapie jest, znaczy że to co sprawdzam istnieje na 100%. Jeśli nie ma w mapie - to znaczy że może jest, może nie ma i trzeba wykonać ciężą operację żeby się upewnić. Około 80% przypadków jest już pre-loaded w mapie.

Dlaczego „około 80%”? Co znaczy „około”? Jak to zmierzono? A co, jeżeli od czasu napisania kodu to „około 80%” zmieniło się w „około 10%”? I jak powtórzyć testy?

Bo to szybki special-case, i warto zaoszczędzić trochę procesora/zasobów.

Ponownie, skąd wiadomo, że jest „szybki”? Skąd wiadomo, że oszczędzanie procesora jest tego warte, bo może tym samym rozpychamy pamięć i tracimy procesor na GC?

Fetchowanie tego drugi raz korzysta z zasobów IO systemowych (i czasami z socketów) - bez sensu jest wykonywanie tej drugiej operacji zawsze.

Skąd wiadomo, że w hashmapie będzie aktualna wartość? A co, jeżeli ta wartość się zmieniła w międzyczasie? Jak często odświeżana jest hashmapa? Czy jeżeli wartość nie jest aktualna, to kiedy staje się to problemem? Czy w ogóle? A dlaczego w ogóle hashmapa, dlaczego nie jakiś słownik ze słabymi referencjami?

Co do Clean Code, to nie trafiłeś, przeczytałem tę książkę.

0

@TomRiddle: jeśli "fast check" to IMHO najlepsza nazwa to: check_in_cache.

Co do rozwiązania, to jeśli możesz je zakodować w postaci enuma tak jak powiedział @V-2 to nawet lepiej, bo wtedy możesz stworzyć listę metod jak np. w przyszłości pojawi się jakaś pośrednia metoda, która będzie dawała wynik wolniej niż cache, ale szybciej niż pełen przegląd to możesz dodać to bardzo łatwo do listy. Przykład:

#[derive(PartialOrd, Ord, PartialEq, Eq, Debug)]
enum CheckResult {
  Unknown,
  Maybe(bool),
  Sure(bool)
}

impl CheckResult {
  fn and_then<F: Fn() -> CheckResult, T>(&self, func: F) -> CheckResult {
    match *self {
      Sure(_) @ val => val,
      _ => func()
    }
  }
}

#[derive(Debug)]
struct Checker {
  cache: HashMap<u32, bool>
}

fn check_cache(cache: &HashMap<u32, bool>, value: u32) -> CheckResult {
  match cache.get(&value) {
    Some(result) => CheckResult::Sure(result),
    None => Unknown
  }
}

fn check_sure(cache: &HashMap<u32, bool>, value: u32) -> CheckResult {
  // …
}

fn check(cache: &HashMap<u32, bool>, value: u32) -> bool {
  let result =
    check_cache(cache, value)
    .and_then(|| check_sure(cache, value));

  match result {
    Sure(value) => value,
    other => panic!("Unsure value: {:?}", other)
  }
}
0
cs napisał(a):

@TomRiddle Nie szkoda CI czasu na takie dywagacje, może po prostu:

boolean check(Object o) {
  return quickCheck(o) ? true : exactCheck(o);
}

quickCheck() nie mówi o tym że jest tam false negative.

V-2 napisał(a):

Niestety w Javie Optional.of(Void) jest chyba równy Optional.empty().

A jak chcesz stworzyć instancję Void? :D

Afish napisał(a):

Szybko znaczy hashMap.containsKey('key').

A dlaczego druga metoda jest wolna?

Napisałem to w poście wyżej, bo korzysta z IO i z socketów.

I mam teraz sprawdzać implementację obu metod, czy może lepiej będzie wcisnąć skrót w IDE i zobaczyć dokumentację z wyjaśnieniem?

Idealnie by było gdyby nazwa była wymowna i nie musiałbyś szukać nigdzie indziej informacji o tym.

Jestem przekonany że wyciąganie czegoś z hash mapy po kluczu jest wystarczająco szybkie :D

Czyli nie mierzyłeś, która metoda jest szybsza?

Nie mierzyłem, ale mam mózg i wiem bez sprawdzania że implementacja oparta na mapie jest szybsza niż fetchowanie czegoś z IO/socket'ów.

A może różnica jest pomijalna?

Nie jest pomijalna.

A może hashmapa powoduje duże zużycie pamięci i lepiej pozbyć się jej całkowicie?

Nie mogę pozbyć się całkowicie hash'mapy bo ten szybki pre-test nie jest jej jedynym zadaniem, jest istotnym elementem innej części aplikacji (głównie UI).

A może od czasu napisania tej metody ktoś zmienił hashmapę na inny słownik (lub na coś całkowicie innego) i już nie jest szybkie?

Jeśli ktoś przebuduję część aplikacji, to faktycznie implementację fast/slow check'a trzeba będzie zmienić. Teraz to jest zupełnie nie potrzebne.

Jak patrzysz tu i teraz, to nie potrzebujesz nic, bo jesteś autorem kodu i wiesz, co on robi, ale jak za tydzień przyjdzie ktoś inny, to nagle Twoje „oczywistości” będą nieczytelne.

Właśnie dokładnie dlatego chcę poświęcić czas i dobrze nazwać tę funkcję - żeby dokładnie mówiła czym jest i co robi.

Nie mówiłem że jest "niepoprawny". Mówiłem że jest niepewny.
Jeśli klucz w mapie jest, znaczy że to co sprawdzam istnieje na 100%. Jeśli nie ma w mapie - to znaczy że może jest, może nie ma i trzeba wykonać ciężą operację żeby się upewnić. Około 80% przypadków jest już pre-loaded w mapie.

Dlaczego „około 80%”? Co znaczy „około”? Jak to zmierzono? A co, jeżeli od czasu napisania kodu to „około 80%” zmieniło się w „około 10%”? I jak powtórzyć testy?

A jakie to ma w ogóle znaczenie? Nawet gdyby to był 1%, to i tak warto zrobić pre-test, bo a nuż się okaże że pre-test dał true i wykonywanie ciężkiej operacji nie ma sensu.

Co znaczy „około”? Jak to zmierzono?

"około" znaczy 70-90%. Mniej więcej 8/10 case'ów - widzę po reakcji programu że trochę krócej mieli.

Bo to szybki special-case, i warto zaoszczędzić trochę procesora/zasobów.

Ponownie, skąd wiadomo, że jest „szybki”?

BO WYCIĄGANIE Z MAPY PO KLUCZU JEST JEDNĄ SZYBSZYCH OPERACJI W JAVIE. Szybsze chyba może być tylko dostęp do array'a po indexie.

Skąd wiadomo, że oszczędzanie procesora jest tego warte, bo może tym samym rozpychamy pamięć i tracimy procesor na GC?

No nie. Teraz to muszę pisać w assemblerze, skoro mapa jest taka nieoptymalna :/

Fetchowanie tego drugi raz korzysta z zasobów IO systemowych (i czasami z socketów) - bez sensu jest wykonywanie tej drugiej operacji zawsze.

Skąd wiadomo, że w hashmapie będzie aktualna wartość? A co, jeżeli ta wartość się zmieniła w międzyczasie? Jak często odświeżana jest hashmapa? Czy jeżeli wartość nie jest aktualna, to kiedy staje się to problemem? Czy w ogóle? A dlaczego w ogóle hashmapa, dlaczego nie jakiś słownik ze słabymi referencjami?

Chwila... próbujesz mi teraz powiedzieć że moja implementacja ma milion dziur? Czy twierdzisz że korzystanie z szybki pre-optymalizacji z możliwymi false-negative'ami jest niebezpieczne?

Jak to się ma do dobrego doboru nazwy?

Co do Clean Code, to nie trafiłeś, przeczytałem tę książkę.

No to na prawdę nie wiem czemu twierdzisz że nie warto poświęcać czasu na dobre nazwy.

0
hauleth napisał(a):

@TomRiddle: jeśli "fast check" to IMHO najlepsza nazwa to: check_in_cache.

Co do rozwiązania, to jeśli możesz je zakodować w postaci enuma tak jak powiedział @V-2 to nawet lepiej, bo wtedy możesz stworzyć listę metod jak np. w przyszłości pojawi się jakaś pośrednia metoda, która będzie dawała wynik wolniej niż cache, ale szybciej niż pełen przegląd to możesz dodać to bardzo łatwo do listy. Przykład:

@hauleth Faktycznie Twoje odpowiedzi są bardzo merytoryczne, i wnoszą dużo do tematu. Ale:

Błaagaaam, bez over-enginerringu, mój kod wygląda tak

if (fastCheck(a)) {
  return true;
}
return slowCheck(a);

Nie widzę powodu żeby go refactorować - działa spoko. Chcę tylko zmienić nazwy funkcji na bardziej wymowne. Że jedna jest szybka ale nie pewna, a druga jest wolna ale pewna. Pomożecie?

0

Ciekawy problem. Może coś w stylu

if (preliminaryCheckMostCommonCases(a)) {
  return true;
}
return reliableExpensiveCheck(a);

?

0
Potat0x napisał(a):

Ciekawy problem. Może coś w stylu

if (preliminaryCheckMostCommonCases(a)) {
  return true;
}
return reliableExpensiveCheck(a);

?

Nazwa preliminaryCheckMostCommonCases() nie mówi o tym że metoda jest szybka.

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