2009-10-17 30 views
6

Cách trơn tru nhất, giống Ruby nhất để làm điều này là gì?Sự khác biệt thứ tự mảng đầu tiên trong Ruby

[1, 3, 10, 5].diff 

nên sản xuất

[2, 7, -5] 

có nghĩa là, một loạt các khác biệt thứ tự đầu tiên. Tôi đã đưa ra một giải pháp mà tôi sẽ thêm vào dưới đây, nhưng nó đòi hỏi ruby ​​1.9 và không phải là tất cả những slick. những gì khác là có thể?

Trả lời

2

Tuy nhiên, một way..Seems ngắn nhất cho đến nay :)

module Enumerable 
    def diff 
     self[1..-1].zip(self).map {|x| x[0]-x[1]} 
    end  
    end 
+0

Điều này thật dễ thương, nhưng khá kém hiệu quả nếu các mảng nhận được lớn ... –

+0

một giải pháp tương thích tốt 1.8. cảm ơn. – Peter

+1

Bạn có thể sử dụng liên kết phá hủy trong khối thay vì lập chỉ mục: '{| đầu tiên, thứ hai | thứ nhất - thứ hai} '. Ngoài ra, trên 1.8.7+ 'self [1 ..- 1]' tương đương với 'drop (1)', làm cho toàn bộ thứ được đọc như sau: 'drop (1) .zip (self) .collect {| first , thứ hai | thứ nhất - thứ hai} '. Một lần nữa, ngay cả khi bạn không biết Ruby và đọc to như tiếng Anh, nó gần như dễ hiểu, giống như định nghĩa toán học về sự khác biệt thứ tự đầu tiên: "Kết hợp Array với chính nó được dịch chuyển bằng cách tính toán sự khác biệt và thu thập kết quả . " –

1
# Attempt, requires ruby 1.9. 
module Enumerable 
    def diff 
    each_cons(2).with_object([]){|x,array| array << x[1] - x[0]} 
    end 
end 

Ví dụ:

[1,3,10,5].diff 
=> [2, 7, -5] 
+0

đẹp addon. each_cons. – Gishu

2

khái niệm này xuất phát từ chức năng lập trình, tất nhiên:

module Enumerable 
    def diff 
    self.inject([0]) { |r,x| r[-1] += x; r << -x } [1..-2] 
    end 
end 

[1,3,10,5].diff 

Lưu ý rằng bạn không cần bất kỳ biến trung gian riêng biệt ở đây

0

Một cách khác để làm điều đó.

module Enumerable 
    def diff 
    result = [] 
    each_with_index{ |x, i| 
     return result if (i == (self.length-1)) 
     result << self[i+1] - x 
    } 
    end 
end 
5

Tôi thích phong cách chức năng này:

module Enumerable 
    def diff 
    each_cons(2).map {|pair| pair.reverse.reduce :-} 
    end 
end 

EDIT: Tôi chỉ nhận ra rằng reverse là hoàn toàn không cần thiết. Nếu đây là ngôn ngữ chức năng, tôi đã sử dụng tính năng khớp mẫu, nhưng Ruby không hỗ trợ khớp mẫu. Tuy nhiên, nó không, hỗ trợ liên kết phá hủy, là một xấp xỉ đủ tốt để khớp mẫu trong trường hợp này.

each_cons(2).map {|first, second| second - first} 

Không có mặt cười.

Tôi thích âm thanh này như thế nào nếu bạn chỉ đọc to từ trái sang phải: "Đối với mỗi cặp, hãy áp dụng sự khác biệt giữa phần tử thứ nhất và thứ hai của cặp." Trong thực tế, tôi thường không thích tên collect và thích map thay vào đó, nhưng trong trường hợp này mà đọc thậm chí tốt hơn: "Đối với mỗi cặp, thu phần chênh lệch giữa các thành phần của nó"

each_cons(2).collect {|first, second| second - first} 

Âm thanh gần giống như định nghĩa về sự khác biệt thứ tự đầu tiên.

+0

Trông khá, nhưng nó chậm hơn 4 lần so với tôi ... –

