2010-08-03 30 views
26

Làm thế nào tôi có thể nhận được một mảng lười biếng trong Ruby?Làm thế nào tôi có thể nhận được một mảng lười biếng trong Ruby?

Trong Haskell, tôi có thể nói về [1..], đây là danh sách vô hạn, được tạo ra khi cần thiết. Tôi cũng có thể làm những việc như iterate (+2) 0, áp dụng bất kỳ chức năng nào mà tôi cung cấp để tạo danh sách lười biếng. Trong trường hợp này, nó sẽ cho tôi tất cả các số chẵn.

Tôi chắc rằng tôi có thể làm những việc như vậy trong Ruby, nhưng dường như không thể giải quyết được.

+2

Về mảng lười: Mảng khác biệt đáng kể so với danh sách. Việc triển khai các mảng lười sẽ cho phép các mảng vô hạn, sẽ có các thuộc tính thời gian chạy khủng khiếp. – sepp2k

Trả lời

39

Với Ruby 1.9, bạn có thể sử dụng lớp Enumerator. Đây là một ví dụ từ các tài liệu:

fib = Enumerator.new { |y| 
    a = b = 1 
    loop { 
     y << a 
     a, b = b, a + b 
    } 
    } 

    p fib.take(10) #=> [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] 

Ngoài ra, đây là một thủ thuật rất hay:

Infinity = 1.0/0 

    range = 5..Infinity 
    p range.take(10) #=> [5, 6, 7, 8, 9, 10, 11, 12, 13, 14] 

Cái này chỉ có tác dụng cho các giá trị liên tiếp mặc dù.

+4

họ không phải liên tiếp: (10..100) .step (20) .take (5) # => [10, 20, 30, 40, 50 ] – horseyguy

+2

Trong Ruby 1.8, bạn có thể 'yêu cầu 'backports'' để có được chức năng này quá :-) –

+2

Lưu ý rằng mặc dù thực hiện' fib.map {| x | x + 1} .take (10) 'sẽ không hoạt động, vì bản đồ sẽ cố tạo một mảng. Cũng lưu ý rằng nếu bạn thực hiện 'fib.take (10)' hai lần, các phần tử sẽ được tính hai lần (không giống như các danh sách lười, nơi các phần tử được giữ trong bộ nhớ khi chúng được tính toán). Vì vậy, điều này không chính xác tương đương với danh sách lười biếng. – sepp2k

-3

Mảng Ruby tự động mở rộng khi cần. Bạn có thể áp dụng các khối cho họ để trả về những thứ như số chẵn.

array = [] 
array.size # => 0 
array[0] # => nil 
array[9999] # => nil 
array << 1 
array.size # => 1 
array << 2 << 3 << 4 
array.size # => 4 

array = (0..9).to_a 
array.select do |e| 
    e % 2 == 0 
end 

# => [0,2,4,6,8] 

Điều này có hữu ích không?

+0

cảm ơn bạn - tin vào những giấc mơ của bạn – carlfilips

+2

Không phải là câu hỏi về việc tạo ra các đối tượng * lười biếng? Giải pháp chính xác không phải là về mảng (mà luôn luôn được xác định hoàn toàn trong Ruby) mà là về các loại 'Số khác ', như trong câu trả lời của Sanjana. –

+0

Tôi mới bắt đầu ruby, tôi nghĩ điều này là chính xác. Tôi sẽ sửa lỗi này - có thể mong muốn của bạn trở thành sự thật – carlfilips

5

phạm vi Lazy (số tự nhiên):

Inf = 1.0/0.0 
(1..Inf).take(3) #=> [1, 2, 3] 

phạm vi Lazy (số chẵn):

(0..Inf).step(2).take(5) #=> [0, 2, 4, 6, 8] 

Lưu ý, bạn cũng có thể mở rộng Enumerable với một số phương pháp để làm việc với các dãy lười biếng (và vv) thuận tiện hơn:

module Enumerable 
    def lazy_select 
    Enumerator.new do |yielder| 
     each do |obj| 
     yielder.yield(obj) if yield(obj) 
     end 
    end 
    end 
end 

# first 4 even numbers 
(1..Inf).lazy_select { |v| v.even? }.take(4) 

output: 
[2, 4, 6, 8] 

Thông tin thêm tại đây: http://banisterfiend.wordpress.com/2009/10/02/wtf-infinite-ranges-in-ruby/

Ngoài ra còn có triển khai của lazy_map, và lazy_select cho lớp Enumerator có thể được tìm thấy ở đây: http://www.michaelharrison.ws/weblog/?p=163

+0

Lưu ý rằng, giống như với giải pháp điều tra viên, bạn không thể làm những việc như 'infinite_range.filter {| x | f (x)} lấy (5) ', vì vậy nó không hoạt động như danh sách lười biếng. – sepp2k

+0

@ sepp2k, thêm liên kết đến trang web có triển khai 'lazy_select' v.v. đối với lớp' Enumerator' – horseyguy

1

Như tôi đã nói trong ý kiến ​​của tôi, thực hiện một điều như mảng lười biếng sẽ không được hợp lý.

Sử dụng số đếm thay vào đó có thể hoạt động tốt trong một số trường hợp, nhưng khác với danh sách lười biếng ở một số điểm: các phương pháp như bản đồ và bộ lọc sẽ không được đánh giá một cách lười biếng (vì vậy chúng sẽ không hoạt động trên bảng kê vô hạn) được tính một lần không được lưu trữ, vì vậy nếu bạn truy cập một phần tử hai lần, nó được tính hai lần.

Nếu bạn muốn chính xác hành vi của danh sách lười biếng của haskell trong ruby, có một lazylist gem thực hiện danh sách lười biếng.

19

Gần đây có thể đếm được :: Lười biếng đã được thêm vào thân cây hồng ngọc. Chúng ta sẽ thấy nó trong ruby ​​2.0. Cụ thể:

a = data.lazy.map(&:split).map(&:reverse) 

sẽ không được đánh giá ngay lập tức.
Kết quả là ví dụ của Enumerable :: Lazy, có thể được lười biếng xích thêm nữa.Nếu bạn muốn để có được một kết quả thực tế - sử dụng #to_a, #take(n) (#take bây giờ lười quá, sử dụng #to_a hoặc #force) vv
Nếu bạn muốn biết thêm về chủ đề này và vá C của tôi - xem bài viết trên blog của tôi Ruby 2.0 Enumerable::Lazy

1

Điều này sẽ lặp đến vô cùng:

0.step{|i| puts i} 

Điều này sẽ lặp đến vô cùng gấp đôi nhanh:

0.step(nil, 2){|i| puts i} 

Điều này sẽ chuyển đến vô cùng, chỉ khi bạn muốn (kết quả trong một điều tra viên).

table_of_3 = 0.step(nil, 3) 
Các vấn đề liên quan