Czy << to tylko lukier składniowy?

Dziś spotkałem przykład:

puts << 'Hello' << ' World'

Instrukcja z wyglądu przypominała mi std::cout i operator<< ze standardowego libu C++.

Ale jak się okazuje jest tu efekt uboczny, dochodzi do konkatenacji, co w sumie mnie zaskoczyło.

Chciałbym wiedzieć czy << to lukier składniowy dla operacji +=, a jeśli nie to jakie występują różnice?
Co sądzicie o << ?
Jak dla mnie z pozoru ładnie to wygląda przy instrukcji puts, ale to było cholernie mylące, jestem ciekaw waszej opinii.

[quote=programuje_na_sedesie]Dziś spotkałem przykład:

puts << 'Hello' << ' World'

Instrukcja z wyglądu przypominała mi std::cout i operator<< ze standardowego libu C++.[/quote]
To chyba nie w tym języku. << to metoda, powyższe jest bezsensu bo nil nie posiada takiej metody. Nie chcem napisać jakieś głupoty, ale w ruby wszystkie operatory to wywołania metod na obiektach. Obiekt musi odpowiadać na daną metode inaczej będzie Exception undefined method.

Powyższe to to samo co

puts nil.send(:<<, 'Hello'.send(:<<, 'World'))

Jeśli sobie zajrzysz w nil to masz:

nil.methods => [:to_i, :to_f, :to_s, :to_a, :inspect, :&, :|, :^, :nil?, :to_r, :rationalize, :to_c, :===, :=~, :!~, :eql?, :hash, :<=>, :class, :singleton_class, :clone, :dup, :initialize_dup, :initialize_clone, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :freeze, :frozen?, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_to?, :respond_to_missing?, :extend, :display, :method, :public_method, :define_singleton_method, :__id__, :object_id, :to_enum, :enum_for, :==, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__]

<< to metoda, podobnie jak +=. W przypadku Stringów, powoduje ona połączenie obu stringów.

http://ruby-doc.org/core/classes/String.html#M001173

Przykład który podałeś nie zadziała, spotkałeś jakiś kijowy kod:

[code ruby]ruby-1.9.2-p180-patched :004 > puts << “elo” << “ziom”

NoMethodError: undefined method <<' for nil:NilClass from (irb):4 from /home/hubert/.rvm/rubies/ruby-1.9.2-p180-patched/bin/irb:16:in
ruby-1.9.2-p180-patched :005 > puts “elo” << “ziom”
eloziom
=> nil[/code]

http://www.ruby-lang.org/en/documentation/ruby-from-other-languages/

Prawdopodobnie widziałeś taki kod:

$stdout << "elo" << "ziom"

i to działa, ponieważ $stdout jest obiektem klasy IO, który ma metodę <<, z grubsza działającą tak samo jak puts.

Fakt podałem zły kod, macie rację:

puts 'Hello' << ' World'

Przyzwyczajenie z C++ wzięło tym razem górę.

Mnie zastanawia tylko po co robisz temat na forum z pytaniem o operator << zamiast poszperac w google i wybrac jedna z tysiaca stron opisujaca operatory w ruby. Przeciez tak jest o wiele szybciej O_o

http://www.tutorialspoint.com/ruby/ruby_operators.htm

Nie myśl, że jestem leniem. Sprawdzałem tego typu strony, ale operator << na stronach określany jest jako operator przesunięcia bitowego w lewo, a tu pojawia się konkatencja. Dlatego pytam, czy << to tylko maskowanie +=, czy coś więcej.

hmm

http://ruby-doc.org/core/classes/String.html#M001173

Dzięki własnie tego szukałem.

Nie jestem w 100% pewien, ale tworząc własną metodę o nazwie << okazuje się, że muszę
Co ciekawe metoda o nazwie << musi przyjmować argument, inaczej w trakcie wywołania zgłasza błąd.

Nie musi:

[code]class Foo
def <<
“foo”
end
end

Foo.new.<< # “foo”[/code]

No tu masz rację, ale mnie chodziło o wygląda taki jaki występuje przy stringach:

[code]class Foo
def <<
“foo”
end
end

ff = Foo.new
puts ff <<[/code]

To zależy od obiektu (zwykle – od jego klasy). Dla liczby całkowitej to przesunięcie bitowe, dla łańcucha znaków to konkatenacja, dla tablicy i zbioru – dopisanie elementu itp.

Zwykle jest tak, że += (które samo w sobie jest lukrem składniowym: a += b jest rozwijane do nie-atomowego a = a + b, co ma znaczenie chociażby przy wielowątkowości) jest kosztowniejsze, bo tworzy dodatkowy obiekt, podczas gdy << modyfikuje receivera¹ „w miejscu” – czyli