+0

Thực tế chỉ chậm hơn 2,6x, nhưng vẫn còn. –

+0

Ồ, và trên mảng dài sự khác biệt lớn hơn. Với mảng 1000 số, tốc độ này chậm hơn khoảng 10 lần so với phiên bản nhanh nhất của tôi! –

0

nỗ lực yếu ớt của tôi ...

module Enumerable 
    def diff 
    na = [] 
    self.each_index { |x| r << self[x]-self[x-1] if x > 0 } 
    na 
    end 
end 

p [1,3,10,5].diff #returned [2, 7, -5] 
+0

Cách sử dụng 'inject' để tạo danh sách thay thế? Rút ngắn phương thức từ 3 đến 1 dòng và dễ đọc hơn (imo, tất nhiên, nhưng ít nhất là giống ruby ​​hơn). – rfunduk

+0

Nhưng lưu ý rằng điều đó cũng sẽ làm cho nó chậm hơn ... –

2

Dưới đây là cách nhanh nhất tôi có thể tìm (nhanh hơn so với tất cả những người khác đề nghị tại thời điểm này, trong cả hai phiên bản 1.8 và 1.9):

module Enumerable 
    def diff 
    last=nil 
    map do |x| 
     r = last ? x - last : nil 
     last = x 
     r 
    end.compact 
    end 
end 

Với này xếp hàng Á hậu:

module Enumerable 
    def diff 
    r = [] 
    1.upto(size-1) {|i| r << self[i]-self[i-1]} 
    r 
    end 
end 

Trong số những người khác ở đây, nỗ lực tự mô tả "yếu ớt" của testr là nhanh nhất tiếp theo, nhưng vẫn chậm hơn một trong hai cách này.

Và nếu tốc độ là không có đối tượng, đây là yêu thích thẩm mỹ của tôi:

module Enumerable 
    def diff! 
    [-shift+first] + diff! rescue [] 
    end 

    def diff 
    dup.diff! 
    end 
end 

Nhưng đây là (vì lý do tôi không hoàn toàn hiểu) theo độ chậm hơn so với bất cứ đề nghị khác ở đây!

+0

wow, tôi không có ý tưởng giải pháp của tôi đã được nhanh chóng. tôi chỉ cố gắng làm cho nó hoạt động với rất ít phương pháp mà tôi quen thuộc với (xin lỗi, vẫn là một newbie). Rất thích chủ đề này, tôi đã học về each_cons và tiêm :) – testr

+0

Nếu tốc độ thực thi là mối quan tâm chính, có thể bạn sẽ không sử dụng Ruby để bắt đầu, nhưng tôi nghĩ sẽ rất thú vị khi chạy thử nghiệm. –

+0

Hầu hết các triển khai Ruby hiện có khá crappy tại các lời gọi phương thức. Ngoài ra, việc triển khai rộng rãi được sử dụng là khá crappy lúc phân bổ đối tượng. Trong giải pháp của bạn, bạn tái sử dụng một lần cho mỗi phần tử của mảng, và bạn phân bổ hai mảng mới cho mỗi bước, vì vậy tất cả trong tất cả các bạn có chiều sâu ngăn xếp cuộc gọi n, 6 cuộc gọi phương thức mỗi bước (tổng 6n) và tổng cộng 2n mảng được phân bổ. Tất cả đều khá chậm trên các máy ảo hiện tại. Trình biên dịch JRuby JIT mới (chưa được phát hành) có loại bỏ phân bổ mảng (trong số những thứ khác), và có khả năng tăng tốc độ này một cách đáng kể. –

2

biến nhỏ trên Jörg W Mittag của:

module Enumerable 
    def diff 
    each_cons(2).map{|a,b| b-a} 
    end 
end 
+0

Đẹp, điều này là dễ dàng nhất rõ ràng (nếu 1,9 là OK). 1.6x chậm hơn phiên bản của tôi trong các bài kiểm tra của tôi ... –

+0

Tôi nghĩ rằng nó hoạt động trong 1.8.7 nhưng không phải là 1.8.6 – jes5199

Các vấn đề liên quan