Я прошу щодо c #, але я припускаю, що це так само і в більшості інших мов.
Хтось має чітке визначення виразів і висловлювань і в чому відмінності?
Я прошу щодо c #, але я припускаю, що це так само і в більшості інших мов.
Хтось має чітке визначення виразів і висловлювань і в чому відмінності?
Відповіді:
Вираз: Щось, що оцінює значення. Приклад: 1 + 2 / x
заява: рядок коду, який щось робить. Приклад: GOTO 100
У ранніх мовах програмування загального призначення, як FORTRAN, відмінність була кристально чистою. У FORTRAN заява була однією одиницею страти, що ви зробили. Єдина причина, яку його не називали "лінією", це тому, що іноді вона охоплювала кілька рядків. Самовираз не міг нічого зробити ... вам довелося призначити його змінній.
1 + 2 / X
помилка в FORTRAN, оскільки вона нічого не робить. Вам потрібно було щось зробити з цим виразом:
X = 1 + 2 / X
У FORTRAN не було граматики, як ми її знаємо сьогодні - ця ідея була придумана разом із формою Бекус-Наура (BNF), як частина визначення Алгола-60. У цей момент семантичне відмінність ("мати значення" проти "зробити щось") було закріплено в синтаксисі : один вид фрази - це вираз, а інший - висловлювання, і аналізатор міг їх розрізнити.
Дизайнери пізніших мов розмили розрізнення: вони дозволили синтаксичним виразам робити речі, і вони дозволили синтаксичним висловлюванням, які мали значення. Найдавніший приклад популярної мови, який досі зберігається, - це C. Дизайнери зрозуміли, що шкоди не було, якщо вам дозволили оцінити вираз і відкинути результат. У C кожен синтаксичний вираз може бути перетворений у висловлювання, проставляючи крапку з комою в кінці:
1 + 2 / x;
це абсолютно законний вислів, хоча абсолютно нічого не відбудеться. Так само і в C вираз може мати побічні ефекти - він може щось змінити.
1 + 2 / callfunc(12);
бо callfunc
може просто зробити щось корисне.
Як тільки ви дозволите, що будь-який вираз є висловлюванням, ви також можете дозволити оператору призначення (=) всередині виразів. Ось чому C дозволяє робити такі речі
callfunc(x = 2);
Це оцінює вираз x = 2 (присвоївши значення від 2 до x), а потім передає цю (2) функції callfunc
.
Це розмивання виразів і висловлювань відбувається у всіх C-похідних (C, C ++, C # і Java), які все ще мають деякі висловлювання (як while
), але які дозволяють використовувати майже будь-який вираз як вираз (у призначенні лише C #, вирази виклику, збільшення та декременту можуть використовуватися як заяви; див. відповідь Скотта Вішневського ).
Наявність двох "синтаксичних категорій" (що є технічною назвою для виду речей і виразів) може призвести до дублювання зусиль. Наприклад, С має дві форми умовної форми висловлювання
if (E) S1; else S2;
і форму виразу
E ? E1 : E2
І іноді люди хочуть дублювання, якого там немає: наприклад, у стандартному С, лише оператор може оголосити нову локальну змінну - але ця здатність є достатньо корисною, щоб компілятор GNU C надав розширення GNU, що дозволяє вираження оголосити локальна змінна.
Дизайнерам інших мов не сподобався подібний тип дублювання, і вони рано побачили, що якщо вирази можуть мати як побічні ефекти, так і значення, то синтаксичне розрізнення висловлювань і виразів не все так корисно - тому вони позбулися цього . Haskell, Icon, Lisp та ML - це всі мови, у яких немає синтаксичних висловлювань - вони мають лише вирази. Навіть структуровані циклічні та умовні форми класу вважаються виразами, і вони мають значення, але не дуже цікаві.
callfunc(x = 2);
переходить x
на callfunc
, ні 2
. Якщо x
це поплавок, callfunc(float)
буде називатися, не callfunc(int)
. І в C ++, якщо ви переходите x=y
до func
та берете func
посилання та змінюєте його, воно змінюється x
, а не y
.
where
пункт у haskell вважається виразом, а не висловом. learnnyouahaskell.com/syntax-in-functions#where
where
, що насправді є частиною декларації функції, а не виразу чи висловлення.
Зауважте, що в C "=" - це власне оператор, який робить дві речі:
Ось уривок із граматики ANSI C Ви можете бачити, що у C не так багато різних типів висловлювань ... більшість висловлювань у програмі - це висловлення виразів, тобто вираз з крапкою з комою.
statement
: labeled_statement
| compound_statement
| expression_statement
| selection_statement
| iteration_statement
| jump_statement
;
expression_statement
: ';'
| expression ';'
;
Вираз - це те, що повертає значення, тоді як вислів - ні.
Наприклад:
1 + 2 * 4 * foo.bar() //Expression
foo.voidFunc(1); //Statement
Велика угода між двома полягає в тому, що ви можете зв'язувати вирази разом, тоді як заяви не можуть бути ланцюговими.
foo.voidFunc(1);
- вираз із значенням пустоти. while
і if
є твердженнями.
return
це вважається заміною.
Ви можете знайти це у вікіпедії , але вирази оцінюються до деякого значення, тоді як заяви не мають оціненого значення.
Таким чином, вирази можна використовувати у висловлюваннях, але не навпаки.
Зауважте, що деякі мови (такі як Lisp, і я вважаю Ruby, та багато інших) не розмежовують твердження проти виразу ... у таких мовах все є виразом і може бути пов'язане з іншими виразами.
Для пояснення важливих відмінностей у складостійкості (ланцюговості) виразів та висловлювань, моїм улюбленим посиланням є документ про нагороду Джона Бекуса Тюрінга, чи можна програмування звільнити від стилю фон Неймана? .
Імперативні мови (Fortran, C, Java, ...) підкреслюють висловлювання для структурування програм і мають вирази як своєрідне задум. Функціональні мови підкреслюють вирази. Чисто функціональні мови мають такі потужні вирази, ніж твердження можна усунути взагалі.
Вираз можна оцінити, щоб отримати значення, тоді як заяви не повертають значення (вони недійсні ).
Вирази виклику функцій також можна вважати висловлюваннями, звичайно, але якщо середовище виконання не має спеціальної вбудованої змінної для утримання повернутого значення, немає можливості отримати його.
Мови, орієнтовані на твердження, вимагають, щоб усі процедури були переліком тверджень. Мови, орієнтовані на вираз, - це, мабуть, усі функціональні мови, - це списки виразів, або, у випадку LISP, один довгий S-вираз, який представляє список виразів.
Хоча обидва типи можуть складатися, більшість виразів можна складати довільно, доки типи збігаються. У кожного виду висловлювань є свій спосіб складання інших висловлювань, якщо вони можуть зробити це все. Передбачте, і якщо для заяв потрібна або одна стаття, або всі підпорядковані висловлювання входять у блок тверджень, один за одним, якщо тільки субстатації не дозволяють використовувати власні субстатації.
Виписки можуть також включати вирази, де вираз насправді не включає жодних висловлювань. Однак, одним з винятків буде лямбда-вираз, який представляє функцію, і тому може включати все, що функція може виключати, якщо мова не допускає лише обмежених лямбда, як лямбди з одновиразом Python.
У мові на основі виразів все, що вам потрібно, - це єдине вираження для функції, оскільки всі структури управління повертають значення (багато з них повертають NIL). У зворотному операторі немає необхідності, оскільки останнім оціненим виразом функції є повернене значення.
Void
не є нижчим типом. Дивіться мою відповідь .
null
)? Чи не void
буде схожий на тип одиниці (але з її єдиним значенням недоступний)?
void
тип повернення функції, яка ніколи не повертається (наприклад, функція, яка throw
помилка), це нижній тип . В іншому випадку void
це тип одиниці . Ви правильні, що твердження, яке не може розходитися, має тип одиниці. Але твердження, яке може розходитися, є нижчим типом. Через теорему зупинки ми зазвичай не можемо довести, що функція не розходиться, тому я думаю, що одиниця - це вигадка. Нижній тип не може мати значення, тому він не може мати єдине значення null
.
null
Значення дійсно pseudovalue позначаючи , що посилання вказує на те , що не існує.
Просто: вираз оцінюється до значення, а вираз - ні.
{}
це твердження. Введення слова в лапки від цього не змінює цього. Висловлювання - це синтаксичні конструкції із семантикою. Немає такого поняття, як "семантичний шар" - ти, схоже, маєш на увазі виконання . Ви кажете, що намагаєтесь бути точним, але в цьому вам не вдалося. Ваша скарга на "необізнаність виборців" є чистою hominem; у вас немає інформації про психічні стани низових людей.
{}
визначається як висловлювання в мові C #.
Дещо про мови, засновані на виразах:
Найголовніше: все повертає значення
Немає різниці між фігурними дужками і дужками для розмежування кодових блоків та виразів, оскільки все є виразом. Це не перешкоджає лексичному оцінюванню, хоча: локальна змінна може бути визначена для виразу, в якому міститься її визначення, і всіх тверджень, що містяться в ньому, наприклад.
У мові, що базується на виразі, все повертає значення. Спочатку це може бути трохи дивним - Що (FOR i = 1 TO 10 DO (print i))
повертає?
Деякі прості приклади:
(1)
повертає 1
(1 + 1)
повертає 2
(1 == 1)
повертає TRUE
(1 == 2)
повертає FALSE
(IF 1 == 1 THEN 10 ELSE 5)
повертає 10
(IF 1 == 2 THEN 10 ELSE 5)
повертає 5
Ще кілька складних прикладів:
OpenADoor(), FlushTheToilet()
або TwiddleYourThumbs()
поверне якесь світове значення, таке як ОК, Готово або Успіх.(FOR i = 1 TO 10 DO (print i))
, значення циклу for for дорівнює "10", це викликає (print i)
оцінку виразу 10 разів, щоразу повертаючи i як рядок. Остаточний час через повернення 10
, наша остаточна відповідьЧасто потрібна незначна зміна мислення, щоб отримати максимальну користь від мови, що базується на виразі, оскільки той факт, що все є виразом, дає змогу «вбудувати» багато речей
Як короткий приклад:
FOR i = 1 to (IF MyString == "Hello, World!" THEN 10 ELSE 5) DO ( LotsOfCode )
є цілком коректною заміною неекспресії
IF MyString == "Hello, World!" THEN TempVar = 10 ELSE TempVar = 5 FOR i = 1 TO TempVar DO ( LotsOfCode )
У деяких випадках макет, який дозволяє вираз на основі коду, відчуває мене набагато природніше
Звичайно, це може призвести до божевілля. У рамках хобі-проекту на мові сценаріїв на основі виразів, який називається MaxScript, мені вдалося придумати цю лінію монстрів
IF FindSectionStart "rigidifiers" != 0 THEN FOR i = 1 TO (local rigidifier_array = (FOR i = (local NodeStart = FindsectionStart "rigidifiers" + 1) TO (FindSectionEnd(NodeStart) - 1) collect full_array[i])).count DO
(
LotsOfCode
)
Висловлювання - це особливий випадок виразу, один із void
типом. Схильність мов по-різному ставитися до висловлювань часто викликає проблеми, і було б краще, якби вони були належним чином узагальнені.
Наприклад, у C # у нас є дуже корисний Func<T1, T2, T3, TResult>
перевантажений набір загальних делегатів. Але ми також маємо мати відповідний Action<T1, T2, T3>
набір, і для того, щоб боротися з цим нещасним роздвоєнням, постійно треба дублювати програмування вищого порядку.
Тривіальний приклад - функція, яка перевіряє, чи є посилання нульовим перед тим, як звернутися до іншої функції:
TResult IfNotNull<TValue, TResult>(TValue value, Func<TValue, TResult> func)
where TValue : class
{
return (value == null) ? default(TValue) : func(value);
}
Чи міг компілятор мати справу з можливістю TResult
бути void
? Так. Все, що потрібно зробити, - це вимагати, щоб за поверненням слідувало вираження типу void
. Результат default(void)
буде типовим void
, і функція, що передається, повинна мати форму Func<TValue, void>
(що було б еквівалентно Action<TValue>
).
З ряду інших відповідей випливає, що ви не можете зв'язувати твердження, як ви можете, з виразами, але я не впевнений, звідки ця ідея. Ми можемо думати про те, ;
що з’являється після операторів як оператор бінарної інфіксації, що приймає два вирази типу void
та поєднує їх у єдине вираз типу void
.
Виписки -> Інструкції слідувати послідовно
виразам -> Оцінка, яка повертає значення
Заяви в основному схожі на кроки або вказівки в алгоритмі, результатом виконання оператора є актуалізація вказівника інструкцій (так зване в асемблері)
Вирази не передбачають і порядок виконання з першого погляду, їх мета - оцінити та повернути значення. В імперативних мовах програмування оцінка виразу має порядок, але це лише через імперативну модель, але це не їх суть.
Приклади заяв:
for
goto
return
if
(усі вони означають перехід рядка (оператора) виконання на інший рядок)
Приклад виразів:
2+2
(це не означає ідею виконання, а оцінку)
Заява ,
Заява - це процедурна складова, з якої побудовані всі програми C #. Оператор може оголосити локальну змінну або константу, викликати метод, створити об'єкт або призначити значення змінній, властивості або полі.
Серія висловлювань, оточених фігурними дужками, утворює блок коду. Тіло методу - один із прикладів блоку коду.
bool IsPositive(int number)
{
if (number > 0)
{
return true;
}
else
{
return false;
}
}
Виписки в C # часто містять вирази. Вираз у C # - це фрагмент коду, що містить буквальне значення, просте ім’я або оператор та його операнди.
Вираз ,
Вираз - це фрагмент коду, який можна оцінити на одне значення, об'єкт, метод або простір імен. Два найпростіші типи виразів - це буквальні і прості назви. Буквал - це постійне значення, яке не має імені.
int i = 5;
string s = "Hello World";
І i, і прості імена, що ідентифікують локальні змінні. Коли ці змінні використовуються в виразі, значення змінної отримують і використовують для виразу.
if(number >= 0) return true; else return false;
або ще краще bool? IsPositive(int number) { if(number > 0) return true; else if(number < 0) return false; else return null;}
:)
Я віддаю перевагу значенню statement
у формальному логічному сенсі цього слова. Саме ця змінює стан однієї або декількох змінних у обчисленні, дозволяючи зробити правдиве або хибне твердження про їх значення (значення).
Я думаю, що в світі обчислень та науці взагалі завжди буде плутанина, коли вводиться нова термінологія або слова, існуючі слова "переставляються" або користувачі не знають про існуючу, усталену або "належну" термінологію щодо того, що вони описують.
Я не дуже задоволений жодною з відповідей тут. Я переглянув граматику для C ++ (ISO 2008) . Однак, можливо, для дидактики та програмування відповідей може бути достатньо для розрізнення двох елементів (хоча реальність виглядає складніше).
Висловлювання складається з нуля або більше виразів, але можуть бути й іншими мовними поняттями. Це розширена форма Backus Naur для граматики (уривок для висловлювання):
statement:
labeled-statement
expression-statement <-- can be zero or more expressions
compound-statement
selection-statement
iteration-statement
jump-statement
declaration-statement
try-block
Ми можемо побачити інші поняття, які вважаються твердженнями в C ++.
case
наприклад, це мітка-заяваif
if/else
,case
while
, do...while
,for (...)
break
, continue
, return
(може повертати вираз),goto
try/catch
блокиЦе уривок, що показує частину виразів:
expression:
assignment-expression
expression "," assignment-expression
assignment-expression:
conditional-expression
logical-or-expression assignment-operator initializer-clause
throw-expression
+
, -
, *
, /
, &
, |
, &&
, ||
, ...)throw
становище є виразом занадтоВисловлювання - це граматично завершені речення. Вирази - ні. Наприклад
x = 5
читається як "х отримує 5." Це повне речення. Код
(x + 5)/9.0
читається, "х плюс 5 все поділено на 9,0". Це не повне речення. Заява
while k < 10:
print k
k += 1
- повне речення. Зауважте, що заголовок циклу немає; "поки k <10", є підрядним пунктом.
while
є виразом деяких мов, таких як Scala. Ви плутаєте граматику з набиранням тексту. Дивіться мою відповідь .
while
з тілом все ще є виразом у Scala. Це також може бути твердженням, якщо воно створює побічні ефекти, що дозволяє моя сильно прихильна відповідь (вираз також може бути твердженням). Моя відповідь - єдино правильна. Вибачте за всіх тих читачів, які не можуть зрозуміти.
(x + 5)/9.0
безумовно , може стояти окремо як твердження. Крім того, якщо під граматичним завершенням ви маєте на увазі дійсну програму, C не дозволяє операторам окремо стояти як одна програма.
Ось один із найпростіших відповідей, які я знайшов.
Спочатку відповів Anders Kaseorg
Оператор - це повний рядок коду, який виконує якусь дію, а вираз - це будь-який розділ коду, який оцінює значення.
Вирази можна комбінувати «горизонтально» у більші вирази за допомогою операторів, тоді як висловлювання можна комбінувати лише «вертикально», записуючи одне за одним, або з блоковими конструкціями.
Кожен вираз може використовуватися як вислів (ефект якого полягає в оцінці виразу та ігноруванні отриманого значення), але більшість висловлювань не можна використовувати як вирази.
Фактичною основою цих понять є:
Вирази : синтаксична категорія, екземпляр якої можна оцінити до значення.
Заява : синтаксична категорія, екземпляр якої може бути пов'язаний з оцінкою виразу, і отримане значення оцінки (якщо воно є) не гарантується.
Окрім самого початкового контексту для FORTRAN на початку десятиліть, як визначення виразів, так і твердження у прийнятій відповіді, очевидно, помилкові:
sizeof
оператора, ніколи не оцінюється.(BTW, я хочу додати [цитування необхідних] до цієї відповіді, що стосується матеріалів про C, оскільки я не можу згадати, чи є у DMR такі думки. Здається, це не так, інакше не повинно бути причин для збереження дублювання функціональності в дизайні C : помітно, оператор кома проти тверджень.)
(Наступне обґрунтування не є прямою відповіддю на початкове запитання, але я вважаю за потрібне уточнити щось, про що вже відповіли.)
Тим не менш, сумнівно, що нам потрібна конкретна категорія "висловлювань" мовами програмування загального призначення:
begin
у схемі) або синтаксичним цукром монадичних структур.++i + ++i
безглузду C.)То чому заяви? Так чи інакше, історія вже безлад. Здається, більшість мовних дизайнерів не ставляться до свого вибору ретельно.
Гірше, що він навіть дає деяким ентузіастам системного типу (які недостатньо знайомі з історією PL) деякі помилки, що типові системи повинні мати важливе значення для більш важливих конструкцій правил з оперативної семантики.
Серйозно, міркування залежно від типів у багатьох випадках не так вже й погано, але особливо не конструктивні в цьому спеціальному. Навіть експерти можуть накрутити речі.
Наприклад, хтось наголошує на чітко набраному характері як на центральному аргументі проти традиційного поводження з небажаним продовженням . Хоча висновок дещо розумний, і розуміння складених функцій добре ( але все ще далеко наївне до суті ), цей аргумент не є надійним, оскільки він повністю ігнорує підхід "бічного каналу" на практиці, як _Noreturn any_of_returnable_types
(у С11) для кодування Falsum
. І строго кажучи, абстрактна машина з непередбачуваним станом не тотожна "розбитому комп'ютеру".
У мові програмування, орієнтованої на твердження, блок коду визначається як список операторів. Іншими словами, оператор - це фрагмент синтаксису, який можна помістити всередині кодового блоку, не викликаючи синтаксичної помилки.
Вікіпедія визначає словосполучення аналогічно
У комп’ютерному програмуванні оператор - це синтаксична одиниця імперативної мови програмування, яка виражає певні дії, які слід здійснити. Програма, написана такою мовою, формується послідовністю одного або декількох висловлювань
Зауважте останнє твердження. (хоча "програма" в цьому випадку технічно неправильна, оскільки і C, і Java відкидають програму, яка не складається з нічого з тверджень.)
Вікіпедія визначає вираз слова як
Вираз у мові програмування є синтаксичним утворенням, яке може бути оцінено для визначення його значення
Це, однак, помилково, оскільки в Котліні throw new Exception("")
це вираз, але, коли його оцінюють, він просто кидає виняток, ніколи не повертаючи жодного значення.
У статизованій мові програмування кожен вираз має тип. Однак це визначення не працює в динамічно набраній мові програмування.
Особисто я визначаю вираз як фрагмент синтаксису, який можна скласти з оператором або функцією викликів, щоб отримати більший вираз. Це насправді схоже на пояснення вираження у Вікіпедії:
Це комбінація однієї або декількох констант, змінних, функцій та операторів, які програма програмування інтерпретує (відповідно до її специфічних правил пріоритетності та асоціації) та обчислює для отримання ("повернення" у стані) інше значення
Але проблема полягає в мові програмування на C, задану функцію ExecuteSomething like this:
void executeSomething(void){
return;
}
Це executeSomething()
вираз чи це твердження? Згідно з моїм визначенням, це твердження, тому що, як визначено у довідковій граматиці Microsoft C,
Ви не можете використовувати (неіснуюче) значення виразу, який має тип void будь-яким способом, а також не можете перетворити недійсний вираз (шляхом неявного або явного перетворення) в будь-який тип, крім void
Але ця ж сторінка чітко вказує, що такий синтаксис є виразом.
Для вдосконалення та затвердження моєї попередньої відповіді, визначення термінів мови програмування слід пояснювати з теорії типів інформатики, коли це застосовується.
Вираз має тип, відмінний від типу Bottom, тобто має значення. Заява має тип Unit або Bottom.
З цього випливає, що оператор може мати будь-який ефект у програмі лише тоді, коли створює побічний ефект, тому що він або не може повернути значення, або лише повертає значення типу Unit, яке або неможливо визначити (у деяких мовах таке a C void
) або (наприклад, у Scala) можуть бути збережені для затримки оцінки твердження.
Очевидно, що a @pragma
або a /*comment*/
не мають типу, і тому вони відрізняються від тверджень. Таким чином, єдиним типом твердження, яке не матиме побічних ефектів, було б недіяння. Недіяльність корисна лише як заповнювач для майбутніх побічних ефектів. Будь-яка інша дія через висловлювання буде побічним ефектом. Знову ж натяк компілятора, наприклад @pragma
, не є твердженням, оскільки він не має типу.
@pragma
або /*comment*/
є логічно непослідовними.
Найбільш точно, оператор повинен мати «побічний ефект» (тобто бути обов'язковим ) та вираз має мати на значення типу (тобто не нижній тип).
Типу заяви є типом блоку, але з - за зупиняючи теореми блоку вигадки так що дозволяє сказати нижній тип .
Void
це не саме нижній тип (це не підтип усіх можливих типів). Він існує в мовах, які не мають повністю звукової системи . Це може звучати як вислів снобізму, але повнота, наприклад, анотації з дисперсією, є критично важливою для написання розширюваного програмного забезпечення.
Давайте подивимось, що Вікіпедія має сказати з цього приводу.
https://en.wikipedia.org/wiki/Statement_(computer_science)
У комп'ютерному програмуванні заява - це найменший окремий елемент імперативної мови програмування, який виражає певну дію, яку слід здійснити.
Багато мов (наприклад, C) роблять відмінність між операторами та визначеннями, при цьому оператор містить лише виконуваний код та визначення, що декларує ідентифікатор, тоді як вираз оцінює лише значення.
pass
є твердженням. Це неоперація, і вона нічого не оцінює.