Kod, który konwertuje wartość na inną reprezentację, a następnie konwertuje ją z powrotem do miejsca, w którym się zaczął, jest zły, ale jak? [Zamknięte]

35

Czytałem artykuł o złych praktykach programistycznych .

Wspomniał -

„Kod jo-jo”, który konwertuje wartość na inną reprezentację, a następnie konwertuje ją z powrotem do miejsca, w którym się zaczęła (np. Konwersja dziesiętnego na ciąg znaków, a następnie z powrotem na dziesiętny lub wypełnianie ciągu, a następnie przycinanie go)

Nie rozumiem, dlaczego ten konkretny przykład, który podaje, to zły sposób na pisanie programów. Wydaje mi się, że mogę ponownie przekonwertować, jeśli sytuacja tego wymaga, aby można było użyć wartości.

Czy ktoś może wyjaśnić więcej na ten temat?

użytkownik13107
źródło
4
zalecana lektura: Porozmawiaj o tym {blog}
gnat
8
Przez większość czasu jest to po prostu zbędne i dzieje się tak tylko dlatego, że programista nie znał lepszego sposobu na uzyskanie tego, czego chcieli. Wpis blog daje typowy przykład kilka akapitów później: "Roundabout code" that accomplishes in many instructions what could be done with far fewer (eg: rounding a number by converting a decimal into a formatted string, then converting the string back into a decimal). if the situation is so that they have to be used?- jaka by to była sytuacja?
Konrad Morawski
3
@gnat Nie rozumiem, dlaczego to sprawia, że ​​to złe pytanie. Jeśli chcesz, mogę go edytować i powiedzieć: „czy kod, który konwertuje i konwertuje wartość, jest zły?” i nie będzie już pasować do tego szablonu.
djechlin
5
Niedawno znalazłem kod w Javie, który iteruje po tablicy, szeregując każdy obiekt w obiekt JSON, używając konkatenacji łańcuchów, a nie serializatora JSON. Wynik został następnie przekazany do prywatnej metody, która przeanalizowała tablicę JSON w celu wyodrębnienia kilku identyfikatorów, a następnie przekazała identyfikatory gdzie indziej. Było to jedyne zastosowanie tej tablicy JSON w systemie. To jest kod jo-jo. Nie było powodu do transformacji tam iz powrotem. Moglibyśmy po prostu przekazać identyfikatory z oryginalnych obiektów.
Brandon
3
decimal myValue = decimal.Parse(dataReader["myColumn"].ToString())to mój wkurzony zwierzak.
Matthew

Odpowiedzi:

125

Nawet jeśli zrobić potrzebujemy zarówno numeryczne i znaków reprezentujący liczbę, to lepiej, aby przekonwertować tylko raz, a także powiesić na pierwotnej wartości, zamiast konwersji ponownie za każdym razem trzeba jedno albo drugie.

Zasadą jest, jak zawsze, że kod, który nie istnieje, nie może mieć subtelnych wad , podczas gdy kod, który istnieje, często ma. Może to zabrzmieć paranoicznie, ale doświadczenie uczy nas, że jest to właściwe. Jeśli podejdziesz do programowania ze stałym lekkim niepokojem: „Nie jestem wystarczająco inteligentny, aby zrozumieć ten złożony system”, jesteś na dobrej drodze.

Kilian Foth
źródło
5
Dobrze powiedziane. My, programiści, powinniśmy być bardzo ostrożni.
Neil
58
„kod, który nie istnieje, nie może mieć subtelnych wad, podczas gdy kod, który istnieje często,” chciałbym dać ci za to +2. Nigdy nie lekceważ wartości niepotrzebnego pisania kodu.
Benjamin Gruenbaum
2
Ale wykonanie kilku prostych operacji (konwersja na łańcuch i powrót) może być znacznie mniej skomplikowane (łatwiejsze do zrozumienia i kodowania) niż „właściwy” sposób manipulowania bitami. A utrzymanie wszystkich danych kategorii w jednym formularzu jest często dobrym pomysłem, nawet jeśli pewne dane nieuchronnie zostaną przekonwertowane na inne formularze.
Daniel R Hicks
36
@DanielRHicks, więc przekonwertujmy tę prostą datę (10 listopada 2014 r.) Na ciąg znaków, -> 10-11-2014 i wróć do daty -> (11 października 2014 r.) Hej, czekaj co?
Pieter B
20
@PieterB Istnieje duże niemieckie oprogramowanie księgowe, które nie działa na komputerach z ustawieniami niemieckimi, ponieważ to robi. Najpierw konwertuje datę na ciąg przy użyciu ustawień regionalnych systemu, a następnie próbuje parsować ją przy użyciu ustalonych ustawień regionalnych i skarży się na nielegalny format. Robi to samo z liczbami i różnymi separatorami dziesiętnymi, z tym wyjątkiem, że tam nie narzeka, psuje dane i wykazuje dziwne zachowanie. Zajęło mi kilka dni, żeby to zrozumieć.
CodesInChaos
23

