Подтвердить что ты не робот

Метод Ruby 'tap' - внутреннее назначение

Недавно я обнаружил, что tap может использоваться для "сухого" назначения значений новым переменным; например, для создания и заполнения массива, например:

array = [].tap { |ary| ary << 5 if something }

Этот код нажимает 5 на array, если something правдиво; в противном случае array останется пустым.

Но я не понимаю, почему после выполнения этого кода:

array = [].tap { |ary| ary += [5] if something }

array остается пустым. Кто-нибудь может мне помочь?

4b9b3361

Ответ 1

В первом случае array и ary указывают на один и тот же объект. Затем вы мутируете этот объект с помощью метода <<. Объект, на который указывают как array, так и ary, теперь изменен.

Во втором случае array и ary снова оба указывают на один и тот же массив. Теперь вы переназначаете переменную ary, так что ary теперь указывает на новый массив. Однако переназначение ary не влияет на array. В рубине переопределение переменной никогда не влияет на другие переменные, даже если они указывали на один и тот же объект до переназначения.

Другими словами, array по-прежнему пуст по той же причине, что x не будет 42 в следующем примере:

x = 23
y = x
y = 42 # Changes y, but not x

Изменить: Чтобы добавить один массив к другому на месте, вы можете использовать метод concat, который также должен быть быстрее, чем с помощью +=.

Ответ 2

Я хочу немного развернуть это:

array = [].tap { |ary| ary << 5 if something }

Что это делает (если something - true-ish):

  • присваивает array [] пустой массив.

    array.object_id = 2152428060
    
  • передает [] в блок как ary. ary и array указывают на один и тот же объект массива.

    array.object_id = 2152428060
    ary.object_id = 2152428060
    
  • ary << 5 < является мутативным методом, то есть он будет модифицировать принимающий объект. Это похоже на идиому добавления ! к вызову метода, что означает "изменить это на месте!", Как в .map vs .map! (хотя удар не имеет собственного смысла в методе имя). ary имеет 5 вставленных, поэтому ary= array= [5]

    array.object_id = 2152428060
    ary.object_id = 2152428060
    

На этом заканчивается array, равное [5]

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

array = [].tap{ |ary| ary += [5] if something }    
  • тот же
  • тот же
  • ary += 5 += является коротким для ary = ary + 5, поэтому это первая модификация (+), а затем назначение (=) в этом порядке. Это дает возможность изменить объект на месте, но на самом деле этого не происходит. Он создает совершенно новый объект.

    array.object_id = 2152428060
    ary.object_id = 2152322420
    

Итак, мы заканчиваем с array как исходный объект, пустым массивом с object_id=2152428060 и ary, массивом с одним элементом, содержащим 5 с object_id = 2152322420. После этого ничего не происходит с ary. Это не связано с первоначальным назначением array, которое уже произошло. Tap выполняет блок после array.