Помощь новичкам

Тут я согласен. Но в Delphi тоже COW для строк работает.

Надо будет аккуратнее описать это в справке. Т.е. сказать, что в ряде случаев строка ведет себя как размерная, а в ряде - как ссылочная. И поддерживает модель COW.

У нас всё волшебное ))

1 лайк

Здесь надо договориться о терминологии, что называть размерным, а что - ссылочным типом. COW кстати появилась в какой-то момент времени для строк в Delphi, сделав их более эффективныыми в ряде случаев.

С некоторой точки зрения COW можно тоже считать особенностью реализации и на верхнем уровне не рассматривать.

Кто-то например считает, что если в C# - эта штуковина ссылочная, то и у нас - ссылочная. Но он не прав. Это порочный и тупиковый путь, ведущий в никуда.

3 лайка

А объясните простыми словами почему так получается, пожалуйста

begin
  var s := '123';
  var s1 := s;
  var s2 := '123';
  Swap(s1[1], s1[2]);
  Println(s, s1, s2);//213 213 213
end.

Вот пример проще:

procedure p1(var ch: char) :=
ch := '_';

begin
  var s := '123';
  var s1 := s;
  var s2 := '123';
  p1(s1[2]);
  Println(s, s1, s2);//1_3 1_3 1_3
end.

Строку не копирует, когда передаёт её символ в виде var-параметра. Я не совсем понимаю почему, но наверное ради какой то оптимизации.

@MrFresnel, @Gleb проверьте пожалуйста эту же программу в C#, будет ли там такое же творится.

В C# строки неизменяемые слава богу. Там такого нет.

Так значит это ошибка паскаля? Или всм C# вообще запрещает s[2] := '_' и передачу в виде var-параметров?

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

Ну, тогда надо лишь добавить копирование строки перед передачей её символа в виде var-параметра, разве нет? Так же как во все остальных случаях, когда содержимое строки изменяется.

Я уже выше предложил вариант. Справка пишется для пользователя, причем, для пользователя любого уровня. Размерный тип или ссылочный, - об этом пользователю придется в любом варианте подумать, когда он дойдет до передачи и возврата параметров в подпрограммы, потому что зазубрить все возможные варианты для каждого типа очень сложно и даже глуповато как-то. Иными словами, я предложил посмотреть на типы с точки зрения конечного пользователя. Которому (по крайней мере, вначале) все равно, как там оно “внутри устроено”. Т.е. посмотреть, ведет себя тип как размерный или ссылочный. Соответственно на этом основании решить, куда его отнести. А как иначе? Может быть, в вузе можно по-другому, но если мы надеемся, что язык придет и в школы/колледжи, там иной уровень и учителей, и учеников. Им как-то попроще надо и однозначнее.

А как насчет VB.NET?

Sub Main()
   Dim s As String = "My own string"
   Mid(s, 4, 3) = "NET"
   Console.WriteLine(s)
   Dim m As String = Mid(s1, 8, 6)
   Console.WriteLine(s1)
End Sub

Изменяемость строк и COW – это еще можно понять и простить. :slight_smile: Меня вот больше всего напрягает следующая сомнительная оптимизация: при инициализации строковой переменной (не константы!) создается ссылка на уже существующую строку в куче, если такая уже есть (вместо создания новой).

Это легко может привести к трудно диагностируемым ошибкам, как в примерах выше с обработкой строки в подпрограмме:

begin
  var s1 := '111';
  var s2 := '111';
  s1.Equals(s2).Print //True -- зачем так делать???
end.

Это особенность платформы CLI или компилятора Паскаля? @ibond @Admin Можно ли это как-то запретить и оставить только для инициализации именно констант?

А в чем проблема?

begin
  var s1 := '111';
  var s2 := '111';
  s1.Equals(s2).Println; //True
  s2[1] := '2';
  Println(s1, s2, s1.Equals(s2)) // 111 211 False
end.

В том-то и проблема – смешанное поведение! Гермафродит :slight_smile:

Это называется “протечка абстракций”. Это проблема неустранимая (без нарушения совместимости).

Проблемный пример кода приведен выше (с передачей и обработкой одной из строк-близнецов в подпрограмме). Это крайне неочевидная ситуация для программиста – литеральные строки могут случайно совпасть по значению и незаметно стать сиамскими близнецами с побочным эффектом.

Если тут имеет место моделирование поведения в каком-то другом языке - конечно неустранимая.

А вы попробуйте такое написать - и посмотрим.

Причем тут другие языки? Неустранимая – на уровне строгой классификации типа. Гибрид – он и в Африке гибрид.

Чего тут пробовать и смотреть? Наличие скрытого побочного эффекта у стандартного и широкораспространенного типа в языке (к тому же недокументированного эффекта) – это очень плохо. Потенциальный data loss bug на ровном месте.

Пока это только разговор. Теоретизация. Мы уже видели. что есть тут вещи, которые в обычные теории не укладываются. Поэтому нужен эксперимент. Т.е. код.

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

Так я и сказал, что неустранимая без нарушения совместимости. Вы только раньше переформулировали это по-своему, ссылаясь на моделирование другого языка.

В чем ваш контраргумент? В том, что это гипотетически редкая ситуация, с которой еще надо умудриться столкнуться? Да, к счастью, редкая. У меня пока не было. Но я считаю, раз потенциальная побочка есть, её как минимум следует описать в Справке.

А еще лучше – устранить. Пока вроде бы единственный проблемный случай – использование переменной-строки с индексным свойством в качестве фактического параметра при передаче её значения в подпрограмму по ссылке (var). В этом случае компилятор мог бы делать копию строки и такой побочки бы не было. Правда, это замедлит работу со Swap(<char>,<char>) и ему подобным.

Второй вариант решения – не создавать изначально строковых переменных - “сиамских близнецов” (можно оставить это только для констант) – если это вообще реально сделать на уровне компилятора.

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

Вопрос остается открытым, как это объяснять достаточно просто, но строго, и без путаницы и противоречий. Тем более новичкам. Вводить дополнительное понятие гибридного типа?

1 лайк

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

namespace Test
{	
	class Program
	{	
		public static void Method(ref char x)
		{
			x = '_';
		}
		
		public static void Main(string[] args)
		{
			string s1 = "12";
			Method(ref s1[0]);
		}
	}
}

Выдаст при попытке компиляции:

Свойство, индексатор или динамический член не может передаваться как параметр с ключевым словом out или ref (CS0206) - C:\Users\Admin\Documents\SharpDevelop Projects\Test1\Test1\Program.cs:23,15

Сколько ни произволся PascalABC.Net, узнал про эту особенность строк только сегодня… Не перестаю удивляться.

То, что фактически ссылочный тип, то и называть ссылочным, остальное - размерным. Проверить можно рефлексией (примеры выше). На счёт строк действительно непонятно к чему их относить. Если ориентироваться на терминологию C#, то там нет понятия гибридного типа. Но, это, все - же, не означает, что в любом другом языке его нет. Стоит поискать подобные PascalABC.Net примеры.