Код, який перетворює значення в інше представлення, а потім перетворює його туди, де воно почалося, погано, але як? [зачинено]


35

Я читав статтю про погану практику програмування .

У ньому згадувалося -

"Код Йо-Йо", який перетворює значення в інше представлення, а потім перетворює його туди, де воно розпочалося (наприклад: перетворення десяткової у рядок, а потім назад у десяткову або додавання рядка та обрізання)

Я не розумію, чому той конкретний приклад, який він дає, - це поганий спосіб написання програм. Мені здається нормальним конвертувати назад, якщо ситуація цього вимагає, щоб значення можна було використовувати.

Хтось може пояснити більше про це?


4
рекомендуємо прочитати: Обговори це $ {blog}
gnat

8
Здебільшого це просто зайве, і це відбувається лише тому, що програміст не знав кращого способу отримати те, що хотів. Запис в блозі дає типовий приклад кілька абзаців пізніше: "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?- яка б це була ситуація?
Конрад Моравський

3
@gnat Я не розумію, як це робить це поганим питанням. Якщо ви хочете, я можу відредагувати його, щоб сказати, "це код, який перетворює і перетворює назад значення погано?" і він більше не підійде до цього шаблону.
djechlin

5
Нещодавно я знайшов у Java код, який ітераціює через масив, серіалізуючи кожен об'єкт у об'єкт JSON, використовуючи конкатенацію рядків, а не серіалізатор JSON. Потім результат був переданий приватному методу, який розбирав масив JSON, щоб витягти купу ідентифікаторів, а потім передав ідентифікатор десь в іншому місці. Це єдине використання цього масиву JSON в системі. Це йо-йо код. Причин для трансформації назад і назад не було. Ми могли просто передати ідентифікатори від оригінальних об'єктів.
Брендон

3
decimal myValue = decimal.Parse(dataReader["myColumn"].ToString())є домашнім вихованцем мого.
Метью

Відповіді:


125

Навіть якщо ви робите потреба як числовий і строкове представлення числа, то краще конвертувати тільки один раз , а також повісити на початкове значення, замість того , щоб знову перетворити кожен раз , коли вам потрібно один або інший.

Принцип полягає в тому, що, як завжди, той код, який не існує, не може мати тонких дефектів , тоді як код, який існує часто, є. Це може здатися параноїдальним, але досвід вчить нас, що це доречно. Якщо ви підходите до програмування з постійним легким занепокоєнням "Я не дуже розумний, щоб зрозуміти цю складну систему", ви на правильному шляху.


5
Добре сказано. Ми, програмісти, повинні бути настільки обережними.
Ніл

58
"код, який не існує, не може мати тонких дефектів, тоді як код, який існує часто, бажає, щоб я міг вас за це +2. Ніколи не занижуйте значення того, що не потрібно писати код.
Бенджамін Грюнбаум

2
Але виконати пару простих операцій (перетворити на рядок і назад) може бути набагато менш складним (простіше зрозуміти і кодувати), ніж "правильний" спосіб маніпулювання бітами. Зберігання всіх даних категорії в одній формі часто є хорошою ідеєю, навіть якщо певна дата неминуче буде перетворена на інші форми.
Даніель Р Хікс

36
@DanielRHicks, тож давайте перетворимо цю просту дату (10 листопада 2014 р.) У рядок, -> 10-11-2014 і повернемося до дати -> (11 жовтня 2014 р.), Чекайте, що?
Пітер Б

20
@PieterB Існує велике німецьке програмне забезпечення для бухгалтерського обліку, яке не працює на комп'ютерах з німецькою мовою, тому що це робить. Він спочатку перетворює дату в рядок за допомогою системного локалу, а потім намагається розібрати її з фіксованим локальним словом та скаржиться на незаконний формат. Це робиться так само з числами і різними десятковими роздільниками, за винятком того, що він не скаржиться, він псує дані і проявляє дивну поведінку. Мені потрібні дні, щоб зрозуміти це.
CodesInChaos

23

Це погано з трьох основних причин:

  1. Це показує, що ви не замислювалися над тим, яким типом / форматом має бути насправді змінна, але замість цього перетворюєте її на те, що вам потрібно в цей момент. Це свідчить про відсутність дизайнерської думки.
  2. Це, мабуть, марно. Ви майже напевно витрачаєте цикли та рядки коду, роблячи перетворення, які там не потребують. Це зробить ваш код повільнішим і більш роздутим, ніж потрібно.
  3. Перетворення типів схильні до тонких помилок. Вказуючи ці конверсії через свій код, ви збільшуєте ймовірність помилок.

Я підозрюю, що причина 1 - це причина, по якій ваш джерело думав, виходячи з контексту, в якому воно згадувалося.


6

Я б перефразував опис як "код, який перетворює тип в інше представлення для того, щоб зробити щось, що могло бути зроблено так само добре чи краще в оригіналі, а потім перетворити його назад. Є багато ситуацій, коли щось перетворюється на різного типу, діючи на нього і перетворюючи його назад цілком доречно і невдача зробити це може привести до неправильного поводження.

