#ruby
На примере 2-х хешей hash={:fruit=>"Apple", :vegetable="Cucumber"} и hash={"fruit"=>"Apple", "vegetable"="Cucumber"}. Расскажите в чем разница между этими сущностями и где применяют symbol.
Ответы
Ответ 1
Переменная - это именованная область памяти, она содержит имя и значение, значение можно менять. Строка - это просто область памяти, которую вы можете изменять. Символ - это просто имя. Эта конструкция очень популярна в функциональных языках и обычно называется атомом - нечто неделимое и неизменное. Например, белый цвет, :white - нет надобности для значения, нет надобности для изменения - белый цвет останется белым цветом, чтобы не происходило. Это символ в Ruby. Используется символы там, где значение не требуется, например, для перечислений, передачи имени ключа или метода. Строки и символы можно преобразовывать друг в друга, однако это разные классы.Ответ 2
Думаю, что стоит осветить вопрос с технической точки зрения (отвечу на вопрос как?, но не на вопрос зачем?). Прежде всего общий факт: любое значение в Ruby — объект. И объект уникально идентифицируется ссылкой на него. Можно получить производное от ссылки значение из объект.object_id. (где не указано явно, примеры на Ruby 2.2.3) Со строкой всё более-менее понятно: это просто ссылка на буфер конечной (но меняемой при необходимости) длины, в котором хранятся символы. Содержимое этого буфера можно менять по своему усмотрению (т. е. строки мутабельны), а потому сохраняя ссылку на строку в одном месте, нельзя быть уверенным, что при очередном взгляде по ссылке там будет то же, что было ранее. Одинаковые строки обычно* являются разными объектами: ''.object_id == ''.object_id # => false Символы это небольшая обёртка над строками, объекты которой принципиально иначе хранятся в памяти. Способ хранения такой, что символ с определённым названием (строкой), будучи употреблённым в пределах одного процесса Ruby в любом месте будет одним и тем же объектом. Это позволяет очень быстро сравнивать символы на равенство: достаточно сравнить ссылки на них, даже не смотря в его строку, т. е. выяснить, не один и тот же ли это объект. Это O(1), тогда как сравнение строк на равенство O(object.length). Этот способ хранения называется интернирование, все строки-названия символов в Ruby интернированы. В Ruby String#intern и String#to_sym синонимы. .to_sym, на мой взгляд, предпочтительнее, т. к. отражает настоящий тип возвращаемого значения. Принцип действия неплохо описан в документации к String intern() в Java: класс где-то внутри себя поддерживает глобальный набор всех интернированных значений. Когда запрашивается получение интернированного значения, берётся либо равное ему значение из набора, либо запрашиваемое значение копируется (!) в набор и возвращается оно же. Когда-то все символы, появившись однажды, никогда не исчезали, сборщик мусора их не трогал даже если ими переставали пользоваться. Поэтому делать символы из строк, прилетающих от пользователя, было опасно: присылая различные строки тоннами, можно было занять символами из них кучу оперативной памяти, вызвав её нехватку и падение процесса. Ба-бах. Это исправили в Ruby 2.2. тем временем на горизонте... * ...но это со временем может измениться. В Ruby 2.3 появилась опция "заморозки" всех литералов строк в коде. В Ruby 3.0 она и вовсе будет включена по умолчанию. Заморозка объекта делает невозможной его изменение (мутацию, т. е. объект становится иммутабельным). Объект можно заморозить методом freeze. И применительно к строкам интерпретатор может применить интересную оптимизацию: одинаковые литералы представлять одним и тем же объектом. ''.object_id == ''.object_id # => true # хоть вроде сравнение такое же! Это логично: единственная опасность мутабельного объекта в том, что его содержимое может быть внезапно изменено где-то очень далеко. По-хорошему, где это критично, объект нужно заморозить. Это можно сделать явно и в версиях постарше или при выключенной опции автозаморозки, и эффект воспроизведётся: ''.freeze.object_id == ''.freeze.object_id # => true Но заморозка и интернирование разные вещи, в отличие от символов не всегда одинаковые замороженные строки это один и тот же объект: a = '' a.freeze # он деструктивный, замораживает тот объект, на котором вызван b = ''.freeze a.object_id == b.object_id # => falseОтвет 3
Как верно сказал @cheops, Строка -- это область памяти, которую можно править и пр. Символы же можно воспринимать как константы, значения которых явным образом не указываются, а служат для избежания магических чисел. Наподобие enum.
Комментариев нет:
Отправить комментарий