2017年10月アーカイブ

第9章練習問題13

| コメント(0)

 第9章は、クラスに関する。この部分は、C言語とC++言語との大きな違いでる。練習問題は、いかにもクラスの問題らしい練習問題13を選んだ。問題文は、以下の通り。

 有理数を表すクラスRationalを設計し、実装する。代入演算子、加算演算子、減算演算子、乗算演算子、除算演算子、等価演算子を提供する。また、doubule型への変換をサポートする。

 私の回答は、まずヘッダファイルRational.hは、下記の通り。問題文にはなかった、分母取り出し関数と分子取り出し関数、有理数を出力するための関数を追加した。

#include "..\..\std_lib_facilities.h"

class Rational {
public:
    class Invalid {};

    Rational(int denominator, int numerator);
    Rational();

    double real() const { return double(b) / double(a); }
    int den() const { return a; }
    int num() const { return b; }

private:
    int a, b;
};


Rational operator+(const Rational& x, Rational& y);
Rational operator-(const Rational& x, Rational& y);
Rational operator*(const Rational& x, Rational& y);
Rational operator/(const Rational& x, Rational& y);
bool operator==(const Rational& x, Rational& y);
ostream& operator<<(ostream& os, const Rational& x);

 これの実装の関数Rational.cppは下記の通り。正規化するための最大公約数関数gcm、正規化関数normalizeは、この実装で使うための関数である。

#include "Rational.h"


int gcm(int x, int y) {
    int r;

    x = abs(x);
    y = abs(y);

    if (x < y) swap(x, y);

    /* ユークリッドの互除法 */
    r = x % y;
    while (r != 0) {
        x = y;
        y = r;
        r = x % y;
    }

    return y;
}

void normalization(int& x, int& y) {

    int g{ 1 };

    if ((x!=0) && (y!=0))g = gcm(x, y);

    x = x / g;
    y = y / g;

    if (y == 0) x = 1;

    return;
}


Rational::Rational(int denominator, int numerator)
{
    if (denominator == 0) throw Invalid();

    normalization(denominator, numerator);

    a = denominator;
    b = numerator;

}

Rational::Rational()
    : a{ 1 }, b{ 1 }
{

}

Rational operator+(const Rational& x, Rational& y)
{
    int a, b;
    a = x.den() * y.den();
    b = x.num() * y.den() + y.num()*x.den();
    return { a,b };
}

Rational operator-(const Rational& x, Rational& y)
{
    int a, b;
    a = x.den() * y.den();
    b = x.num()*y.den() - y.num()*x.den();
    return{ a,b };
}

Rational operator*(const Rational& x, Rational& y)
{
    int a, b;
    a = x.den() * y.den();
    b = x.num() * y.num();
    return{ a,b };
}
Rational operator/(const Rational& x, Rational& y)
{
    int a, b;
    if (y.num() == 0) error("divided by zero ");
    a = x.den() * y.num();
    b = x.num() * y.den();
    return{ a,b };
}

bool operator==(const Rational& x, Rational& y)
{
    if (x.den() != y.den()) return false;
    if (x.num() != y.num()) return false;
    return true;
}

ostream& operator<<(ostream& os, const Rational& x)
{
    return os << x.num() << "/" << x.den() << endl;
}


 

最後にテストのメイン関数

#include "Rational.h"
int main() {
    try {
        Rational x, y;

        cout << x;   //デフォルトは1/1

        x = Rational{ 5,4 }; // 4/5
        cout << x;
        x = Rational{ 10,8 };  //これも4/5 正規化ができているか?
        cout << x; 

        y = Rational{ 5,4 }; // 4/5
        cout << x + y;
        cout << x - y;
        cout << x * y;
        cout << x / y;
        cout << (x == y) << endl;
        cout << x.real() << endl;


        return 0;
    }

    catch (Rational::Invalid) {
        cerr << "Denominator = 0" << endl;
        return 1;
    }
    catch (exception& e) {
        cerr << "exception: " << e.what() << endl;
        char c;
        while (cin >> c&& c != ';');
        return 2;
    }
}

 

 この問題は、結構勉強になった。模範解答http://www.stroustrup.com/Programming/Solutions/Ch9/e9-13.cppでは、有理数がマイナスの時に、分母ではなく分子をマイナスにするようにしている。確かにこちらの方が良さそうだ。
 私の回答では、符号を反転させるーとか、等価演算の!=とかを忘れているのも問題だ。一方、除算の時には、ゼロの除算でエラーを出すようにしているのは、私の回答例の方がいいかもしれない。
 分母と分子を、私の回答では隠蔽するようになっている。これは、第9章のDateクラスに倣ったからだ。でも、解答例のように、あっさりPublicに置く方がいいかもしれない。これは、どうなんだろう?有理数の使い方にもよるのだろうけど、1組で数値を表すということを考えると、やっぱり隠蔽して、分母、分子を直接さわれない方がいいように思う。

第8章練習問題2~3

| コメント(0)

 第8章は、C++言語の記法である。関数の参照渡しのconstとか、参考になる。
 練習問題2~3を一度に解く。練習問題は、下記の通り。

 int型のvectorをcoutに出力する関数printを記述する。この関数は引数として、出力に「ラベルを付ける」ための文字列とvectorの2つを受け取る。
 フィビナッチ数列のvectorを作成し、printで出力する。vectorを作成するための関数としてfibonacci(x,y,v,n)を記述する。この場合、整数xとyはint型、vは空のvector<int>、nはvに設定する要素の個数である。

 printは、本文で説明していたvectorの参照渡しで内容を変更しないconstで渡す。逆にfibonaccは、vを参照渡しとして、中身を変更するということだろう。ということで、私の回答は、下記の通り。

#include "..\..\std_lib_facilities.h"

void print(string label,const vector<int>& v)
{
    for (int i = 0; i < v.size(); i++) {
        cout << label << "[" << i << "]=" << v[i] << "\n";
    }
    return;
}

void fibonacci(int x, int y, vector<int>& v, int n)
{
    if ((v.size()<n)||(n < 2)) return;

    v[0] = x;
    v[1] = y;

    for (int i = 2; i < n; i++) {
        v[i] = v[i - 2] + v[i - 1];
    }

    return;
}

int main() {
    const int N{ 10 }; /* Nはフィボナッチ数列の数を指定する定数 */
    vector<int> v(N);
    fibonacci(1, 2, v, N);
    print("fibonacci", v);
    return 0;
}

 

 回答例http://www.stroustrup.com/Programming/Solutions/Ch8/e8-2.cppは、pushbackを使って値を入れていくということにしている。問題文のvは空のvector<int>という指定に従うなら、確かにそうすべきだった。

このアーカイブについて

このページには、2017年10月に書かれた記事が新しい順に公開されています。

前のアーカイブは2017年9月です。

次のアーカイブは2017年11月です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

ウェブページ

  • about
Powered by Movable Type 6.3.6