“Map” yöntemi Ruby'de ne yapıyor?


250

Programlamaya yeniyim. Birisi ne .mapyapacağını açıklayabilir mi :

params = (0...param_count).map

9
Her seferinde bir soru sorun . mapbir dizideki değerleri (özel değerlendirmelerle) dönüştürmek için kullanılan numaralandırılabilir nesneler üzerinde bulunan yaygın bir "işlevsel" yöntemdir. ..ve ...aralık yaratmanın yollarıdır. Ayrıca, tanımak kendin Bu şeyleri deneyebilirsiniz REPL ile! :)

5
Yakut için REPL irb, Raylar için raylar c. REPL kodu doğrudan dil kabuğunun kendisine karşı test etmenizi sağlar.
Gary

Yanıtlar:


431

mapYöntem, numaralandırılabilir nesne ve bir blok alır ve (kullanmak durumunda, orijinal bir amacı değişmez bloktan her dönen değeri çıkış olarak verilmesi, her bir eleman için blok çalışır map!):

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

Arrayve Rangenumaralandırılabilir türlerdir. mapbir blok ile bir Array döndürür. map!orijinal diziyi değiştirir.

Nerede bu yararlı olduğunu ve arasındaki fark nedir map!ve each? İşte bir örnek:

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

Çıktı:

Danil is a programmer
Edmund is a programmer

3
örnek için speransky teşekkürler. o zaman .map ile .each arasındaki fark nedir?
bigpotato

2
Ahhh anladım. Yani .map aslında diziyi değiştirir. .Each sadece orijinal diziye dokunmadan değerlere erişmek için dizi üzerinden döngüler?
bigpotato

24
Sıradan okuyucular için açılış cümlesinin mapsanki sanki tarif ettiği gibi tehlikelidirmap!
kaleidic

12
harita ve her biri arasındaki farkı görmek için bir IRB penceresi açın ve aşağıdaki kodda y ve z sonuçlarına bakın: y = [1,2,3] .each {| x | x + 1}; z = [1,2,3] .map {| x | x + 1}
davej

7
@Inquisitive: 'each', bir blok sağlandığında onu çağıran diziyi döndürür (örnekte [1,2,3]), 'map' blok tarafından hesaplanan değerlerle doldurulmuş yeni bir dizi döndürür. Bu yardımcı olabilir: ary = [1,2,3] değişkenini ayarlayın ve object_id değerine bakın. Sonra y = ary.each {| x | x + 1}; z = ary.map {| x | x + 1}. Şimdi y ve z'nin object_id öğelerini kontrol edin. y, ary ile aynı object_id değerine sahiptir (çünkü her biri ary döndürür), ancak z yeni bir dizi döndürdüğü için z farklı bir object_id değerine sahiptir.
davej

66

mapİle birlikte selectve eachbenim kodunda Ruby'nin workhorses biridir.

Dizinizin nesnelerinin her birinde bir işlem çalıştırmanıza ve hepsini aynı yere döndürmenize olanak tanır. Örnek olarak bir sayı dizisini birer birer artırabiliriz:

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

Dizinizin öğelerinde tek bir yöntem çalıştırabiliyorsanız, bunu kısayol stilinde yapabilirsiniz:

  1. Bunu yukarıdaki örnekle yapmak için böyle bir şey yapmanız gerekir

    class Numeric
      def plusone
        self + 1
      end
    end
    [1,2,3].map(&:plusone)
    #=> [2,3,4]
  2. Ve işareti kısayol tekniğini daha basit kullanmak için farklı bir örnek kullanalım:

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

Ruby'de verilerin dönüştürülmesi genellikle bir dizi mapişlem gerektirir . Çalışma map& selectonlar birincil kütüphanede en yararlı Yakut yöntemlerden bazılarıdır. Onlar kadar önemli each.

( mapayrıca bir takma collectaddır. Kavramsal olarak sizin için en uygun olanı kullanın.)

Daha faydalı bilgiler:

