Cách thức đúng của người Hồi giáo để lặp qua một mảng trong Ruby là gì?


341

PHP, đối với tất cả các mụn cóc của nó, là khá tốt về số lượng này. Không có sự khác biệt giữa một mảng và một hàm băm (có thể tôi ngây thơ, nhưng điều này rõ ràng đúng với tôi) và lặp đi lặp lại thông qua bạn chỉ cần làm

foreach (array/hash as $key => $value)

Trong Ruby có rất nhiều cách để làm điều này:

array.length.times do |i|
end

array.each

array.each_index

for i in array

Băm có ý nghĩa hơn, vì tôi luôn luôn sử dụng

hash.each do |key, value|

Tại sao tôi không thể làm điều này cho mảng? Nếu tôi chỉ muốn nhớ một phương thức, tôi đoán tôi có thể sử dụng each_index(vì nó làm cho cả chỉ số và giá trị khả dụng), nhưng thật khó chịu khi phải làm array[index]thay vì chỉ value.


Oh phải, tôi quên mất array.each_with_index. Tuy nhiên, cái này hút vì nó đi |value, key|hash.eachđi |key, value|! Đây không phải là điên rồ?


1
Tôi đoán array#each_with_indexsử dụng |value, key|vì tên phương thức ngụ ý thứ tự, trong khi thứ tự được sử dụng để hash#eachbắt chước hash[key] = valuecú pháp?
Stewineer

3
Nếu bạn chỉ mới bắt đầu với các vòng lặp trong Ruby, thì hãy kiểm tra bằng cách sử dụng chọn, từ chối, thu thập, tiêm và phát hiện
Matthew Carriere

Câu trả lời:


560

Điều này sẽ lặp qua tất cả các yếu tố:

array = [1, 2, 3, 4, 5, 6]
array.each { |x| puts x }

Bản in:

1
2
3
4
5
6

Điều này sẽ lặp qua tất cả các yếu tố mang lại cho bạn giá trị và chỉ mục:

array = ["A", "B", "C"]
array.each_with_index {|val, index| puts "#{val} => #{index}" }

Bản in:

A => 0
B => 1
C => 2

Tôi không chắc chắn từ câu hỏi của bạn mà bạn đang tìm kiếm.


1
Ngoài chủ đề tôi không thể tìm thấy mỗi_with_index trong ruby ​​1.9 trong mảng
CantGetANick

19
@CantGetANick: Lớp Array bao gồm mô đun Enumerable có phương thức này.
xuinkrbin.

87

Tôi nghĩ rằng không có cách nào đúng . Có rất nhiều cách khác nhau để lặp lại, và mỗi cách có một phân khúc riêng.

  • each là đủ cho nhiều cách sử dụng, vì tôi thường không quan tâm đến các chỉ mục.
  • each_ with _index hoạt động như Hash # mỗi - bạn nhận được giá trị và chỉ mục.
  • each_index- chỉ các chỉ số. Tôi không sử dụng cái này thường xuyên. Tương đương với "length.times".
  • map là một cách khác để lặp lại, hữu ích khi bạn muốn chuyển đổi một mảng thành một mảng khác.
  • select là trình vòng lặp để sử dụng khi bạn muốn chọn một tập hợp con.
  • inject là hữu ích để tạo ra tổng hoặc sản phẩm, hoặc thu thập một kết quả duy nhất.

Nó có vẻ như rất nhiều để nhớ, nhưng đừng lo lắng, bạn có thể nhận được mà không cần biết tất cả chúng. Nhưng khi bạn bắt đầu tìm hiểu và sử dụng các phương pháp khác nhau, mã của bạn sẽ trở nên sạch hơn và rõ ràng hơn và bạn sẽ tiếp tục làm chủ Ruby.


câu trả lời chính xác! Tôi muốn đề cập đến ngược lại như #reject và bí danh như #collect.
Emily Tui Ch

60

Tôi không nói rằng Array-> |value,index|Hash-> |key,value|không điên rồ (xem bình luận của Horace Loeb), nhưng tôi đang nói rằng có một cách lành mạnh để mong đợi sự sắp xếp này.

Khi tôi đang xử lý các mảng, tôi tập trung vào các phần tử trong mảng (không phải chỉ mục vì chỉ mục là nhất thời). Phương thức này là mỗi với chỉ mục, tức là mỗi + chỉ mục, hoặc | mỗi, chỉ mục |, hoặc |value,index|. Điều này cũng phù hợp với chỉ mục được xem như một đối số tùy chọn, ví dụ | value | tương đương với | value, index = nil | phù hợp với | giá trị, chỉ số |.

