Dlaczego:
[1,2,3,4,5].map(&:to_s) #=> ["1", "2", "3", "4", "5"]
pracować, ale:
[1,2,3,4,5].map(&:*(2))
zgłasza nieoczekiwany błąd składni?
Odpowiedzi:
3 dla odpowiedzi № 1&
nazywa się to_proc
operator. To wywołuje to_proc
metoda na wyrażeniu następującym po nim, a następnie przekazuje wynikowy proces do metody jako blok.
W przypadku &:to_s
, :to_s
jest symbolem, więc operator dzwoni Symbol#to_proc
. Dokumenty są trochę zniekształcone, ale wystarczy powiedzieć, że te dwa wyrażenia są mniej więcej równoważne:
my_proc = :to_s.to_proc
my_proc = Proc.new {|obj| obj.to_s }
Tak więc odpowiedź na pytanie "Dlaczego nie" &:*(2)
praca? "to wyrażenie następujące po &
operator, :*(2)
, nie jest prawidłowym wyrażeniem Ruby, a dla parsera Ruby jest tak samo sensowne jak "hello"(2)
.
Jest przy okazji sposób, aby zrobić to, co próbujesz zrobić:
[1,2,3,4,5].map(&2.method(:*))
# => [2, 4, 6, 8, 10]
W powyższym kodzie 2.method(:*)
zwraca odniesienie do *
metoda obiektu 2
jak metoda obiekt. Obiekty metod zachowują się podobnie do obiektów Proc i odpowiadają na nie to_proc
. Jednak powyższe nie jest dokładnie równoważne - tak jest 2 * n
zamiast n * 2
(rozróżnienie, które nie ma znaczenia, jeśli n
jest również numerycznym) i nie jest bardziej zwięzły ani czytelny niż {|n| n * 2 }
i tak rzadko warte są kłopotów.
-1 dla odpowiedzi № 2
Ampersand and object (&: method)
Operator & można również użyć do przekazania obiektu jako bloku do metody, jak w poniższym przykładzie:
arr = [ 1, 2, 3, 4, 5 ]
arr.map { |n| n.to_s }
arr.map &:to_s
Oba powyższe przykłady mają taki sam wynik. W obu metoda map przyjmuje tablicę arr i blok, a następnie uruchamia blok na każdym elemencie tablicy. Kod wewnątrz bloku uruchamia to_s na każdym elemencie, przekształcając go z liczb całkowitych na ciągi. Następnie metoda map zwraca nową tablicę zawierającą przekonwertowane elementy.
Pierwszy przykład jest powszechny i szeroko stosowany. Drugi przykład może na pierwszy rzut oka wyglądać nieco tajemniczo. Zobaczmy, co się dzieje:
W Ruby pozycje poprzedzone dwukropkiem (:) są symbolami. Jeśli nie znasz klasy / typu danych Symbol, proponuję Ci go przeczytać i przeczytać kilka artykułów, zanim przejdziesz dalej. Wszystkie nazwy metod w Ruby są przechowywane wewnętrznie jako symbole. Poprzez prefiksowanie nazwy metody dwukropkiem, nie przekształcamy metody w symbol, ani nie wywołujemy metody, tylko przekazujemy nazwę metody (odwołując się do metody). W powyższym przykładzie przechodzimy: to_s, który jest odniesieniem do metody to_s, do operatora ampersand (&), który utworzy proc (przez wywołanie to_proc pod maską). Proc bierze wartość jako argument, wywołuje to_s na nim i zwraca wartość przekonwertowaną na ciąg znaków.
Chociaż :Symbol to_s jest zawsze taki sam, podczas uruchamiania pętli map będzie odwoływał się do metody to_s klasy odpowiadającej każdemu elementowi tablicy. Jeśli przekażemy tablicę taką jak [21, 4.453,: foobar,] do metody map, to metoda to_s klasy Fixnum zostanie zastosowana (wywołana) na pierwszym elemencie, metoda to_s klasy Float zostanie zastosowana do drugi element i metoda to_s klasy Symbol zostaną zastosowane do trzeciego elementu. Ma to sens, ponieważ nie przekazujemy rzeczywistej metody to_s operatorowi ampersand, tylko jej nazwie.
Poniżej znajduje się przykład tworzenia procesu, który pobiera argument, wywołuje na nim metodę i zwraca wynik tej metody.
p = :upcase.to_proc
p.call("foo bar")
Output:
=> "FOO BAR"
Zobaczmy, co się dzieje w arr.map &: to_s
- Przy każdej iteracji mapy jeden element tablicy (liczba całkowita) jest przekazywany do &: to_s
- The:symbol to_s (który jest odniesieniem do metody to_s) jest przekazywany do operatora &, który tworzy proc, który pobiera argument (element tablicy), wywołuje to_s na argumencie i zwraca wartość przekonwertowaną na łańcuch;
- Metoda map zwraca nową tablicę zawierającą ciągi "1", "2", "3", "4" i "5".