Чи потрібні певні типи?


20

Одне, що трапилось мені днями, це конкретні типи, які все ще потрібні, або спадщина, яка стримує нас. Що я маю на увазі: чи нам справді потрібні короткі, int, long, bigint і т.д. і т.д.

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

Поплавок, реальний і подвійний - трохи складніше, оскільки тип залежить від точної точності. Разом з тим, рядки повинні мати можливість зайняти меншу кількість пам'яті у багатьох випадках (у .Net), де в основному використовується ascii, але бутові струни завжди займають подвійну пам'ять через кодування Unicode.

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

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


6
PHP, Ruby, Perl та інші не вимагають від вас констатувати типи змінних. Навколишнє середовище розраховує це для вас.
FrustratedWithFormsDesigner

7
Рядки Unicode не повинні займати додаткову пам'ять, коли вони випадково використовуються лише для ASCII (UTF-8).

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

Це нагадує мені цей проект: tom.lokhorst.eu/media/…
LennyProgrammers

4
Що з Ада? type hour is range 0 .. 23;
mouviciel

Відповіді:


12

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

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


1
Ознайомтеся з кодовими контрактами в .NET 4.0.
Стівен Євріс

+1 Що стосується зберігання / передачі даних (колишня мережа), обмеження є основними для досягнення максимальної ефективності протоколу / реалізації. Крім того, є багато підстав для отримання, якщо доступні набрані колекції. Крім цього, можна припустити, що ефективність може мати заднє сидіння (особливо якщо це зменшує можливість семантичних помилок).
Еван Плейс

9

Типи адаптації означають логіку, щоб виконати адаптацію, означає працювати під час виконання, щоб запустити цю логіку (час шаблонування та компіляції потребує конкретного типу, умовивід типу - особливий випадок, коли ви отримуєте найкраще з двох світів). Ця додаткова робота може бути нормальною в умовах, коли продуктивність не є критичною, а система зберігає розумні розміри. В інших середовищах це не так (вбудовані системи - це одне, де колись для використання процесора процесора вам доведеться використовувати 32/64-бітні цілочисельні типи та цілісні типи 8/16 біт для оптимізації резервного копіювання статичної пам'яті).

Навіть мови загального призначення, які підтримують пізнє прив'язування (дозвіл типів під час виконання, наприклад VB6), як правило, сприяють сильному введенню тексту (VB.NET) через хит продуктивності, який раніше виникав під час зловживання запізнілим зв'язуванням, і тому, що ви часто в кінцевому підсумку з некрасивим кодом, коли типи не явні ( Довідка / Професійний Рефакторинг у Visual Basic - Даніель Арсеновський ).


Будь ласка, визначте "автоматичне введення тексту".

@delnan: замінили автоматичне введення тексту на пізнє прив’язування, про що я мав на увазі :)
Матьє

Існує багато мов загального призначення, які вирішують типи під час виконання, Common Lisp називає лише одну. (Для ефективності ви можете оголосити типи у Common Lisp, тому це можна зробити лише в критичних розділах.)
Девід Торнлі

@David Thornley: "примусове" сильне введення тексту, можливо, було занадто сильним, "просування" було б більш доречним, відповідно оновив мою відповідь. Мова, яка дозволяє вам обирати між двома видами зв’язування залежно від ситуації, безумовно, краща, ніж вимушена тим чи іншим способом. Особливо, коли ви не займаєтесь програмуванням на низькому рівні та орієнтуєтесь на логіку.
Матьє

4

Простота, пам'ять та швидкість Коли я оголошую змінну, пам'ять для цієї змінної розподіляється в одному блоці. Щоб підтримувати змінну, що динамічно зростає, мені доведеться додати до цієї змінної поняття безперервної пам'яті (або ту, або резервувати найбільший блок, який може представляти змінна). Несумісна пам'ять знизила б продуктивність при призначенні / пошуку. Виділення найбільшого можливого було б марно в сценарії, коли мені потрібен лише байт, але система зберігає довго.