Khi tôi đang đối phó với băm, tôi thường xuyên hơn tập trung vào các phím hơn các giá trị, và tôi thường đối phó với các phím và các giá trị theo thứ tự đó, một trong hai key => valuehoặc hash[key] = value.

Nếu bạn muốn gõ vịt, thì rõ ràng sử dụng một phương thức được xác định như Brent Longborough đã hiển thị hoặc một phương thức ngầm như maxhawkins đã hiển thị.

Ruby là tất cả về việc cung cấp ngôn ngữ cho phù hợp với lập trình viên, không phải là về lập trình viên phù hợp với ngôn ngữ. Đây là lý do tại sao có rất nhiều cách. Có rất nhiều cách để suy nghĩ về một cái gì đó. Trong Ruby, bạn chọn mã gần nhất và phần còn lại của mã thường rơi ra cực kỳ gọn gàng và chính xác.

Đối với câu hỏi ban đầu, "Cách thức đúng đắn của người Bỉ để lặp qua một mảng trong Ruby là gì?", Tôi nghĩ rằng cách cốt lõi (nghĩa là không có cú pháp cú pháp mạnh mẽ hoặc sức mạnh hướng đối tượng) là để làm:

for index in 0 ... array.size
  puts "array[#{index}] = #{array[index].inspect}"
end

Nhưng Ruby là tất cả về sức mạnh cú pháp mạnh mẽ và hướng đối tượng, nhưng dù sao ở đây là tương đương với băm, và các phím có thể được đặt hàng hoặc không:

for key in hash.keys.sort
  puts "hash[#{key.inspect}] = #{hash[key].inspect}"
end

Vì vậy, câu trả lời của tôi là, "Cách thức ngay lập tức để chuyển qua một mảng trong Ruby phụ thuộc vào bạn (tức là lập trình viên hoặc nhóm lập trình) và dự án.". Lập trình viên Ruby tốt hơn làm cho sự lựa chọn tốt hơn (trong đó sức mạnh cú pháp và / hoặc cách tiếp cận hướng đối tượng). Lập trình viên Ruby tốt hơn tiếp tục tìm kiếm nhiều cách hơn.


Bây giờ, tôi muốn hỏi một câu hỏi khác, "Cách thức đúng đắn của Hồi giáo để lặp qua một phạm vi trong Ruby ngược là gì?"! (Câu hỏi này là cách tôi đến trang này.)

Đó là tốt đẹp để làm (cho về phía trước):

(1..10).each{|i| puts "i=#{i}" }

nhưng tôi không thích làm (cho ngược):

(1..10).to_a.reverse.each{|i| puts "i=#{i}" }

Chà, tôi thực sự không bận tâm đến việc đó quá nhiều, nhưng khi tôi dạy ngược, tôi muốn cho học sinh của mình thấy một sự đối xứng đẹp (nghĩa là với sự khác biệt tối thiểu, ví dụ như chỉ thêm một bước lùi, hoặc một bước -1, nhưng không có sửa đổi bất cứ điều gì khác). Bạn có thể làm (đối xứng):

(a=*1..10).each{|i| puts "i=#{i}" }

(a=*1..10).reverse.each{|i| puts "i=#{i}" }

điều mà tôi không thích nhiều, nhưng bạn không thể làm

(*1..10).each{|i| puts "i=#{i}" }
(*1..10).reverse.each{|i| puts "i=#{i}" }
#
(1..10).step(1){|i| puts "i=#{i}" }
(1..10).step(-1){|i| puts "i=#{i}" }
#
(1..10).each{|i| puts "i=#{i}" }
(10..1).each{|i| puts "i=#{i}" }   # I don't want this though.  It's dangerous

Cuối cùng bạn có thể làm

class Range

  def each_reverse(&block)
    self.to_a.reverse.each(&block)
  end

end

nhưng tôi muốn dạy Ruby thuần túy hơn là các cách tiếp cận hướng đối tượng (chưa có). Tôi muốn lặp lại ngược lại:

  • mà không tạo ra một mảng (xem xét 0..1000000000)
  • làm việc cho bất kỳ Phạm vi nào (ví dụ: Chuỗi, không chỉ Số nguyên)
  • mà không sử dụng bất kỳ sức mạnh hướng đối tượng bổ sung (nghĩa là không sửa đổi lớp)

Tôi tin rằng điều này là không thể nếu không định nghĩa một predphương thức, có nghĩa là sửa đổi lớp Range để sử dụng nó. Nếu bạn có thể làm điều này xin vui lòng cho tôi biết, nếu không thì việc xác nhận tính không thể sẽ được đánh giá cao mặc dù điều đó sẽ gây thất vọng. Có lẽ Ruby 1.9 giải quyết điều này.

