Phương pháp bản đồ của Wikipedia làm gì trong Ruby?


250

Tôi mới tham gia lập trình. Ai đó có thể giải thích những gì .mapsẽ làm trong:

params = (0...param_count).map

9
Hỏi một câu hỏi tại một thời điểm. maplà một phương thức "chức năng" phổ biến được tìm thấy trên Vô số đối tượng được sử dụng để chuyển đổi các giá trị theo trình tự (có cân nhắc đặc biệt). .....là cách tạo ra phạm vi. Ngoài ra, hãy làm quen với REPL, nơi bạn có thể tự mình thử thứ này! :)

5
REPL cho ruby ​​là irb, đối với Rails thì đó là rails c. REPL cho phép bạn kiểm tra mã trực tiếp với chính ngôn ngữ.
Gary

Câu trả lời:


431

Các mapphương pháp có một đối tượng đếm được và một khối, và chạy các khối cho mỗi phần tử, xuất ra mỗi giá trị trả về từ khối (đối tượng ban đầu là không thay đổi trừ khi bạn sử dụng map!):

[1, 2, 3].map { |n| n * n } #=> [1, 4, 9]

ArrayRangelà vô số loại. mapvới một khối trả về một mảng. map!làm biến đổi mảng ban đầu.

Điều này hữu ích ở đâu, và sự khác biệt giữa map!và là eachgì? Đây là một ví dụ:

names = ['danil', 'edmund']

# here we map one array to another, convert each element by some rule
names.map! {|name| name.capitalize } # now names contains ['Danil', 'Edmund']

names.each { |name| puts name + ' is a programmer' } # here we just do something with each element

Đầu ra:

Danil is a programmer
Edmund is a programmer

3
cảm ơn speransky cho ví dụ. Vậy thì .map khác với .each như thế nào?
bigpotato

2
Ahhh tôi hiểu rồi Vì vậy, .map thực sự làm thay đổi mảng trong khi .each chỉ lặp qua mảng để truy cập các giá trị trong khi không để lại mảng ban đầu?
bigpotato

24
Điều nguy hiểm cho những người đọc bình thường là câu mở đầu mô tả mapnhư thể nó làmap!
kaleidic

12
để xem sự khác biệt giữa bản đồ và mỗi bản đồ, hãy mở một cửa sổ IRB và xem kết quả cho y và z trong đoạn mã sau: y = [1,2,3] .each {| x | x + 1}; z = [1,2,3] .map {| x | x + 1}
davej

7
@Inquisitive: 'mỗi' trả về mảng gọi nó (trong ví dụ, [1,2,3]) khi một khối được cung cấp, 'map' trả về một mảng mới được điền với các giá trị được tính bởi khối. Điều này có thể giúp: đặt biến ary = [1,2,3] và kiểm tra xem object_id. Sau đó chạy y = ary.each {| x | x + 1}; z = ary.map {| x | x + 1}. Bây giờ hãy kiểm tra object_id's của y và z. y có cùng object_id với ary (vì mỗi ary được trả về), nhưng z có object_id khác nhau, vì map trả về một mảng mới.
davej

66

map, cùng với selecteachlà một trong những con ngựa của Ruby trong mã của tôi.

Nó cho phép bạn chạy một hoạt động trên từng đối tượng của mảng và trả lại tất cả chúng ở cùng một nơi. Một ví dụ sẽ là tăng một mảng các số bằng một:

[1,2,3].map {|x| x + 1 }
#=> [2,3,4]

Nếu bạn có thể chạy một phương thức duy nhất trên các phần tử của mảng, bạn có thể thực hiện theo phương thức tốc ký như vậy:

  1. Để làm điều này với ví dụ trên, bạn phải làm một cái gì đó như thế này

    class Numeric
      def plusone
        self + 1
      end
    end
    [1,2,3].map(&:plusone)
    #=> [2,3,4]
  2. Để đơn giản hơn là sử dụng kỹ thuật phím tắt và dấu, hãy sử dụng một ví dụ khác:

    ["vanessa", "david", "thomas"].map(&:upcase)
    #=> ["VANESSA", "DAVID", "THOMAS"]

Chuyển đổi dữ liệu trong Ruby thường liên quan đến một loạt các maphoạt động. Nghiên cứu map& select, chúng là một số phương thức Ruby hữu ích nhất trong thư viện chính. Chúng cũng quan trọng như each.

( mapcũng là một bí danh cho collect. Sử dụng bất cứ điều gì tốt nhất cho bạn về mặt khái niệm.)

Thêm thông tin hữu ích:

Nếu Đối tượng có thể đếm được bạn đang chạy eachhoặc mapbật có chứa một tập hợp các phần tử có thể đếm được (băm, mảng), bạn có thể khai báo từng phần tử đó trong các ống khối của mình như sau:

[["audi", "black", 2008], ["bmw", "red", 2014]].each do |make, color, year|
  puts "make: #{make}, color: #{color}, year: #{year}"
end
# Output:
# make: audi, color: black, year: 2008
# make: bmw, color: red, year: 2014

Trong trường hợp của Hash (cũng là một Enumerableđối tượng, Hash chỉ đơn giản là một mảng các bộ dữ liệu với các hướng dẫn đặc biệt cho trình thông dịch). "Thông số đường ống" đầu tiên là khóa, thứ hai là giá trị.