a = 'a' * 1_000_000_000 a += 'b' * 1_000_000_000
będzie kosztowniejsze niż

a = 'a' * 1_000_000_000 a << 'b' * 1_000_000_000
bo w pierwszym przypadku będziesz mieć w pamięci 1 GB łańcuch ‘aaa…aaa’, 1 GB łańcuch ‘bbb…bbb’ i 2 GB łańcuch ‘aaa…aaabbb…bbb’ (Garbaty dwa pierwsze względnie szybko znajdzie i zakosi, ale to też kosztuje zasoby), a w drugim 1 GB łańcuch ‘aaa…aaa’ zostanie rozszerzony o 1 GB łańcuch ‘bbb…bbb’ do 2 GB łańcucha ‘aaa…aaabbb…bbb’.

¹ jest jakiś dobry staropolski odpowiednik „receivera”? „odbiorca”? ble…

[quote=programuje_na_sedesie]No tu masz rację, ale mnie chodziło o wygląda taki jaki występuje przy stringach:

[code]class Foo
def <<
“foo”
end
end

ff = Foo.new
puts ff <<[/code]
[/quote]
To jest specyfika akurat <<, przy którym parser dostaje lekkiego kociokwiku, bo może to być też znak początku „surowego” (zwykle wielolinijkowego) łańcucha, tzw. heredoca.

Zauważ, że

[code]class Foo
def <<
“foo”
end
end

ff = Foo.new
puts ff.<<[/code]
zadziała poprawnie.

Czy mógłbyś to potwierdzić jakimś oficjalnym dokumentem?

  1. Co to znaczy „oficjalnym dokumentem”? Matz ma to wytatuować na skórze _why’a i przysłać, dołączając ksero paszportu i umowy o pracę w Heroku?

  2. Co miałbym potwierdzić? To, że += jest lukrem składniowym, to, że jest zwykle kosztowniejsze, czy to, że << zwykle modyfikuje receivera?

Mam na myśli źródło informacji po którym dowiedziałeś się, że += jest kosztowniejsze. Skąd to wiesz. Książka? Podaj autora i rozdział. Strona? Podaj link. Ta informacja jest bardzo ciekawa, chciałbym mieć pewność, że jest prawdziwa.

[code=ruby]require ‘benchmark’
n = 100000
s1 = ‘string1’
s2 = ‘string2’

Benchmark.bm do |x|
x.report(’+=’) do
n.times do
s1 += ‘b’
end
end
x.report(’<<’) do
n.times do
s2 << ‘b’
end
end
end[/code]
Nie wiem czy super poprawnie napisalem ten benchmark bo nie robie tego za czesto no ale jakis wynik mamy

[quote]user system total real
+= 1.630000 0.000000 1.630000 ( 1.627092)
<< 0.020000 0.000000 0.020000 ( 0.022852)[/quote]

user, system - user oznacza ile czasu proces wykonywał się w przestrzeni użytkownika, a system ile w przestrzeni systemu? Wiem, że moje słowo ‘przestrzeń’ może być nie na miejscu więc proszę o poprawę.

Co do przykładu. To sam kod okazał się bardzo ciekawy i najlepiej uzmysławia różnicę. Byłem pod wrażeniem.

Zastanawia mnie jeszcze jedna rzecz. Skoro operacja << jest taka super, to czemu korzysta się z += czy to czemuś służy? Chyba tylko liczbom, bo << powodowałby przesuniecie bitowe, nie?

[quote=programuje_na_sedesie]user, system - user oznacza ile czasu proces wykonywał się w przestrzeni użytkownika, a system ile w przestrzeni systemu? Wiem, że moje słowo ‘przestrzeń’ może być nie na miejscu więc proszę o poprawę.

Co do przykładu. To sam kod okazał się bardzo ciekawy i najlepiej uzmysławia różnicę. Byłem pod wrażeniem.

Zastanawia mnie jeszcze jedna rzecz. Skoro operacja << jest taka super, to czemu korzysta się z += czy to czemuś służy? Chyba tylko liczbom, bo << powodowałby przesuniecie bitowe, nie?[/quote]
W ruby jest tak ze poszczegolne klasy roznie implementuja operatory i wszystko zalezy od ciebie jak uwazasz gdzie uzywasz i po co uzywasz.
Masz tak duza dowolnosc ze ciezko powiedziec co jest tym jedynym najlepszym rozwiazaniem danego problemu