(Cảm ơn bạn đã dành thời gian đọc nó.)


5
1.upto(10) do |i| puts i end10.downto(1) do puts i endloại cho sự đối xứng bạn muốn. Mong rằng sẽ giúp. Không chắc chắn nếu nó sẽ làm việc cho chuỗi và như vậy.
Suren

(1..10) .to_a.sort {| x, y | y <=> x} .each {| i | đặt "i = # {i}"} - đảo ngược chậm hơn.
Davidslv

Bạn có thể [*1..10].each{|i| puts "i=#{i}" }.
Hauleth

19

Sử dụng Each_with_index khi bạn cần cả hai.

ary.each_with_index { |val, idx| # ...

12

Các câu trả lời khác chỉ tốt, nhưng tôi muốn chỉ ra một điều ngoại vi khác: Mảng được sắp xếp, trong khi Băm không nằm trong 1.8. (Trong Ruby 1.9, Băm được sắp xếp theo thứ tự các phím.) Vì vậy, sẽ không có ý nghĩa gì trước 1.9 để lặp lại Hash theo cùng một cách / trình tự như Mảng, vốn luôn có thứ tự xác định. Tôi không biết thứ tự mặc định là gì cho các mảng kết hợp PHP (dường như google fu của tôi không đủ mạnh để tìm ra điều đó), nhưng tôi không biết làm thế nào bạn có thể xem xét các mảng PHP và mảng kết hợp PHP thông thường là "giống nhau" trong bối cảnh này, vì thứ tự cho các mảng kết hợp dường như không được xác định.

Như vậy, cách Ruby có vẻ rõ ràng và trực quan hơn đối với tôi. :)


1
băm và mảng là cùng một điều! mảng ánh xạ các số nguyên cho các đối tượng và băm các đối tượng ánh xạ tới các đối tượng. mảng chỉ là trường hợp đặc biệt của băm, không?
Tom Lehman

1
Như tôi đã nói, một mảng là một tập hợp có thứ tự. Một ánh xạ (theo nghĩa chung) là không có thứ tự. Nếu bạn giới hạn khóa được đặt thành số nguyên (chẳng hạn như với mảng), điều đó sẽ xảy ra khi bộ khóa có thứ tự. Trong ánh xạ chung (Hash / Associative Array), các khóa có thể không có thứ tự.
Pistos

@Horace: Băm và mảng không giống nhau. Nếu một là một acase đặc biệt của người khác, họ không thể giống nhau. Nhưng thậm chí tệ hơn, mảng không phải là một loại băm đặc biệt, đó chỉ là một cách trừu tượng để xem chúng. Như Brent ở trên đã chỉ ra, sử dụng băm và mảng thay thế cho nhau có thể chỉ ra mùi mã.
Zane

9

Cố gắng làm điều tương tự một cách nhất quán với mảng và băm có thể chỉ là mùi mã, nhưng, có nguy cơ tôi bị gắn mác là một người vá nửa con mã, nếu bạn đang tìm kiếm hành vi nhất quán, điều này có làm nên chuyện không ?

class Hash
    def each_pairwise
        self.each { | x, y |
            yield [x, y]
        }
    end
end

class Array
    def each_pairwise
        self.each_with_index { | x, y |
            yield [y, x]
        }
    end
end

["a","b","c"].each_pairwise { |x,y|
    puts "#{x} => #{y}"
}

{"a" => "Aardvark","b" => "Bogle","c" => "Catastrophe"}.each_pairwise { |x,y|
    puts "#{x} => #{y}"
}

7

Dưới đây là bốn tùy chọn được liệt kê trong câu hỏi của bạn, được sắp xếp bởi quyền tự do kiểm soát. Bạn có thể muốn sử dụng một cái khác tùy thuộc vào những gì bạn cần.

  1. Đơn giản chỉ cần đi qua các giá trị:

    array.each
  2. Đơn giản chỉ cần đi qua các chỉ số:

    array.each_index
  3. Đi qua các chỉ số + biến chỉ số:

    for i in array
  4. Số vòng lặp điều khiển + biến chỉ số:

    array.length.times do | i |

5

Tôi đã cố gắng xây dựng một menu (trong Cắm trạiMarkaby ) bằng cách sử dụng hàm băm.

Mỗi mục có 2 yếu tố: nhãn menuURL , do đó, hàm băm có vẻ đúng, nhưng '/' URL cho 'Trang chủ' luôn xuất hiện sau cùng (như bạn mong đợi cho hàm băm), vì vậy các mục menu xuất hiện sai đặt hàng.

