Можно ли инициализировать несколько переменных некоторого типа в одной строке?

Я пытался писать как можно меньше кода. Итак, я использую:

MyBoolean := Memo1.Text <> '';

вместо

if Memo1.Text = '' then
  MyBoolean := true
else
  MyBoolean := false;

Объявить и инициализировать

var
  myGlobal: integer = 99;

объявить и инициализировать глобальную переменную. Я хотел бы сделать то же самое для локальных переменных, но кажется, что это невозможно, поэтому мне интересно, есть ли способ инициализировать несколько переменных некоторого типа в одной строке, как в C

int x, y, z;
x=y=z=0;

Спасибо.


person Edijs Kolesnikovičs    schedule 27.04.2014    source источник
comment
Ну да и нет. Да для письма, например. X: Integer = 99; Y: Integer = 99; в одной строке, а не в нескольких операторах присваивания.   -  person TLama    schedule 27.04.2014
comment
Это не так много одной строки, если вы используете Ctrl + D (также известное как форматирование кода).   -  person Edijs Kolesnikovičs    schedule 27.04.2014
comment
Да, но нет возможности инициализировать несколько переменных в одном выражении. И я рад, что это так, поскольку такое утверждение, как x=y=z=0, мне довольно трудно читать. Лично я бы написал даже это в несколько строк.   -  person TLama    schedule 27.04.2014
comment
@TLama Почему x=y=z=0 трудно читать или непонятно? Это также стандартная математическая запись, и некоторые языки программирования действительно допускают такой синтаксис. Что, если вместо 0 присваиваемое значение было получено от вызова дорогостоящей (трудоёмкой) функции? Этот синтаксис потребует только одного вызова. Чтобы избежать множественных вызовов в Delphi, вам нужно создать временную переменную, присвоить ей результат функции, а затем иметь несколько строк, присваивающих временное значение переменным. Это было бы если не менее ясно, то уж точно менее изящно. В этом синтаксисе есть законная ценность.   -  person alcalde    schedule 27.04.2014
comment
Эджис, я восхищаюсь твоей приверженностью DRY (не повторяйся). К сожалению, Delphi имеет тенденцию принимать форму WET (нам нравится печатать или писать все дважды). Существует много синтаксического сахара, который язык не реализует, что может сократить дублированный код, например. тернарный оператор: x, если a else c, множественное присваивание, лучший приоритет оператора (чтобы избежать необходимости в круглых скобках в операторах с логическими операторами), оператор мощности, шаг в цикле for, строки в операторах case, нотация среза, вывод типа и т. д. Я надеюсь, что когда они перейдут на новый компилятор, мы получим некоторые из этих вещей.   -  person alcalde    schedule 28.04.2014
comment
@alcalde, почему? Я не знаю. Может быть, потому что, когда я что-то читаю, мой мозг обычно меньше сосредотачивается на том, что находится в середине выражения. Так что, может быть, я боюсь потерять эти переменные y и z при чтении, я не знаю. А насчет временных переменных, что с ними не так? Я скорее буду четко видеть написанное, чем бояться лишнего задания. Кроме того, в сгенерированном ассемблерном коде будет то же самое. Или вы думаете, что есть одна инструкция, которая будет присваивать возвращаемое значение функции сразу трем переменным?   -  person TLama    schedule 28.04.2014
comment
@alcalde: надеюсь, они этого не делают. Во-первых, если вы хотите программировать на C, то программируйте на C. Во-вторых, нехорошо перегружать язык всеми синтаксическими структурами, которые кто-то где-то может найти полезным в некоторых случаях. А в Delphi ничего не пишется дважды. Объявление не является инициализацией.   -  person Rudy Velthuis    schedule 28.04.2014
comment
@TLama Говорят, что каждая не написанная строка кода — это строка кода, которая гарантированно не содержит ошибок. :-) В python для замены переменных вы можете просто сделать a, b = b, a. Это более понятнее, чем temp := a;a :=b;b := temp, и нет возможности испортить порядок присваиваний (как мы все делаем время от времени). Меня не волнует сборка; Я забочусь о написании чистого, ясного, элегантного кода. temp := ExpensiveFunc;a := temp, b := temp; c:= temp не так лаконичен, как a = b =c = ExpensiveFunc(); Посмотрите это около четырех минут, и вы увидите прекрасный пример: youtu.be/OSGv2VnC0go?t=34m2s   -  person alcalde    schedule 28.04.2014
comment
@RudyVelthuis Идея о том, что если у любого другого языка есть хорошая идея, то нужно просто использовать этот язык и оставить Delphi навсегда сохраненным в янтаре, является частью проблемы, которая сегодня считается устаревшей. У нас не было бы дженериков, итераций или множества других полезных функций, если бы EMBT думал так. Во-вторых, рассматриваемый синтаксис не предназначен для какой-то неясной проблемы: инициализация переменных является рутиной почти в каждом типе программ. Руди, МНОГИЕ вещи в Delphi пишутся дважды. Черт возьми, разделы интерфейса и реализации сами по себе опровергают это утверждение. То же самое для предварительных объявлений.   -  person alcalde    schedule 28.04.2014
comment
@RudyVelthuis Также есть перегруженные функции, настройка параметров SQL до fireDAC - множество ADOQuery1.Parameters.ParamByName(, текущая библиотека REST имеет аналогичную проблему, вы не можете инициализировать массив, такой как x := [1,2, 3] но нужно сделать x[1] := 1; x[2] := 2; x[3] := 3, отсутствие тернарного оператора означает, что вы должны сделать, если x = 7, то z := y * 3 иначе z := y * 2 вместо z := y * (2, если x = 7, иначе 3); Если бы я хотел разделить строку и поместить результат в 3 переменные, мне пришлось бы сначала разделить ее на временную массив, затем скопируйте результаты один за другим в три переменные и т. д.   -  person alcalde    schedule 28.04.2014
comment
@alcalde: ничего не пишется дважды. Декларатон — это не определение, и в разделе реализации вам не нужно повторять параметры (хотя я бы посоветовал это сделать любому). Конечно, есть смысл заимствовать идеи из других языков. Но не все, что кому-то где-то может пригодиться. И как кто-то упоминает: присваивание - это не выражение, это оператор, поэтому x := y := z := 0 не имеет никакого смысла. Вы можете назначать константы массива переменным, но не литералы массива. В Delphi нет (и не должно быть) всех мыслимых возможностей, как и в других языках.   -  person Rudy Velthuis    schedule 28.04.2014
comment
@alcalde: Другими словами: дополнения к языку и синтаксису следует выбирать с осторожностью. Не все мыслимое должно быть сделано, чтобы сохранить язык как можно более последовательным. Никому не нужен эквивалент C++, который может многое, но сделать это должным образом чрезвычайно сложно.   -  person Rudy Velthuis    schedule 28.04.2014
comment
@alcalde сказал: Каждая не написанная строка кода — это строка кода, гарантированно свободная от ошибок. Черт, думаю, мне следует вернуться и удалить строку кода, которую я добавил на прошлой неделе. Я что-то отлаживал, и оказалось, что был забыт очень важный шаг. ;) ... Шутки в сторону, я полностью согласен с тем, что лишний код - опасная трата. Цель должна состоять в том, чтобы написать четкий однозначный код. Но не позволяйте знакомству с конкретной языковой особенностью ввести вас в заблуждение и заставить поверить, что что-то ясно. Например. Swap(a, b) намного понятнее, чем a, b = b, a.   -  person Disillusioned    schedule 29.04.2014
comment
@alcalde сказал: вы не можете инициализировать массив как x := [1,2,3]; На самом деле вы можете написать TIntegerArray.Create(1, 2, 3);. См. (stackoverflow.com/a/23361469/224704) пример. Я не знаю, когда эта функция была введена. Я узнал об этом совсем недавно. ... Дело в том, что у языков есть разные способы достижения определенной эффективности. Вместо того, чтобы пытаться встроить в язык другие функции (создавая больше способов решения одной и той же проблемы), иногда лучше узнать о различиях между языками. (Но это ни в коем случае не должно быть поводом для застоя.)   -  person Disillusioned    schedule 29.04.2014
comment
Синтаксис конструктора для динамических массивов был введен в Delphi 2005 или 2007, IIRC. Обратите внимание, что он генерирует довольно неоптимальный код, но он работает.   -  person Rudy Velthuis    schedule 02.05.2014


