Tính bằng nhau và hash
Thuộc phần Lập trình hướng đối tượng trong hành trình Ruby của Coddy. Bài học 37 trên 57.
Ruby có một số kiểu so sánh bằng. == là phép so sánh bằng theo giá trị, được bạn định nghĩa cho lớp của mình. Hash và uniq sử dụng hai phương thức khác: eql? và hash. Khi chỉ định nghĩa ==, các điểm có giá trị bằng nhau vẫn là những khóa hash khác nhau:
class Point
attr_reader :x, :y
def initialize(x, y)
@x = x
@y = y
end
def ==(other)
other.is_a?(Point) && x == other.x && y == other.y
end
end
a = Point.new(1, 2)
b = Point.new(1, 2)
puts a == b
puts [a, b].uniq.size
puts({ a => "first" }[b].inspect)Đầu ra:
true
2
nilHash so sánh các giá trị hash, là những con số được tính từ một đối tượng, rồi kiểm tra các ứng viên bằng eql?. Hãy định nghĩa cả hai dựa trên cùng các trường như ==. alias eql? == dùng lại ==, còn [x, y].hash kết hợp các trường:
class Point
attr_reader :x, :y
def initialize(x, y)
@x = x
@y = y
end
def ==(other)
other.is_a?(Point) && x == other.x && y == other.y
end
alias eql? ==
def hash
[x, y].hash
end
end
a = Point.new(1, 2)
b = Point.new(1, 2)
puts [a, b, Point.new(3, 4)].uniq.size
puts({ a => "first" }[b])Đầu ra:
2
firstQuy tắc là: các đối tượng thỏa mãn eql? phải có cùng hash. Hai đối tượng khác nhau có thể tình cờ có cùng giá trị hash, vì vậy eql? vẫn cần kiểm tra. Khi đó, đếm bằng Hash sẽ dựa trên giá trị thay vì danh tính của đối tượng:
class Word
attr_reader :text
def initialize(text)
@text = text.downcase
end
def ==(other)
other.is_a?(Word) && text == other.text
end
alias eql? ==
def hash
text.hash
end
end
counts = Hash.new(0)
%w[Ruby ruby RUBY gem].each { |t| counts[Word.new(t)] += 1 }
counts.each { |word, n| puts "#{word.text}: #{n}" }Kết quả:
ruby: 3
gem: 1equal? vẫn giữ nghĩa so sánh định danh và không bao giờ nên được định nghĩa lại. Các giá trị dựng sẵn đã tuân theo những quy tắc này, đó là lý do chuỗi và mảng có thể làm khóa cho hash:
a == b # cùng giá trị (bạn tự định nghĩa)
a.eql?(b) # cùng giá trị khi dùng làm khóa Hash và với uniq (bí danh của ==)
a.hash # một số; các đối tượng bằng nhau cho ra các số bằng nhau
a.equal?(b) # cùng một đối tượng (đừng bao giờ định nghĩa lại)Thử thách
DễHoàn thiện lớp Point để các điểm có cùng tọa độ được xem là bằng nhau với ==, eql? và hash, đồng thời hoạt động làm khóa trong Hash. to_s trả về (1, 2).
Code được cung cấp đọc các điểm từ những dòng có dạng x,y. Code in ra số điểm riêng biệt bằng uniq, rồi đếm số lần ghé thăm từng điểm trong Hash và in chúng theo thứ tự ghé thăm lần đầu dưới dạng (1, 2): 2, cuối cùng cho biết một Point.new(0, 0) mới có phải là khóa hay không.
Code của bạn đặt trong point.rb. main.rb chứa code đầu vào/đầu ra được cung cấp và không thể chỉnh sửa.
Tự làm thử
require_relative 'point'
# Đoạn code đầu vào/đầu ra có sẵn: giữ nguyên
input = []
while (line = gets)
input << line.chomp
end
points = input.map { |line| Point.new(*line.split(",").map(&:to_i)) }
puts "distinct: #{points.uniq.size}"
visits = Hash.new(0)
points.each { |pt| visits[pt] += 1 }
visits.each { |pt, n| puts "#{pt}: #{n}" }
puts "origin visited: #{visits.key?(Point.new(0, 0))}"
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Lớp và đối tượng
Làm việc với tệpĐịnh nghĩa lớpBiến instancePhương thức và selfto_s và inspectÔn tập: thẻ thư viện4Tính đóng gói
Phương thức riêng tưPhương thức được bảo vệPhương thức thiết lập riêng tưBảo vệ trạng tháiÔn tập: máy bán hàng tự động7Tính đa hình và duck typing
Tính đa hìnhDuck typingPhương thức trừu tượngcase và lớpÔn tập: phương thức thanh toán2Phương thức truy cập và self
Phương thức đọc và ghiPhương thức gán tùy chỉnhself trong phương thứcĐịnh danh và tính bằng nhauÔn tập: Danh sách liên hệ8Phương thức đặc biệt
Tính bằng nhau và hashPhương thức toán tửTruy cập theo chỉ số và thêm phần tửBộ sưu tập tùy chỉnhÔn tập: phân sốTự luyện tập: Trình biên dịch Ruby online