Computer >> Máy Tính >  >> Lập trình >> Ruby

Làm việc với phân số và hợp lý trong Ruby

Tôi có một lời thú nhận những thứ đã làm. Tôi ghét những con số dấu phẩy động. Chắc chắn, chúng hữu ích nếu bạn là một chiếc máy tính, nhưng nếu bạn là con người, bạn sẽ phải vò đầu bứt tai trước những tình huống như thế này:

129.95 * 100
# => 12994.999999999998

Điều này không chỉ bay khi đối mặt với sự hài hòa toán học mà còn là UX tồi.

Nếu một công thức yêu cầu bạn đo 0,37211927843 cốc bột, bạn có thể sẽ tự cười bản thân về tác giả của một tên ngốc và bắt đầu đo một phần ba cốc.

Hầu hết mọi người có thể nghĩ về phân số dễ dàng hơn rất nhiều so với việc họ có thể nghĩ về các số thập phân tùy ý. Vì vậy, nếu ứng dụng của bạn đang cố gắng truyền đạt các con số cho mọi người, bạn nên khám phá các cách thể hiện chúng dưới dạng phân số.

Rational của Ruby class là một công cụ tuyệt vời để làm việc với các số hữu tỉ. Nó không chỉ cung cấp cho bạn khả năng làm toán hữu tỉ mà còn cho phép bạn tìm các phân số đơn giản gần đúng với số dấu phẩy động gnarly. Hãy cùng xem!

Hợp lý là gì?

Đối với mục đích của chúng tôi, "số hữu tỉ" chỉ là một cách nói hoa mỹ để nói "phân số". Chúng có hai phần:tử số và mẫu số.

1/2 # Numerator is 1. Denominator is 2. 
5   # Numerator is 5. Denominator is 1.

Trong Ruby, các số hữu tỉ có kiểu dữ liệu riêng giống như số nguyên và số dấu phẩy động. Có một số cách để tạo ra một lý trí mới:

3/2r              # This syntax was introduced in Ruby 2.1
1.5.to_r          # Floats can be converted to rationals via `to_r`
"3/2".to_r        # ...so can strings
Rational('3/2')   # This is how we had to do things in the olden days
Rational(3, 2)    # ...see how hard life was?

Phép toán đơn giản

Khi bạn cộng, trừ, nhân hoặc chia hai số hữu tỉ, kết quả cũng là số hữu tỉ.

2/3r + 1/3r
# => (1/1)
2/3r - 1/3r
# => (1/3)
2/3r * 1/3r
# => (2/9)
(2/3r) / (1/3r) # We need parens here to avoid confusing the interpreter
# => (2/1)

Tất cả các toán tử toán học khác cũng hoạt động giống như bạn mong đợi:** , > , < , v.v ..

Quy tắc chung là cả hai đầu vào phải là phân số để kết quả là một phân số. Một ngoại lệ tôi có thể tìm thấy là với số nguyên. Vì tất cả các số nguyên đều hợp lý, nên Ruby thực hiện điều thông minh và giả định mọi người là đầu ra hợp lý:

2/3r + 2
# => (8/3)

Xấp xỉ

Một trong những điều hữu ích nhất về số hữu tỉ là chúng cho phép chúng ta tính gần đúng và dễ dàng thực hiện các phép tính trong đầu. Để tận dụng điều này, chúng ta cần giữ cho các phân số của chúng ta đơn giản. 3/2 thay vì 3320774221237909/2251799813685248 .

May mắn thay, Ruby cung cấp cho chúng ta một cách dễ dàng để chuyển đổi những con số chính xác nhưng xấu xí này thành những con số gần đúng nhưng đẹp. Tôi đang nói về rationalize phương pháp.

Đây là những gì nó trông như thế nào:

# Precise but ugly
(1.47472).to_r
=> (3320774221237909/2251799813685248)

# Less precise, but pretty
(1.47472).to_r.rationalize(0.05)
=> (3/2)

Phương pháp hợp lý hóa có một đối số. Nó chỉ định dung sai - lượng chính xác mà bạn sẵn sàng đánh đổi để đơn giản hóa.

Phương pháp tìm một số có mẫu số thấp nhất trong khả năng chịu đựng của bạn. Đây là ý tôi:

# What's the number with the lowest denominator between 5/10 and 7/10?
(6/10r).rationalize(1/10r)
# => (1/2)

# What's the number with the lowest denominator between 11/20 and 13/20?
(6/10r).rationalize(1/20r)
=> (3/5)

# ..and between 1/10 and 11/10?
(6/10r).rationalize(1/2r)
=> (1/1)

Xấp xỉ bằng máy nén

Nếu tất cả những gì bạn cần làm là tìm một số nguyên hoặc số dấu phẩy động tương ứng với phân số, bạn có một số tùy chọn.

# Return the nearest integer. 
(6/10r).round
# => 1

# Round down
(12/10r).to_i 
# => 1

Hạn chế của Luận điểm

Có một số hạn chế cần lưu ý khi làm việc với số hữu tỉ và Ruby. Tất nhiên bạn không thể chia cho 0:

4/0r
ZeroDivisionError: divided by 0

Và bạn sẽ gặp phải một số hành vi kỳ lạ nếu bạn cố gắng coi các số vô tỉ là số hữu tỉ.

# Umm, isn't the square root of 2 irrational?
Rational(Math.sqrt(2))
# => (6369051672525773/4503599627370496)

# And I'm pretty sure PI is irrational as well.
Rational(Math::PI)
# => (884279719003555/281474976710656)

Chúng ta có thể mong đợi rằng việc yêu cầu Ruby xử lý một số vô tỷ là hợp lý sẽ làm nảy sinh một số loại ngoại lệ. Nhưng tiếc là Ruby dường như không đủ thông minh để làm điều này. Thay vào đó, nó chuyển đổi các giá trị xấp xỉ dấu phẩy động của các số vô tỉ này thành các số hữu tỉ. Đó không phải là một vấn đề lớn, nhưng là một điều cần lưu ý.