Sử dụng một mảng với each_slicecông việc:

['Home', '/', 'Page two', 'two', 'Test', 'test'].each_slice(2) do|label,link|
   li {a label, :href => link}
end

Thêm các giá trị bổ sung cho mỗi mục menu (ví dụ như tên CSS ID ) chỉ có nghĩa là tăng giá trị lát. Vì vậy, giống như một hàm băm nhưng với các nhóm bao gồm bất kỳ số lượng các mục. Hoàn hảo.

Vì vậy, đây chỉ là để nói lời cảm ơn vì vô tình gợi ý về một giải pháp!

Rõ ràng, nhưng đáng nói: Tôi đề nghị kiểm tra xem độ dài của mảng có chia hết cho giá trị lát không.


4

Nếu bạn sử dụng đếm được mixin (như Rails không), bạn có thể làm điều gì đó tương tự như php đoạn mã niêm yết. Chỉ cần sử dụng phương thức Each_slice và làm phẳng hàm băm.

require 'enumerator' 

['a',1,'b',2].to_a.flatten.each_slice(2) {|x,y| puts "#{x} => #{y}" }

# is equivalent to...

{'a'=>1,'b'=>2}.to_a.flatten.each_slice(2) {|x,y| puts "#{x} => #{y}" }

Yêu cầu vá khỉ ít hơn.

Tuy nhiên, điều này không gây ra vấn đề khi bạn có một mảng đệ quy hoặc hàm băm với các giá trị mảng. Trong ruby ​​1.9, vấn đề này được giải quyết với một tham số cho phương pháp làm phẳng chỉ định mức độ sâu để tái diễn.

# Ruby 1.8
[1,2,[1,2,3]].flatten
=> [1,2,1,2,3]

# Ruby 1.9
[1,2,[1,2,3]].flatten(0)
=> [1,2,[1,2,3]]

Đối với câu hỏi liệu đây có phải là mùi mã không, tôi không chắc chắn. Thông thường khi tôi phải cúi người về phía sau để lặp đi lặp lại một cái gì đó tôi lùi lại và nhận ra mình đang tấn công vấn đề sai.


2

Cách đúng đắn là cách bạn cảm thấy thoải mái nhất và đó là điều bạn muốn nó làm. Trong lập trình hiếm khi có một cách 'chính xác' để thực hiện, thường thì có nhiều cách để chọn.

Nếu bạn cảm thấy thoải mái với một số cách làm việc nhất định, hãy làm nó, trừ khi nó không hoạt động - thì đã đến lúc tìm cách tốt hơn.


2

Trong Ruby 2.1, phương thức Each_with_index bị xóa. Thay vào đó, bạn có thể sử dụng Each_index

Thí dụ:

a = [ "a", "b", "c" ]
a.each_index {|x| print x, " -- " }

sản xuất:

0 -- 1 -- 2 --

4
Đây không phải là sự thật. Như đã nêu trong các ý kiến ​​của câu trả lời được chấp nhận, lý do each_with_indexkhông xuất hiện trong tài liệu là vì nó được cung cấp bởi Enumerablemô-đun.
ihaztehcodez

0

Sử dụng cùng một phương pháp để lặp qua cả mảng và băm có ý nghĩa, ví dụ để xử lý các cấu trúc băm và mảng lồng nhau thường xuất phát từ các trình phân tích cú pháp, từ việc đọc các tệp JSON, v.v.

Một cách thông minh chưa được đề cập là cách nó được thực hiện trong thư viện Ruby Facets của các phần mở rộng thư viện tiêu chuẩn. Từ đây :

class Array

  # Iterate over index and value. The intention of this
  # method is to provide polymorphism with Hash.
  #
  def each_pair #:yield:
    each_with_index {|e, i| yield(i,e) }
  end

end

Đã có Hash#each_pair, một bí danh Hash#each. Vì vậy, sau bản vá này, chúng tôi cũng có Array#each_pairvà có thể sử dụng nó thay thế cho nhau để lặp qua cả Băm và Mảng. Điều này sửa chữa sự điên rồ quan sát được của OP Array#each_with_indexcó các đối số khối đảo ngược so với Hash#each. Ví dụ sử dụng:

my_array = ['Hello', 'World', '!']
my_array.each_pair { |key, value| pp "#{key}, #{value}" }

# result: 
"0, Hello"
"1, World"
"2, !"

my_hash = { '0' => 'Hello', '1' => 'World', '2' => '!' }
my_hash.each_pair { |key, value| pp "#{key}, #{value}" }

# result: 
"0, Hello"
"1, World"
"2, !"
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.