Tôi tìm thấy mã này trong RailsCast :
def tag_names
@tag_names || tags.map(&:name).join(' ')
end
Gì (&:name)trong map(&:name)bình?
Tôi tìm thấy mã này trong RailsCast :
def tag_names
@tag_names || tags.map(&:name).join(' ')
end
Gì (&:name)trong map(&:name)bình?
Câu trả lời:
Nó là viết tắt của tags.map(&:name.to_proc).join(' ')
Nếu foolà một đối tượng với một to_procphương thức, thì bạn có thể truyền nó cho một phương thức &foo, nó sẽ gọi foo.to_procvà sử dụng nó làm khối của phương thức.
Các Symbol#to_procphương pháp ban đầu được bổ sung bởi ActiveSupport nhưng đã được tích hợp vào Ruby 1.8.7. Đây là thực hiện của nó:
class Symbol
def to_proc
Proc.new do |obj, *args|
obj.send self, *args
end
end
end
&, tức làtags.map(&:name.to_proc).join(' ')
Một tốc ký tuyệt vời khác, nhiều người chưa biết, là
array.each(&method(:foo))
đó là một tốc ký cho
array.each { |element| foo(element) }
Bằng cách gọi, method(:foo)chúng tôi đã lấy một Methodđối tượng từ selfđó đại diện cho foophương thức của nó và sử dụng &để biểu thị rằng nó có một to_proc phương thức chuyển đổi nó thành a Proc.
Điều này rất hữu ích khi bạn muốn làm điều point-free style. Một ví dụ là kiểm tra xem có chuỗi nào trong một mảng bằng chuỗi không "foo". Có một cách thông thường:
["bar", "baz", "foo"].any? { |str| str == "foo" }
Và có một cách miễn phí:
["bar", "baz", "foo"].any?(&"foo".method(:==))
Cách ưa thích nên là cách dễ đọc nhất.
array.each{|e| foo(e)}vẫn ngắn hơn :-) +1 dù sao
&method?
[1,2,3].map(&Array.method(:new))
Nó tương đương với
def tag_names
@tag_names || tags.map { |tag| tag.name }.join(' ')
end
Mặc dù chúng ta cũng lưu ý rằng ampersand #to_proccó thể hoạt động với bất kỳ lớp nào, không chỉ là Biểu tượng. Nhiều người Ruby chọn định nghĩa #to_proctrên lớp Array:
class Array
def to_proc
proc { |receiver| receiver.send *self }
end
end
# And then...
[ 'Hello', 'Goodbye' ].map &[ :+, ' world!' ]
#=> ["Hello world!", "Goodbye world!"]
Ampersand &hoạt động bằng cách gửi to_proctin nhắn trên toán hạng của nó, trong đoạn mã trên, thuộc lớp Array. Và vì tôi đã định nghĩa #to_procphương thức trên Array, nên dòng trở thành:
[ 'Hello', 'Goodbye' ].map { |receiver| receiver.send( :+, ' world!' ) }
Nó là viết tắt của tags.map { |tag| tag.name }.join(' ')
&gọi to_procvào toán hạng của nó. Vì vậy, nó không đặc trưng cho phương thức bản đồ và trên thực tế hoạt động trên bất kỳ phương thức nào lấy một khối và chuyển một hoặc nhiều đối số cho khối.
Câu trả lời của Josh Lee gần như đúng ngoại trừ mã Ruby tương đương phải có như sau.
class Symbol
def to_proc
Proc.new do |receiver|
receiver.send self
end
end
end
không phải
class Symbol
def to_proc
Proc.new do |obj, *args|
obj.send self, *args
end
end
end
Với mã này, khi print [[1,'a'],[2,'b'],[3,'c']].map(&:first)được thực thi, Ruby chia đầu vào đầu tiên [1,'a']thành 1 và 'a' để cho obj1 và args*'a' để gây ra lỗi vì đối tượng Fixnum 1 không có phương thức tự (đó là: đầu tiên).
Khi [[1,'a'],[2,'b'],[3,'c']].map(&:first)được thực thi;
:firstlà một đối tượng Biểu tượng, do đó, khi &:firstđược đưa cho một phương thức bản đồ làm tham số, Ký hiệu # to_proc được gọi.
map gửi tin nhắn cuộc gọi đến: first.to_proc với tham số [1,'a'], ví dụ, :first.to_proc.call([1,'a'])được thực thi.
Thủ tục to_proc trong lớp Symbol gửi một thông điệp gửi đến một đối tượng mảng ( [1,'a']) với tham số (: đầu tiên), ví dụ, [1,'a'].send(:first)được thực thi.
lặp đi lặp lại trên phần còn lại của các yếu tố trong [[1,'a'],[2,'b'],[3,'c']]đối tượng.
Điều này giống như [[1,'a'],[2,'b'],[3,'c']].map(|e| e.first)biểu thức thực thi .
[1,2,3,4,5,6].inject(&:+)- tiêm mong đợi một lambda với hai tham số (ghi nhớ và vật phẩm) và :+.to_proccung cấp nó - Proc.new |obj, *args| { obj.send(self, *args) }hoặc{ |m, o| m.+(o) }
Hai điều đang xảy ra ở đây, và điều quan trọng là phải hiểu cả hai.
Như được mô tả trong các câu trả lời khác, Symbol#to_procphương pháp đang được gọi.
Nhưng lý do to_procđang được gọi trên biểu tượng là vì nó được chuyển đến mapdưới dạng đối số khối. Đặt &trước một đối số trong một cuộc gọi phương thức làm cho nó được truyền theo cách này. Điều này đúng với bất kỳ phương thức Ruby nào, không chỉ mapvới các ký hiệu.
def some_method(*args, &block)
puts "args: #{args.inspect}"
puts "block: #{block.inspect}"
end
some_method(:whatever)
# args: [:whatever]
# block: nil
some_method(&:whatever)
# args: []
# block: #<Proc:0x007fd23d010da8>
some_method(&"whatever")
# TypeError: wrong argument type String (expected Proc)
# (String doesn't respond to #to_proc)
Nó Symbolđược chuyển đổi thành một Procvì nó được truyền vào dưới dạng một khối. Chúng tôi có thể chỉ ra điều này bằng cách cố gắng vượt qua một Proc .mapmà không cần ký hiệu:
arr = %w(apple banana)
reverse_upcase = proc { |i| i.reverse.upcase }
reverse_upcase.is_a?(Proc)
=> true
arr.map(reverse_upcase)
# ArgumentError: wrong number of arguments (1 for 0)
# (map expects 0 positional arguments and one block argument)
arr.map(&reverse_upcase)
=> ["ELPPA", "ANANAB"]
Mặc dù không cần phải chuyển đổi, phương thức này sẽ không biết cách sử dụng vì nó mong muốn một đối số khối. Vượt qua nó với &cho.map khối nó mong đợi.
Về cơ bản, nó thực hiện lệnh gọi phương thức tag.nametrên mỗi thẻ trong mảng.
Nó là một tốc ký đơn giản của ruby.
Mặc dù chúng tôi đã có câu trả lời tuyệt vời, nhưng nhìn qua góc nhìn của người mới bắt đầu tôi muốn thêm thông tin bổ sung:
Bản đồ (&: name) có nghĩa là gì trong Ruby?
Điều này có nghĩa là, bạn đang truyền một phương thức khác làm tham số cho hàm ánh xạ. (Trong thực tế, bạn đang chuyển một biểu tượng được chuyển đổi thành một Proc. Nhưng điều này không quan trọng trong trường hợp cụ thể này).
Điều quan trọng là bạn có một methodtên nameđược sử dụng bởi phương thức bản đồ làm đối số thay vì blockkiểu truyền thống .
Đầu tiên, &:namelà một lối tắt cho &:name.to_proc, trong đó :name.to_proctrả về một Proc(một cái gì đó tương tự, nhưng không giống với lambda) mà khi được gọi với một đối tượng là đối số (đầu tiên), gọi namephương thức trên đối tượng đó.
Thứ hai, trong khi &trong def foo(&block) ... endngười cải đạo một khối truyền cho foođến một Proc, nó là đối diện khi áp dụng cho một Proc.
Do đó, &:name.to_proclà một khối lấy một đối tượng làm đối số và gọi namephương thức trên nó, tức là { |o| o.name }.
Nó giống như dưới đây:
def tag_names
if @tag_names
@tag_names
else
tags.map{ |t| t.name }.join(' ')
end