Ответы (4)


Вы можете инициализировать только одну локальную переменную для каждого оператора.

person David Heffernan    schedule 27.04.2014

В C присваивание представляет собой выражение (возвращает значение).

В Паскале присваивание представляет собой инструкцию (не возвращает значение).

Разница имеет некоторые интересные последствия. Например, в C оба

while (x=0)

а также

while (x==0)

являются синтаксически допустимыми конструкциями (это источник бесчисленных ошибок), в то время как в Паскале

while (x:=0)

является синтаксически недопустимым, поскольку x:=0 является оператором.

person kludg    schedule 27.04.2014
comment
Я вообще не понимаю, как это относится к вопросу. Вопрос в инициализации нескольких переменных одновременно. В Python присваивание — это оператор, и вы не можете присвоить переменную в цикле while, как в Паскале. Однако x=y=z=0 также является допустимым утверждением. Между этими двумя понятиями нет абсолютно никакой связи. - person alcalde; 27.04.2014
comment
@alcalde Я недостаточно знаком с Python, чтобы знать назначение языка, но актуальность может зависеть от того, как вы рассматриваете его функцию присваивания: (1) обрабатывается ли присваивание как выражение с синтаксической солью для предотвратить распространенные ошибки (как в цикле while)? (2) Или это рассматривается как оператор с синтаксическим сахаром для упрощения присвоения нескольких значений? ... Лично я считаю ответ достаточно актуальным и интересным с точки зрения лучшего понимания различий между языками. - person Disillusioned; 29.04.2014