Jest zły z trzech głównych powodów:

  1. Pokazuje, że nie pomyślałeś o tym, jaki typ / format powinna być zmienna, ale zamiast tego konwertujesz ją na to, czego potrzebujesz w danym momencie. To pokazuje brak myśli projektowej.
  2. To chyba marnotrawstwo. Prawie na pewno marnujesz cykle i wiersze kodu, robiąc konwersje, które nie muszą tam być. Spowoduje to, że Twój kod będzie wolniejszy i bardziej rozdęty, niż powinien.
  3. Konwersje typów są podatne na subtelne błędy. Rozrzucając te konwersje w kodzie, zwiększasz prawdopodobieństwo błędu.

Podejrzewam, że powodem 1 jest powód, dla którego twoje źródło myślało na podstawie kontekstu, w którym zostało wspomniane.

Jack Aidley
źródło
6

Przeredagowałbym opis jako „kod, który konwertuje typ na inną reprezentację w celu zrobienia czegoś, co można było zrobić równie dobrze lub lepiej w oryginale, a następnie konwertuje go z powrotem. Istnieje wiele sytuacji, w których konwersja czegoś do inny typ, działając na nie i przekształcając go z powrotem jest całkowicie odpowiedni, a jego niepodanie spowodowałoby nieprawidłowe zachowanie.

Na przykład, gdy konwersja jest dobra:
jeden ma cztery floatwartości dowolnych znaków, których wielkości mogą się różnić aż do 1000, a na końcu należy obliczyć sumę z dokładnością do 0,625 jednostek. Konwertowanie wszystkich czterech wartości na double, obliczanie sumy i konwertowanie wyniku z powrotem na floatbędzie znacznie wydajniejsze niż w przypadku jakiegokolwiek podejścia używającego floatsamego.
Wartości zmiennoprzecinkowe są co najwyżej z dokładnością do 0,5 jednostki na ostatnim miejscu (ULP). Ten przykład wymagałby, aby błąd zaokrąglania w najgorszym przypadku nie był większy niż 25% powyżej optymalnego błędu w najgorszym przypadku. Użycie podwójnej wartości da dokładność w granicach 0,5001 ULP. Podczas gdy wymóg 0.625 ULP może wydawać się wymyślony, takie wymagania są często ważne w algorytmach sukcesywnej aproksymacji. Im ściślej określone jest ograniczenie błędu, tym niższy wymóg dotyczący iteracji w najgorszym przypadku.

Na przykład, gdy konwersja jest zła:
jeden ma liczbę zmiennoprzecinkową i chce wypisać ciąg, który będzie reprezentował jego wartość w unikalny sposób. Jednym z podejść jest przekonwertowanie liczby na ciąg z określoną liczbą cyfr, próba konwersji z powrotem i sprawdzenie, czy wynik jest zgodny.

Ale w rzeczywistości jest to złe podejście. Jeśli ciąg dziesiętny reprezentuje wartość, która znajduje się prawie dokładnie w połowie odległości między dwiema wartościami zmiennoprzecinkowymi, jest dość drogi w przypadku metody ciąg-zmiennoprzecinkowy, aby zagwarantować, że zawsze da wynik bliższyfloat wartość , a wiele takich metod konwersji nie utrzymują takiej gwarancji (między innymi zrobienie tego wymagałoby w niektórych przypadkach odczytania wszystkich cyfr liczby, nawet jeśli miała ona miliardy cyfr).