Подумайте про компроміси між масивом і вектором (або пов'язаним списком). За допомогою масиву пошук конкретної позиції є простим питанням отримання початкової позиції та переміщення вказівника пам'яті x пробілами, щоб знайти нову позицію в пам'яті. Подумайте про int як про біт [32], читаючи int, передбачає проходження через цей масив, щоб отримати всі значення бітів.

Щоб зробити тип динамічного числа, вам потрібно змінити це з масиву бітів на вектор біт. Читання вашого динамічного числа передбачає перехід до голови, отримання цього біта, запитання, де наступний біт у пам’яті, переміщення до цього місця, отримання цього біта тощо. Для кожного біта динамічного числа ви виконуєте три операції зчитування ( поточний), прочитати (адреса наступного), перемістити (наступний). Уявіть, що читаєте значення мільйона чисел. Це мільйон зайвих операцій. Це може здатися незначним. Але подумайте про системи (як, наприклад, фінансові), де кожна мілісекунда має значення.

Було прийнято рішення, що нанесення розробника на перевірку розміру та перевірки розміру - це невелика компенсація порівняно з впливом на продуктивність системи.


1
Інша альтернатива - реалізація чисел, подібних до масивів, де масив перерозподіляється, коли число переростає поточний розмір. Крім того, ви повинні врахувати випадок, коли користувач ХОЧЕТЕ переповнювати цикл.
Майкл Браун

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

Якщо бути справедливим, це не так жорстоко, як ви робите. Cf мою майбутню відповідь.
Пол Натан

3

Для мов, орієнтованих на обладнання, потрібні конкретні типи. Одним із прикладів є протоколи мережевої мережі.

Але давайте створимо - для задоволення - тип лаку такою мовою, як C ++. Побудуйте його з new'd масиву ints.

Реалізувати додавання не важко: просто зберіть байти разом і перевірте високі біти: якщо є операція переносу, newу новому верхньому байті і перенесіть біт. Віднімання слід тривіально в поданні 2 на доповнення. (Це також відоме як суматор для переноски пульсацій).

Множення випливає аналогічно; використовувати ітеративне додавання / зміщення. Як завжди, справжній поворот у хвості - це поділ [*].

Що ви втратили, коли це трапляється?

  • Детермінований час. У вас є syscall ( new), який може спрацьовувати в точках, які не обов'язково контролюються.

  • Детермінований простір.

  • Напівпрограмне забезпечення математики відбувається повільно.

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

[*] Cf алгоритми математичної апаратури для більш швидких способів зробити це - як правило, фокус - це паралельні операції.


2

Це гарне запитання. Це пояснює, чому для такої мови, як Python, не потрібні "короткі, int, long, bigint і т. Д.": Цілі числа є, ну цілими числами (в Python 3 є один цілочисельний тип) і не мають обмеженого розміру (за винятком мови пам'ять комп’ютера, звичайно).

Що стосується Unicode, кодування UTF-8 (яка є частиною Unicode) використовує лише один символ для символів ASCII, тому це не так вже й погано.

Загалом, динамічні мови, схоже, йдуть у тому напрямку, про який ви згадуєте. Однак з міркувань ефективності в деяких випадках корисніші обмежені типи (наприклад, програми, які повинні працювати швидко). Я не бачу великих змін в осяжному майбутньому, оскільки процесори організовують дані в байти (або 2, 4, 8 і т.д. байтів).


1

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

Це приблизно те, що дає програмування OOP у типовому вигляді. Насправді в Java ви ефективно розмовляєте про класи BigIntegerта BigDecimalкласи, які виділяють простір залежно від того, скільки потрібно для зберігання об’єкта. (Як зауважив FrustratedWithFormsDesigner, багато мов сценарію ще більше проходять цей шлях і навіть не вимагають декларації типу і зберігатимуть все, що ви їм надаєте.)

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


Я розумію, що якесь динамічне / адаптивне введення тексту здається дорогим та менш ефективним, ніж у нас зараз, і використання поточних компіляторів вони, безумовно, були б. Але чи ми на 100% впевнені, що якщо ви будуєте мову та компілятор з нуля, ви не зможете їх зробити, якщо не настільки швидкими, як статично набраними, але, мабуть, можливо, швидкими вони того варті.
Хомде

1
@MKO: Чому б ти не спробував це і не побачив?
Анон.

1
Так, ви можете зробити це можливо швидко (але, ймовірно, ніколи не так швидко, як статична система для чисел). Але частина "чи варто це" складніше. Більшість людей працюють з даними, діапазон яких зручно вписується в intабо double, а якщо цього немає, вони знають про це, тому динамічне розмір значення - це особливість, за яку їм не потрібно платити.
jprete

Як всі програмісти, звичайно, я мрію про те, щоб якось день зробити власну мову;)
Homde

@jprete: Я не згоден; більшість людей не знають про можливі великі проміжні результати. Така мова може бути зроблена досить швидко для більшості цілей.
Девід Торнлі

1

Це залежить від мови. Для мов вищого рівня, таких як Python, Ruby, Erlang, і таких, у вас є лише поняття інтегральних і десяткових чисел.

Однак для певного класу мов, які мають ці типи, дуже важливі. Коли ви пишете код для читання та запису бінарних форматів, таких як PNG, JPeg тощо, вам потрібно точно знати, скільки інформації читається одночасно. Те саме з написанням ядер операційної системи та драйверів пристроїв. Не всі це роблять, і в мовах вищого рівня вони використовують бібліотеки С для детального важкого підйому.

У shortньому все ще є місце для більш конкретних типів, але багато проблем розвитку не потребують такої точності.


0

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

  • Булева
  • Номер
  • Рядок
  • Дата, час

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


0

Мови програмування рухаються в цьому напрямку. Візьмемо, наприклад, рядки. У старих мовах ви повинні оголосити розмір рядка, як PIC X(42)у COBOL, DIM A$(42)у деяких версіях BASIC або [ VAR] CHAR(42)в SQL. У сучасних мовах у вас просто один динамічно виділений stringтип і не потрібно думати про розмір.

Однак цілі лічильники різні:

Що я маю на увазі: чи нам справді потрібні короткі, int, long, bigint і т.д. і т.д.

Погляньте на Python. Він використовував для розмежування цілих чисел ( int) та довільних розмірів ( long). У 3.x колишнього немає (старе long- нове int), і його ніхто не сумує.

Але все ж є спеціалізований тип для послідовностей 8-бітних цілих чисел у вигляді bytesі bytearray. Чому б не використовувати а tupleчи listцілі числа відповідно? Щоправда, у bytesнього є додаткові рядкові схожі методи, які tupleне мають, але, безумовно, ефективність має багато спільного з цим.

Поплавок, реальний і подвійний - трохи складніше, оскільки тип залежить від точної точності.

Не зовсім. Підхід "все вдвічі точніше" дуже поширений.


1
Можливо, базові типи повинні оголошувати основний намір типу, тобто int для "звичайних" чисел, подвійних для всіх звичайних "десяткових знаків" (чи не повинні ints мати можливість десяткових знаків, хоча для простоти?) "Гроші" для роботи з сумами та байтами для роботи з двійковими даними. Обмеження типу, оголошене за допомогою атрибута, може допускати оголошення допустимого діапазону, десяткової точності, зведення нанівець та навіть дозволених значень. Було б здорово, якби ви могли створити власні та багаторазові типи таким чином
Homde

@konrad: ІМХО, причина "непідписаних" цілих чисел викликає такі головні болі в C, це те, що вони іноді використовуються для представлення чисел, а іноді використовуються для представлення членів обгорткового абстрактного алгебраїчного кільця. Наявність окремих типів "дзвінка" та "непідписане число" могло б забезпечити, що такий код, як unum64 += ring32a-ring32bзавжди, спричинить правильну поведінку, незалежно від того, чи є ціле число за замовчуванням 16 біт або 64 [зауважте, що використання +=є істотним; вираз на зразок unum64a = unum64b + (ring32a-ring32b);має бути відхилений як неоднозначний.]
supercat

0

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

Поплавок, реальний і подвійний - трохи складніше, оскільки тип залежить від точної точності. Разом з тим, рядки повинні мати можливість зайняти меншу кількість пам'яті у багатьох випадках (у .Net), де в основному використовується ascii, але бутові струни завжди займають подвійну пам'ять через кодування Unicode.

У Фортрана було щось подібне (я не знаю, чи це саме ви маєте на увазі, оскільки я справді бачу два запитання). Наприклад, у F90 вгору вам не потрібно чітко визначати розмір типу , так би мовити. Що добре, не тільки тому, що воно надає вам центральне місце для визначення типів даних, але й портативний спосіб їх визначення. REAL * 4 не є однаковим у всіх реалізаціях на всіх процесорах (а під процесором я маю на увазі компілятор CPU +), а не на longshot.

selected_real_kind (p, r) повертає значення виду реального типу даних з десятковою точністю, що перевищує принаймні p цифр, а діапазон експонентів більший принаймні r.

Так ви йдете, наприклад;

program real_kinds
integer,parameter :: p6 = selected_real_kind(6)
integer,parameter :: p10r100 = selected_real_kind(10,100) !p is precision, r is range
integer,parameter :: r400 = selected_real_kind(r=400)
real(kind=p6) :: x
real(kind=p10r100) :: y
real(kind=r400) :: z

print *, precision(x), range(x)
print *, precision(y), range(y)
print *, precision(z), range(z)
end program real_kinds

(Я думаю, що це досить зрозумілий приклад).

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

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