I looked into the ISO Ruby Language Specification, the RubySpec, and the RDoc for interpolated String literals, and neither of the three define a specific evaluation order for interpolations. They probably should explicitly say that evaluation-order is undefined, but still, the fact that it isn't explicitly defined, means that it is implicitly undefined.
So, in other words: if your interpolations have side-effects, the order of those side-effects both with respect to each other and the time when they are inserted into the string is unspecified, and what you are observing is a perfectly legal implementation of the spec.
From what I can tell, (at least) any of the following results is legal:
'1 != 11 != 111 != 1'
'1 != 11 != 111 != 11'
'1 != 11 != 111 != 111'
'1 != 111 != 11 != 1'
'1 != 111 != 11 != 11'
'1 != 111 != 11 != 111'
'1 != 111 != 111 != 1'
'1 != 111 != 111 != 11'
'1 != 111 != 111 != 111'
'11 != 11 != 111 != 1'
'11 != 11 != 111 != 11'
'11 != 11 != 111 != 111'
'11 != 111 != 11 != 1'
'11 != 111 != 11 != 11'
'11 != 111 != 11 != 111'
'11 != 111 != 111 != 1'
'11 != 111 != 111 != 11'
'11 != 111 != 111 != 111'
'111 != 11 != 111 != 1'
'111 != 11 != 111 != 11'
'111 != 11 != 111 != 111'
'111 != 111 != 11 != 1'
'111 != 111 != 11 != 11'
'111 != 111 != 11 != 111'
'111 != 111 != 111 != 1'
'111 != 111 != 111 != 11'
'111 != 111 != 111 != 111'
printf("%d %d %d",i,++i,++i);thanks"%s != %s != %s != %s" % [o, m(o), m(o), o]. First the RHO array is prepared (both calls tom(o)are done,) then the string is being interpolated.def m(o); o << '1'; end; o = '1'; [o, m(o), m(o), o].map(&:__id__) #⇒ [46935826870120, 46935826870120, 46935826870120, 46935826870120]. That is the same object.