Як приклад, коли конверсія хороша.
Одне має чотири floatзначення довільних знаків, величини яких можуть відрізнятись в коефіцієнт до 1000, і на останньому місці потрібно обчислити суму в межах 0,625 одиниць. Перетворення всіх чотирьох значень double, обчислення суми та перетворення результату назад floatбуде набагато ефективнішим, ніж будь-який підхід, використовуючи floatокремо.
плаваючою комою в кращому випадку точні до 0,5 одиниці останнього місця (ULP). Цей приклад вимагає, щоб найгірша помилка округлення була не більше ніж на 25% вище оптимальної помилки в гіршому випадку. Використання подвійного дасть значення, яке буде точним в межах 0,5001 ULP. Хоча вимога 0,625 ULP може здатися надуманою, такі вимоги часто важливі в алгоритмах послідовного наближення. Чим чіткіше задана помилка, тим нижча вимога до ітерації в гіршому випадку.

Як приклад, коли конверсія погана:
один має число з плаваючою комою і бажає вивести рядок, який буде однозначно представляти його значення. Один із підходів - перетворити число в рядок з певною кількістю цифр, спробувати перетворити його назад і побачити, чи відповідає результат.

Але це насправді поганий підхід. Якщо десятковий рядок являє собою значення, яке майже точно розташоване на півдорозі між двома значеннями з плаваючою комою, метод методу string-to float є досить дорогим, щоб гарантувати, що він завжди буде ближчеfloat значення, і багато таких методів перетворення не не підтримувати таку гарантію (крім іншого, якщо це зробити, в деяких випадках вимагається прочитати всі цифри числа, навіть якщо це було довгі мільярди цифр).

Метод набагато дешевше гарантувати, що він завжди поверне значення, яке знаходиться в межах 0,5625 одиниць, на останньому місці (ULP) від представленого значення. Надійний "оборотний" режим форматування десяткових рядків повинен обчислювати, наскільки вихідне значення від правильного значення, і продовжувати виводити цифри, поки результат не буде в межах 0,375 (ULP), якщо не 0,25 (ULP). В іншому випадку він може вивести рядок, який деякі методи перетворення оброблять правильно, але інші методи перетворення не будуть.

Краще іноді виводити цифру, яка може не бути "необхідною", ніж виводити значення, яке може бути неправильно інтерпретоване. Ключова частина полягає в тому, що рішення про те, скільки цифр має бути виведено, має прийматися на основі чисельних обчислень, пов'язаних з процесом виведення, а не на основі спроби одного конкретного методу перетворити рядок назад у число.


1
Ваш приклад не повертає початкового значення, про яке задається ОП. Він просто повертає значення одного типу, обчислене з декількох входів.
CJ Dennis

2

Різні причини

  1. Це безглуздо і додає складності - як в кількості коду для запису та обслуговування, так і в кількості необхідного часу процесора

    1. Він може втратити точність або ще гірше, цілком зіпсувати значення

    2. Він витрачає пам'ять (можливо, залежно від мови), оскільки ви в кінцевому підсумку зберігаєте більше представлень потрібного числа

Доброю практикою є лише збереження першого, найточнішого можливого представлення будь-яких даних, які ви отримуєте. Виконайте будь-які обчислення, використовуючи ці дані, і лише коли-небудь конвертуйте їх, якщо вам потрібно вивести їх або відобразити у більш легкому для читання форматі.


це , здається, не додає нічого суттєвого по точкам зроблених і пояснено в попередніх відповідях
комар

2
Чи це виправдовує зниження голосу? Я вважаю, що моя публікація потенційно більш лаконічна
Історія Джона

попередні відповіді насправді виглядають більш лаконічно, обидві
гнат

0

Чому? Тому що навіть найкращі з нас можуть помилитися.

Подивіться, що сталося, коли Microsoft намагалася реалізувати формат "туди-назад" спеціально для того, щоб переконатися, що перетворення рядка <-> в безпеку: https://stackoverflow.com/q/24299692/541686


0

Коли я був у школі (і після школи з електротехніки), нас вчили ділити після множення. Відділення часто багато цифр і округлюються. Множення після ділення множить на помилку ділення.

Конверсії типів однакові, ви ризикуєте втратити дані. CInt (1.3) = 1.

У моїй мові Basic, ми робимо лише конверсії (програма VB6 витрачає 90% часу, роблячи перетворення ANSI / Unicode для всіх дзвінків API, які виконуються під час виконання).

Перетворення типів має на увазі все, що ми робимо.

 Print 5

Рядок "5" виводиться з друкованого числа.

form1.caption = "My Form"

Літеральний рядок unicode перетворюється в рядок ANSI і надсилається SetWindowsTextA пакетом форм.

Навіть це працює в основному

a = "5"
b = 3

c = a + b (= 8)

Я сьогодні програміст варіантів - я навіть не думаю про тип. Я просто покладаюся на автоконверсії.

У будь-якому випадку мої 3 домашні вихованці є

Призначення рядкових літералів змінним для їх використання (витрачає пам'ять і повільно)

Безглузді функції, коли код може бути вбудованим (а компілятор, ймовірно, скасує вашу функцію і все одно вбуде в неї вбудований)

Встановлення нічого об'єктів як останні рядки перед кінцевою функцією або кінцем програми.

і четвертий для коротких програм

Безглуздо затемнення ваших 3 змінних у програмі на 5 рядків.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.