Eğer Enumerable yayınladığınız nesne eachveya mapüzerinde Enumerable elemanları (hash değerleri, diziler) kümesi içerir engellenenler boruları yüzden mi içeride, o öğelerin her biri ilan edebilir:

[["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

Bir karma durumunda (aynı zamanda bir Enumerablenesne, bir karma sadece yorumlayıcı için özel talimatlar içeren bir dizi tupledir). İlk "boru parametresi" anahtar, ikincisi değerdir.

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

Asıl soruyu cevaplamak için:

paramsBu bir karma olduğunu varsayarsak , bu onun içinden eşlemenin en iyi yoludur: Karmadaki yorumlanan her bir demet için anahtar ve değer çiftini yakalamak için bir yerine iki blok parametresi kullanın.

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

Bu benim için işe yaramaz. NoMethodError: private method 'plusone' called for 1:FixnumYakut 2 ve yakut 1.9 / 1.8 'yanlış sayıda argüman' alıyorum . Her neyse, lambda kullandım: plusone = ->(x) { x + 1 }sembol belirleyicisini çıkar [1,2,3].map(&plusone).
tjmcewan

1
hmm private, metodunuzu koymadan önce metodunuzu koyduğunuz sınıfın içinde beyan ettiğiniz gibi geliyor
boulder_ruby

Evet, kesinlikle öyle. Dışında değildi. . :( o ikincisi düz irb sınıfların o / w, düz bir komut oldu Öncelikle İşte benim kopyası / kodunuzun yapıştırın: gist.github.com/tjmcewan/a7e4feb2976a93a5eef9
tjmcewan

Evet, tamamen koduma kötü bir örnek koydum özür dilerim. Değiştirilen kodu deneyin. Şimdi çalışıyor ...
boulder_ruby

1
@boulder_ruby bunu normal bir yöntemle yapmanın bir yolu var - sınıfta olduğu gibi değil mi?
tekknolagi

6

Ruby 2.4 kullanarak aynı şeyi kullanarak yapabilirsiniz transform_values, bu özellik raylardan yakutlara çıkarılır.

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

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

4

0..param_count"param_count'a kadar ve dahil" anlamına gelir. 0...param_count"en fazla, ancak param_count dahil değil" anlamına gelir.

Range#mapbir döndürmez Enumerable, aslında bir diziye eşler. İle aynı Range#to_a.


3

Bu, bir fonksiyondaki her öğeye bir işlev "eşleştirir" Enumerable- bu durumda bir aralık. Bu nedenle, 0 ile param_count(özel - noktalar hakkında haklısın) her tamsayı için bir kez geçirilen bloğu çağırır ve her dönüş değerini içeren bir dizi döndürür.

İşte belgeleri Enumerable#map. Ayrıca bir takma adı vardır collect.


Garip, ama Range#mapaslında bir diziye dönüştürüyor.
Pedro Nascimento

1
@PedroNascimento: Evet ... ben de öyle dedim?
Ry-

Maalesef, kendi aradığı haritanın Enumerableher biri gibi bir geri dönmediğinin farkında değildim . Öyle olduğunu düşündüm.
Pedro Nascimento

2

Harita, numaralandırılabilir modülün bir parçasıdır. "Topla" ya çok benzer Örneğin:

  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}

Harita, blok parametreleri tarafından döndürülen bir dizi üzerinden yinelenen değerler sağlar.


harita toplama ile tamamen aynı.
BKSpurgeon

0

#each

#eachdizideki her öğe için bir işlev çalıştırır. Aşağıdaki iki kod alıntısı eşdeğerdir:

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

#mapbir dizinin her öğesine bir işlev uygular ve sonuçta oluşan diziyi döndürür. Aşağıdakiler eşdeğerdir:

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

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

#map!

#map!gibidir #map, ancak diziyi yerinde değiştirir. Aşağıdakiler eşdeğerdir:

array = ["zero", "one", "two"]
array.map!{|element| element.capitalize()}
array = ["zero", "one", "two"]
array = array.map{|element| element.capitalize()}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.