Metoda jest znacznie tańsza, aby zagwarantować, że zawsze zwróci wartość z dokładnością do 0,5625 jednostek na ostatnim miejscu (ULP) reprezentowanej wartości. Solidna „odwracalna” procedura formatowania dziesiętnego na ciąg powinna obliczyć, jak daleko jest wartość wyjściowa od prawidłowej wartości, i kontynuować wyprowadzanie cyfr, aż wynik znajdzie się w granicach 0,375 (ULP), jeśli nie 0,25 (ULP). W przeciwnym razie może wygenerować ciąg, który niektóre metody konwersji przetworzą poprawnie, ale inne metody konwersji nie.

Lepiej czasami wyprowadzić cyfrę, która może nie być „konieczna”, niż wypisać wartość, która może być źle zinterpretowana. Kluczową częścią jest to, że decyzja o tym, ile cyfr powinna być wyprowadzona, powinna być podejmowana na podstawie obliczeń numerycznych związanych z procesem wyjściowym, a nie na podstawie próby jednej konkretnej metody konwersji łańcucha z powrotem na liczbę.

supercat
źródło
1
Twój przykład nie zwraca oryginalnej wartości, o którą prosi OP. Zwraca jedynie wartość tego samego typu, obliczoną na podstawie wielu danych wejściowych.
CJ Dennis,
2

Różne powody

  1. Jest to bezcelowe i zwiększa złożoność - zarówno pod względem ilości kodu do pisania i obsługi, jak i ilości potrzebnego czasu procesora

    1. Może stracić dokładność lub gorzej, całkowicie zepsuć wartość

    2. Marnuje pamięć (potencjalnie, w zależności od języka), gdy kończy się przechowywanie większej liczby reprezentacji liczby, której potrzebujesz

Dobrą praktyką jest utrzymanie pierwszej, najdokładniejszej możliwej reprezentacji dla wszystkich otrzymywanych danych. Wykonuj wszelkie obliczenia przy użyciu tych danych i zawsze konwertuj je tylko wtedy, gdy chcesz je wydrukować lub wyświetlić w łatwiejszym do odczytania formacie.

Jon Story
źródło
wydaje się, że nie dodaje to nic istotnego w stosunku do punktów poczynionych i wyjaśnionych we wcześniejszych odpowiedziach
gnat
2
Czy to uzasadnia głosowanie w dół? Wierzę, że mój post jest potencjalnie bardziej zwięzły
Jon Story
wcześniejsze odpowiedzi w rzeczywistości wydają mi się bardziej zwięzłe, oba
gnat
0

Czemu? Ponieważ nawet najlepsi z nas mogą popełniać błędy.

Zobacz, co się stało, gdy Microsoft próbował zaimplementować format „w obie strony”, aby upewnić się, że konwersje typu float <-> są bezpieczne: https://stackoverflow.com/q/24299692/541686

Mehrdad
źródło
0

Kiedy byłem w szkole (i po szkole elektrotechnicznej), uczono nas, jak się dzielić po pomnożeniu. Podziel często wiele cyfr i zaokrąglij. Mnożenie po podziale zwielokrotnia błąd podziału.

Konwersje typów są takie same, ryzykujesz utratę danych. CInt (1.3) = 1.

W moim języku Basic wykonujemy tylko konwersje typów (program VB6 spędza 90% czasu na konwersji ANSI / Unicode, dla wszystkich wywołań API wykonywanych przez środowisko wykonawcze).

Konwersja typów jest implikowana we wszystkim, co robimy.

 Print 5

Ciąg „5” jest drukowany z literału numerycznego.

form1.caption = "My Form"

Dosłowny ciąg znaków Unicode jest konwertowany na ciąg ANSI i wysyłany do SetWindowsTextA przez pakiet form.

Nawet to działa w zasadzie

a = "5"
b = 3

c = a + b (= 8)

Obecnie jestem programistą wariantowym - nawet nie myślę o typie. Po prostu polegam na automatycznych konwersjach.

W każdym razie moje 3 rękawy dla zwierząt są

Przypisywanie literałów łańcuchowych do zmiennych w celu ich użycia (marnuje pamięć i spowalnia)

Bezcelowe funkcje, kiedy kod może być wbudowany (a kompilator prawdopodobnie cofnie twoją funkcję i wstawi ją i tak)

Ustawienie wszystkich obiektów na nic jako ostatnich linii przed funkcją końcową lub końcem programu.

i czwarty dla krótkich programów

Bezcelowe ściemnianie 3 zmiennych w programie 5-liniowym.

triggeradeadcat
źródło