Переваги синтаксису мови зліва направо


18

Я дивився інтерв’ю з Herb Sutter на Channel9, і він згадав у кінці відео, що синтаксис мови зліва направо опиниться вгорі у його списку бажань для майбутнього стандарту C ++ (хоча він визнає, що зміна C ++ таким чином майже би зробив для зовсім іншого звіра).

Окремо від:

  • більш зрозумілий людям, зрозуміліший неозброєним оком; наприклад

    //C syntax
    
    /*pointer to function taking a pointer to function(which takes 2 integers as 
    
    arguments and returns an int), and an int as arguments and returning an int*/
    
    int (*fp)(int (*ff)(int x, int y), int b)
    
    //Go analogous syntax which is left to write
    
    f func(func(int,int) int, int) int
  • простіший аналіз (призводить до кращої підтримки інструментів, як згадується у відео - наприклад, рефакторинг коду)

які ще переваги є синтаксисом "зліва направо" на мові програмування. Я знаю лише про Pascal і Go, що використовують такий синтаксис (і Go навіть не йде повним шляхом, як я розумію з цієї публікації в блозі, з якої я взяв також приклади) Чи було б можливо, щоб система мов програмування таких систем була такою синтаксису?


1
haskell використовує зліва направо:f :: (Int -> Int -> Int) -> Int -> Int
Каролі Хорват

1
ActionScript , а також: function strlen(s:String):int {...}. Також набрали лямбда-числення (звідси Haskell).
outis

2
Хтось може пояснити, будь ласка, закриття голосів :)? Я не бачу причин для його закриття, але, можливо, я задаю "неправильне" питання.

1
Я не голосував за закриття, але, коментар @Devjosh доречний, це більше підходить для програмістів, сподіваємось, хтось перенесе це ....
Нім

3
@Frank: і не забувайте, що у разі покажчиків функцій, синтаксис незручний, оскільки фактичний тип розділений ! Це низький удар ...
Матьє М.

Відповіді:


12

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

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

// common way of obtaining the static size in elements of an array:
template <typename T, int N>
char (&array_size_impl( T (&)[N] ))[N];
// alternative parser:
template <typename T, int N>               // this would probably be changed also
array_simple_impl function( T [N] & ) char [N] &;

І буде менше шансів на нерозуміння (як на найпокутніший розбір ):

// Current C++
type x( another_type() );      // create an instance x of type passing a
                               // default constructed another_type temporary?
                               // or declare a function x that returns type and takes as argument
                               // a function that has no arguments and returns another_type
// How the compiler reads it:
x function( function() another_type ) type;

// What many people mean:
x type { another_type{} };

Використовуючи аналогічний підхід до рівномірної ініціалізації в C ++ 0x (тобто {}для ідентифікації ініціалізації). Зауважте, що з підходу зліва направо набагато зрозуміліше, що ми визначаємо. Багато людей (мене точно) у будь-який момент укусив ця помилка розбору в минулому (не один раз), і це не було б з синтаксисом зліва направо.


Як щодо виразів? Як би цей синтаксис "зліва направо" впливав на пріоритет оператора та порядок оцінки?

1
@celavek: Якщо ви повернетесь до інтерв'ю, ви зауважите, що він не хоче змінювати весь синтаксис мови, лише оголошення s та визначення s. Звичайно, що може проникнути в інші вирази, я не надто впевнений, що останній рядок у наведених вище прикладах є належним зліва направо (подумайте, як створюється тимчасовий, який може знадобитися змінити ... у C # це вирішується шляхом надання newоператору двох семантик для structабо class, що не застосовується до C ++, оскільки у C ++ немає відмінностей за
типовими

5

Як ми тут потрапили

Синтаксис C для оголошення функціональних точок був призначений для відображення дзеркального використання. Розглянемо звичайну декларацію функції на зразок цього <math.h>:

double round(double number);

Щоб мати змінну точки, ви можете призначити її за допомогою безпеки типу

fp = round;

вам потрібно було б оголосити цю fpзмінну точки таким чином:

double (*fp)(double number);

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

Можливо, це було простіше в оригіналі C, який навіть не мав підпису функції, але не будемо повертатися туди, добре?

Місце, яке йому стає особливо неприємним, - це з'ясування того, як оголосити функцію, яка або бере аргумент, або повертає вказівник на функцію, або і те і інше.

Якщо у вас була функція:

void myhandler(int signo);

ви можете передати його до функції сигналу (3) таким чином:

signal(SIGHUP, myhandler);

або якщо ви хочете зберегти старий обробник, то

old_handler = signal(SIGHUP, new_handler);

що досить легко. Що досить легко - ні красиво, ні просто - це правильне декларування.

signal(int signo, ???)

Ну, ви просто повертаєтесь до своєї декларації функції і поміняєте ім'я на посилання на точку:

signal(int sendsig, void (*hisfunc)(int gotsig));

Оскільки ви не заявляєте gotsig, вам може бути легше прочитати, якщо ви не пропустите:

signal(int sendsig, void (*hisfunc)(int));

А може, ні. :(

За винятком того, що це недостатньо добре, оскільки сигнал (3) також повертає старий обробник, як у:

old_handler = signal(SIGHUP, new_handler);

Тож тепер ви повинні розібратися, як заявити про все це.

void (*old_handler)(int gotsig);

достатньо для змінної, якій ви збираєтесь призначити. Зауважте, що ви насправді gotsigтут не декларуєте old_handler. Тож цього справді достатньо:

void (*old_handler)(int);

Це приводить нас до правильного визначення сигналу (3):

void (*signal(int signo, void (*handler)(int)))(int);

Typedefs до рятувального

До цього часу я думаю, що всі погодиться, що це безлад. Іноді краще назвати свої абстракції; часто, справді. З правом typedefце стає набагато простіше зрозуміти:

typedef void (*sig_t) (int);

Тепер ваша змінна обробка обробника стає

sig_t old_handler, new_handler;

і ваша заява на сигнал (3) стає справедливою

sig_t signal(int signo, sig_t handler);

що раптом зрозуміло. Позбавлення від * також позбудеться деяких заплутаних дужок (і вони кажуть, що парени завжди полегшують розуміння - так!). Ваше використання все одно те саме:

old_handler = signal(SIGHUP, new_handler);

але тепер у вас є шанс зрозуміти декларації для old_handler, new_handlerі навіть signalколи ви вперше зіткнетеся з ними або вам потрібно їх написати.

Висновок

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

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

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


1
Надуманий, важко дотримуватися ... +1 за зусилля, хоча як це ілюструє те, що іноді важко отримати це право в C.
celavek

4

Я думаю, що ти дещо пропустив момент, коли ти зосередився на біті зліва направо.

Проблема C і C ++ - це жахлива граматика, яку вони важко читати (люди) та аналізувати (інструменти).

Маючи більш послідовний (або регулярний) ) граматику, це полегшує обом. А простіший аналіз означає простіший інструментарій: більшість сучасних інструментів не отримують права C ++, навіть не останній плагін Eclipse, коли вони намагалися винаходити колесо ... і не вдалося, і вони, ймовірно, мають більше людей, ніж середній проект ОС.

Тож ви, мабуть, прибивали це під час зосередження на читанні та розборі ... і це велика справа :)


Це великий сюрприз, коли Eclipse все ще не може розібрати такі речі, як декларації вище. Чому вони не використовують справжній аналізатор С, як від gcc?
tchrist

@tchrist: Я виявив дві проблеми із Eclipse, і обидві, схоже, пов'язані з макросами. Можливо, більше питання про препроцесора, ніж покоління AST.
Матьє М.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.