From 70a4471e40ca9dc54132d92d465d5bed07deed69 Mon Sep 17 00:00:00 2001 From: mRcfps <1402491442@qq.com> Date: Tue, 20 Feb 2018 12:52:03 +0800 Subject: [PATCH 1/2] rational-numbers: implement exercise --- config.json | 12 ++ exercises/rational-numbers/README.md | 59 ++++++++ exercises/rational-numbers/example.py | 58 ++++++++ .../rational-numbers/rational_numbers.py | 34 +++++ .../rational-numbers/rational_numbers_test.py | 132 ++++++++++++++++++ 5 files changed, 295 insertions(+) create mode 100644 exercises/rational-numbers/README.md create mode 100644 exercises/rational-numbers/example.py create mode 100644 exercises/rational-numbers/rational_numbers.py create mode 100644 exercises/rational-numbers/rational_numbers_test.py diff --git a/config.json b/config.json index 12b936eed6..65ed4f7e95 100644 --- a/config.json +++ b/config.json @@ -944,6 +944,18 @@ "conditionals" ] }, + { + "uuid": "1d21cd68-10ac-427d-be6d-77152bceacc4", + "slug": "rational-numbers", + "core": false, + "unlocked_by": null, + "difficulty": 2, + "topics": [ + "classes", + "arithmetic", + "operator_overloading" + ] + }, { "uuid": "a7bc6837-59e4-46a1-89a2-a5aa44f5e66e", "slug": "diamond", diff --git a/exercises/rational-numbers/README.md b/exercises/rational-numbers/README.md new file mode 100644 index 0000000000..cd2b0b6fb4 --- /dev/null +++ b/exercises/rational-numbers/README.md @@ -0,0 +1,59 @@ +# Rational Numbers + +A rational number is defined as the quotient of two integers `a` and `b`, called the numerator and denominator, respectively, where `b != 0`. + +The absolute value `|r|` of the rational number `r = a/b` is equal to `|a|/|b|`. + +The sum of two rational numbers `r1 = a1/b1` and `r2 = a2/b2` is `r1 + r2 = a1/b1 + a2/b2 = (a1 * b2 + a2 * b1) / (b1 * b2)`. + +The difference of two rational numbers `r1 = a1/b1` and `r2 = a2/b2` is `r1 - r2 = a1/b1 - a2/b2 = (a1 * b2 - a2 * b1) / (b1 * b2)`. + +The product (multiplication) of two rational numbers `r1 = a1/b1` and `r2 = a2/b2` is `r1 * r2 = (a1 * a2) / (b1 * b2)`. + +Dividing a rational number `r1 = a1/b1` by another `r2 = a2/b2` is `r1 / r2 = (a1 * b2) / (a2 * b1)` if `a2 * b1` is not zero. + +Exponentiation of a rational number `r = a/b` to a non-negative integer power `n` is `r^n = (a^n)/(b^n)`. + +Exponentiation of a rational number `r = a/b` to a negative integer power `n` is `r^n = (b^m)/(a^m)`, where `m = |n|`. + +Exponentiation of a rational number `r = a/b` to a real (floating-point) number `x` is the quotient `(a^x)/(b^x)`, which is a real number. + +Exponentiation of a real number `x` to a rational number `r = a/b` is `x^(a/b) = root(x^a, b)`, where `root(p, q)` is the `q`th root of `p`. + +Implement the following operations: + - addition, subtraction, multiplication and division of two rational numbers, + - absolute value, exponentiation of a given rational number to an integer power, exponentiation of a given rational number to a real (floating-point) power, exponentiation of a real number to a rational number. + +Your implementation of rational numbers should always be reduced to lowest terms. For example, `4/4` should reduce to `1/1`, `30/60` should reduce to `1/2`, `12/8` should reduce to `3/2`, etc. To reduce a rational number `r = a/b`, divide `a` and `b` by the greatest common divisor (gcd) of `a` and `b`. So, for example, `gcd(12, 8) = 4`, so `r = 12/8` can be reduced to `(12/4)/(8/4) = 3/2`. + +Assume that the programming language you are using does not have an implementation of rational numbers. + +## Exception messages + +Sometimes it is necessary to raise an exception. When you do this, you should include a meaningful error message to +indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. Not +every exercise will require you to raise an exception, but for those that do, the tests will only pass if you include +a message. + +To raise a message with an exception, just write it as an argument to the exception type. For example, instead of +`raise Exception`, you shold write: + +```python +raise Exception("Meaningful message indicating the source of the error") +``` + +## Submitting Exercises + +Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/rational-numbers` directory. + +You can find your Exercism workspace by running `exercism debug` and looking for the line that starts with `Workspace`. + +For more detailed information about running tests, code style and linting, +please see the [help page](http://exercism.io/languages/python). + +## Source + +Wikipedia [https://en.wikipedia.org/wiki/Rational_number](https://en.wikipedia.org/wiki/Rational_number) + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. diff --git a/exercises/rational-numbers/example.py b/exercises/rational-numbers/example.py new file mode 100644 index 0000000000..d8b5f9cf5a --- /dev/null +++ b/exercises/rational-numbers/example.py @@ -0,0 +1,58 @@ +from __future__ import division + +from fractions import gcd + + +class Rational(object): + """ + Toyish implementation of rational numbers. For production purpose, + please use `fractions.Fraction` in standard library instead. + """ + def __init__(self, numer, denom): + self.numer, self.denom = self._reduce(numer, denom) + + def _reduce(self, numer, denom): + if numer == 0: + n, d = 0, 1 + else: + g = gcd(numer, denom) + n, d = int(numer/g), int(denom/g) + if n > 0 and d < 0: + n, d = -n, -d + return n, d + + def __eq__(self, other): + return self.numer == other.numer and self.denom == other.denom + + def __repr__(self): + return '{}/{}'.format(self.numer, self.denom) + + def __add__(self, other): + return Rational( + self.numer*other.denom + self.denom*other.numer, + self.denom*other.denom + ) + + def __sub__(self, other): + return Rational( + self.numer*other.denom - self.denom*other.numer, + self.denom*other.denom + ) + + def __mul__(self, other): + return Rational(self.numer * other.numer, self.denom * other.denom) + + def __truediv__(self, other): + return Rational(self.numer * other.denom, self.denom * other.numer) + + def __abs__(self): + if self.numer >= 0: + return self + else: + return Rational(-self.numer, self.denom) + + def __pow__(self, power): + return Rational(self.numer ** power, self.denom ** power) + + def __rpow__(self, base): + return base ** (self.numer / self.denom) diff --git a/exercises/rational-numbers/rational_numbers.py b/exercises/rational-numbers/rational_numbers.py new file mode 100644 index 0000000000..f94d20b477 --- /dev/null +++ b/exercises/rational-numbers/rational_numbers.py @@ -0,0 +1,34 @@ +from __future__ import division + + +class Rational(object): + def __init__(self, numer, denom): + self.numer = None + self.denom = None + + def __eq__(self, other): + return self.numer == other.numer and self.denom == other.denom + + def __repr__(self): + return '{}/{}'.format(self.numer, self.denom) + + def __add__(self, other): + pass + + def __sub__(self, other): + pass + + def __mul__(self, other): + pass + + def __truediv__(self, other): + pass + + def __abs__(self): + pass + + def __pow__(self, power): + pass + + def __rpow__(self, base): + pass diff --git a/exercises/rational-numbers/rational_numbers_test.py b/exercises/rational-numbers/rational_numbers_test.py new file mode 100644 index 0000000000..c0070f8d95 --- /dev/null +++ b/exercises/rational-numbers/rational_numbers_test.py @@ -0,0 +1,132 @@ +from __future__ import division + +import unittest + +from rational_numbers import Rational + + +# Tests adapted from `problem-specifications//canonical-data.json` @ v1.0.0 + +class RationalTests(unittest.TestCase): + + # Test addition + def test_add_two_positive(self): + self.assertEqual(Rational(1, 2) + Rational(2, 3), Rational(7, 6)) + + def test_add_positive_and_negative(self): + self.assertEqual(Rational(1, 2) + Rational(-2, 3), Rational(-1, 6)) + + def test_add_two_negative(self): + self.assertEqual(Rational(-1, 2) + Rational(-2, 3), Rational(-7, 6)) + + def test_add_opposite(self): + self.assertEqual(Rational(1, 2) + Rational(-1, 2), Rational(0, 1)) + + # Test subtraction + def test_subtract_two_positive(self): + self.assertEqual(Rational(1, 2) - Rational(2, 3), Rational(-1, 6)) + + def test_subtract_positive_and_negative(self): + self.assertEqual(Rational(1, 2) - Rational(-2, 3), Rational(7, 6)) + + def test_subtract_two_negative(self): + self.assertEqual(Rational(-1, 2) - Rational(-2, 3), Rational(1, 6)) + + def test_subtract_from_self(self): + self.assertEqual(Rational(1, 2) - Rational(1, 2), Rational(0, 1)) + + # Test multiplication + def test_multiply_two_positive(self): + self.assertEqual(Rational(1, 2) * Rational(2, 3), Rational(1, 3)) + + def test_multiply_negative_by_positive(self): + self.assertEqual(Rational(-1, 2) * Rational(2, 3), Rational(-1, 3)) + + def test_multiply_two_negative(self): + self.assertEqual(Rational(-1, 2) * Rational(-2, 3), Rational(1, 3)) + + def test_multiply_reciprocal(self): + self.assertEqual(Rational(1, 2) * Rational(2, 1), Rational(1, 1)) + + def test_multiply_by_one(self): + self.assertEqual(Rational(1, 2) * Rational(1, 1), Rational(1, 2)) + + def test_multiply_by_zero(self): + self.assertEqual(Rational(1, 2) * Rational(0, 1), Rational(0, 1)) + + # Test division + def test_divide_two_positive(self): + self.assertEqual(Rational(1, 2) / Rational(2, 3), Rational(3, 4)) + + def test_divide_positive_by_negative(self): + self.assertEqual(Rational(1, 2) / Rational(-2, 3), Rational(-3, 4)) + + def test_divide_two_negative(self): + self.assertEqual(Rational(-1, 2) / Rational(-2, 3), Rational(3, 4)) + + def test_divide_by_one(self): + self.assertEqual(Rational(1, 2) / Rational(1, 1), Rational(1, 2)) + + # Test absolute value + def test_absolute_value_of_positive(self): + self.assertEqual(abs(Rational(1, 2)), Rational(1, 2)) + + def test_absolute_value_of_negative(self): + self.assertEqual(abs(Rational(-1, 2)), Rational(1, 2)) + + def test_absolute_value_of_zero(self): + self.assertEqual(abs(Rational(0, 1)), Rational(0, 1)) + + # Test exponentiation of a rational number + def test_raise_a_positive_rational_to_a_positive_integer_power(self): + self.assertEqual(Rational(1, 2) ** 3, Rational(1, 8)) + + def test_raise_a_negative_rational_to_a_positive_integer_power(self): + self.assertEqual(Rational(-1, 2) ** 3, Rational(-1, 8)) + + def test_raise_zero_to_an_integer_power(self): + self.assertEqual(Rational(0, 1) ** 5, Rational(0, 1)) + + def test_raise_one_to_an_integer_power(self): + self.assertEqual(Rational(1, 1) ** 4, Rational(1, 1)) + + def test_raise_a_positive_rational_to_the_power_of_zero(self): + self.assertEqual(Rational(1, 2) ** 0, Rational(1, 1)) + + def test_raise_a_negative_rational_to_the_power_of_zero(self): + self.assertEqual(Rational(-1, 2) ** 0, Rational(1, 1)) + + # Test exponentiation of a real number to a rational number + def test_raise_a_real_number_to_a_positive_rational(self): + self.assertAlmostEqual(8 ** Rational(4, 3), 16.0, places=8) + + def test_raise_a_real_number_to_a_negative_rational(self): + self.assertAlmostEqual( + 9 ** Rational(-1, 2), 0.3333333333333333, places=8 + ) + + def test_raise_a_real_number_to_a_zero_rational(self): + self.assertAlmostEqual(2 ** Rational(0, 1), 1.0, places=8) + + # Test reduction to lowest terms + def test_reduce_positive(self): + self.assertEqual(Rational(2, 4), Rational(1, 2)) + + def test_reduce_negative(self): + self.assertEqual(Rational(-4, 6), Rational(-2, 3)) + + def test_reduce_rational_with_negative_denominator(self): + self.assertEqual(Rational(3, -9), Rational(-1, 3)) + + def test_reduce_zero(self): + self.assertEqual(Rational(0, 6), Rational(0, 1)) + + def test_reduce_integer(self): + self.assertEqual(Rational(-14, 7), Rational(-2, 1)) + + def test_reduce_one(self): + self.assertEqual(Rational(13, 13), Rational(1, 1)) + + +if __name__ == '__main__': + unittest.main() From 635b1b727c74ec732172656e337b76fec6ae116c Mon Sep 17 00:00:00 2001 From: mRcfps <1402491442@qq.com> Date: Wed, 21 Feb 2018 11:20:00 +0800 Subject: [PATCH 2/2] rational-numbers: fix topics in config.json --- config.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/config.json b/config.json index 65ed4f7e95..cbee3d09ab 100644 --- a/config.json +++ b/config.json @@ -951,9 +951,10 @@ "unlocked_by": null, "difficulty": 2, "topics": [ + "equality", "classes", - "arithmetic", - "operator_overloading" + "floating_point_numbers", + "mathematics" ] }, {