Конечно, можно было бы написать функцию

function AssignInteger(var _AssignTo: integer; _Value: integer): integer;
begin
  Result := _Value;
  _AssignTo := _Value;
end;

и используйте его так:

var
  i, j, k: integer;
begin
  i := AssignInteger(j, AssignInteger(k, 5));

Но это не совсем то, что вы хотите, говоря, что вы хотите написать как можно более короткий код.

Я указываю это, тем не менее, на всякий случай.

person dummzeuch    schedule 27.04.2014

Как говорит dummzeuch, вы можете написать свою собственную процедуру. Однако я бы предпочел вариант с подписью типа: procedure AssignIntegers(AValue, ATargetArray);. И в идеале вызовите процедуру с помощью: AssignInteger(99, [X, Y, Z]);.

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

procedure AssignIntegers(AValue: Integer; const ATargets: array of PInteger);
var
  I: Integer;
begin
  for I := Low(ATargets) to High(ATargets) do
    ATargets[I]^ := AValue;
end;

Следующий тестовый пример DUnit демонстрирует, что он работает. Вы даже можете держать под рукой массив этих целочисленных указателей, чтобы переназначить их в любое время.

type
  TArrayPInteger = array of PInteger;
procedure TDelphiTests.TestAssignIntegers;
var
  X,Y,Z: Integer;
  LGroup: TArrayPInteger;
begin
  AssignIntegers(1, [@X, @Y, @Z]); { Pass open arrray using addresses of integers to initialise }
  CheckEquals(1, X);
  CheckEquals(1, Y);
  CheckEquals(1, Z);

  LGroup := TArrayPInteger.Create(@X, @Y); { Handy technique to initialise dynamic arrays }
  AssignIntegers(2, LGroup);
  CheckEquals(2, X);
  CheckEquals(2, Y);
  CheckEquals(1, Z); { Not part of group }
end;

ПРЕДУПРЕЖДЕНИЕ

Единственным реальным недостатком является то, что вы теряете проверку типов. Компилятор не помешает вам передать адрес нецелочисленных типов. Это может привести к повреждению данных других структур или нарушению прав доступа.

person Disillusioned    schedule 29.04.2014