{:make => "audi", :color => "black", :year => 2008}.each do |k,v|
    puts "#{k} is #{v}"
end
#make is audi
#color is black
#year is 2008

Để trả lời câu hỏi thực tế:

Giả sử đó paramslà một hàm băm, đây sẽ là cách tốt nhất để ánh xạ qua nó: Sử dụng hai tham số khối thay vì một để nắm bắt cặp khóa & giá trị cho mỗi bộ dữ liệu được giải thích trong hàm băm.

params = {"one" => 1, "two" => 2, "three" => 3}
params.each do |k,v|
  puts "#{k}=#{v}"
end
# one=1
# two=2
# three=3

Điều này không làm việc cho tôi trong irb. Tôi nhận được NoMethodError: private method 'plusone' called for 1:Fixnumtrong ruby ​​2 và 'số lượng đối số sai' trong ruby ​​1.9 / 1.8. Dù sao, tôi đã sử dụng lambda: plusone = ->(x) { x + 1 }sau đó lấy ra công cụ xác định biểu tượng : [1,2,3].map(&plusone).
tjmcewan

1
hmm có vẻ như bạn đã khai báo privatebên trong lớp nơi bạn đặt phương thức của mình trước khi bạn đặt phương thức của mình
boulder_ruby

Vâng, nó hoàn toàn làm được. Ngoại trừ nó đã không. :( Trước hết, đó là trong một tập lệnh thẳng với các lớp, thứ hai là đơn giản. Đây là bản sao / dán mã của bạn: gist.github.com/tjmcewan/a7e4feb2976a93a5eef9
tjmcewan

Vâng, tôi hoàn toàn chỉ đưa một ví dụ xấu trong mã của tôi, tôi xin lỗi. Hãy thử mã sửa đổi. Nó hoạt động ngay bây giờ ...
boulder_ruby

1
@boulder_ruby có cách nào để làm điều này với một phương thức bình thường - như trong, không phải là một phương thức lớp không?
tekknolagi

6

Sử dụng ruby ​​2.4 bạn có thể làm điều tương tự bằng cách sử dụng transform_values, tính năng này được trích xuất từ ​​đường ray thành ruby.

h = {a: 1, b: 2, c: 3}

h.transform_values { |v| v * 10 }
 #=> {a: 10, b: 20, c: 30}

4

0..param_countcó nghĩa là "lên đến và bao gồm param_count". 0...param_countcó nghĩa là "lên đến, nhưng không bao gồm param_count".

Range#mapkhông trả về một Enumerable, nó thực sự ánh xạ nó đến một mảng. Nó giống như Range#to_a.


3

Nó "ánh xạ" một chức năng cho từng mục trong một Enumerable- trong trường hợp này là một phạm vi. Vì vậy, nó sẽ gọi khối được truyền một lần cho mỗi số nguyên từ 0 đến param_count(độc quyền - bạn nói đúng về các dấu chấm) và trả về một mảng chứa mỗi giá trị trả về.

Đây là tài liệu cho Enumerable#map. Nó cũng có một bí danh collect,.


Thật kỳ lạ, nhưng Range#mapthực sự chuyển đổi nó thành một mảng.
Pedro Nascimento

1
@PedroNascimento: Vâng ... đó là những gì tôi đã nói?
Ry-

Xin lỗi, tôi không biết rằng bản đồ được gọi bởi chính nó đã không trả lại Enumerable, giống như mỗi bản đồ. Tôi nghĩ rằng nó đã làm.
Pedro Nascimento

2

Bản đồ là một phần của mô-đun vô số. Rất giống với "thu thập" Ví dụ:

  Class Car

    attr_accessor :name, :model, :year

    Def initialize (make, model, year)
      @make, @model, @year = make, model, year
    end

  end

  list = []
  list << Car.new("Honda", "Accord", 2016)
  list << Car.new("Toyota", "Camry", 2015)
  list << Car.new("Nissan", "Altima", 2014)

  p list.map {|p| p.model}

Map cung cấp các giá trị lặp qua một mảng được trả về bởi các tham số khối.


bản đồ là chính xác như thu thập.
BKSpurgeon

0

#each

#eachchạy một hàm cho mỗi phần tử trong một mảng. Hai đoạn trích sau đây là tương đương:

x = 10
["zero", "one", "two"].each{|element|
    x++
    puts element
}
x = 10
array = ["zero", "one", "two"]

for i in 0..2
    x++
    puts array[i]
end

#map

#mapáp dụng một hàm cho mỗi phần tử của một mảng, trả về mảng kết quả. Sau đây là tương đương:

array = ["zero", "one", "two"]
newArray = array.map{|element| element.capitalize()}
array = ["zero", "one", "two"]

newArray = []
array.each{|element|
    newArray << element.capitalize()
}

#map!

#map!là như thế #map, nhưng sửa đổi các mảng tại chỗ. Sau đây là tương đương:

array = ["zero", "one", "two"]
array.map!{|element| element.capitalize()}
array = ["zero", "one", "two"]
array = array.map{|element| element.capitalize()}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.