質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.51%

  • C++

    3468questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

行列クラスにおいて、行・列ごとの参照を返す関数を実装したい

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 741

dashi8

score 3

実現したいこと

c++の勉強のため、行列クラスを作っています。行列クラスのオブジェクトに行ごと(列ごと)にアクセスするため、大きさ1×n (m×1)の部分行列の参照を返すメンバ関数を作りたいです。しかし、実装方法が全く分かりません・・・。何かいい方法はあるでしょうか?ご教示お願いします。

ソースコード

Matrix.h

#ifndef _Matrix_H_
#define _Matrix_H_

#include <bits/stdc++.h>
using namespace std;

class Matrix {
public:
    Matrix(int r, int c);//コンストラクタで行列の大きさを決定
    Matrix();
    Matrix(const Matrix& A);

    vector<vector<double> > ele; //行列の要素はvector配列で管理

    void set(double first, ...); //要素をセット
    void show();                 //行列を標準出力に表示
    int rownumber() const;       //行数を返す
    int columnumber() const;     //列数を返す


    //今回実装したい関数
    Matrix& row(int n);          //引数の行の参照を返す
    Matrix& colum(int n);        //引数の列の参照を返す



  //演算子オーバーロード
    //添え字演算子
    vector<double>& operator[](int n);

    //代入演算子
    Matrix& operator=(const Matrix& A);

    //二項演算子
    Matrix operator+(const Matrix& A) const;
    Matrix operator-(const Matrix& A) const;
    Matrix operator*(const Matrix& A) const;

    //単項演算子
    Matrix operator+() const;
    Matrix operator-() const;
    Matrix& operator+=(const Matrix& A);
    Matrix& operator-=(const Matrix& A);
    Matrix& operator*=(const Matrix& A);
    Matrix& operator*=(double k);
    Matrix& operator/=(double k);

    //比較演算子
    bool operator==(const Matrix& A) const;
    bool operator!=(const Matrix& A) const;


private:
    int _row;      //行数
    int _colum;    //列数
};

//二項演算子
Matrix operator*(double k, const Matrix& A);
Matrix operator*(const Matrix& A, double k);
Matrix operator/(const Matrix& A, double k);



#endif

Matrix.cpp

#include "Matrix.h"

Matrix::Matrix(int r, int c){
    _row = r, _colum = c;
    ele.resize(_row);
    for(int i = 0; i < _row; i++) {
        ele[i].resize(_colum);
    }
    for(int i = 0; i < _row; i++) {
        for(int j = 0; j < _colum; j++) {
            ele[i][j] = 0;//0で初期化
        }
    }
}

Matrix::Matrix(){
    _row = 1, _colum = 1;
    ele.resize(1);
    ele[0].resize(1);
    ele[0][0] = 0;
}

Matrix::Matrix(const Matrix& A){
    _row = A.rownumber(), _colum = A.columnumber();
    ele.resize(_row);
    for(int i = 0; i < _row; i++) {
        ele[i].resize(_colum);
    }
    for(int i = 0; i < _row; i++) {
        for(int j = 0; j < _colum; j++) {
            ele[i][j] = A.ele[i][j];
        }
    }
}


void Matrix::set(double first, ...){
    double element = first;
    va_list args;
    va_start(args, first);
    for(int i = 0; i < _row; i++) {
        for(int j = 0; j < _colum; j++) {
            ele[i][j] = element;
            element = va_arg(args, double);
        }
    }
    va_end(args);
}

void Matrix::show(){
    for(int i = 0; i < _row; i++) {
        for(int j = 0; j < _colum; j++) {
            printf("%lf ", ele[i][j]);
        }
        printf("\n");
    }
    printf("\n");
}

int Matrix::rownumber() const {
    return _row;
}

int Matrix::columnumber() const {
    return _colum;
}


//今回実装したい関数
Matrix& Matrix::row(int n){

//参照を返せるようにしたい

}

//今回実装したい関数
Matrix& Matrix::colum(int n){

//参照を返せるようにしたい

}


//演算子オーバーロード
vector<double>& Matrix::operator[](int n) {
    return ele[n];
}

Matrix& Matrix::operator=(const Matrix& A){
    this->_row = A.rownumber();
    this->_colum = A.columnumber();
    this->ele.resize(_row);
    for(int i = 0; i < _row; i++) {
        this->ele[i].resize(_colum);
    }

    for(int i = 0; i < _row; i++) {
        for(int j = 0; j < _colum; j++) {
            this->ele[i][j] = A.ele[i][j];
        }
    }
    return *this;
}


//以下省略

実現したい動作
main.cpp

#include <bits/stdc++.h>
#include "Matrix.h"
using namespace std;

int main(){
    Matrix M(3, 3);
    M.set(1.0, 2.0, 3.0,
          4.0, 5.0, 6.0,
          7.0, 8.0, 9.0);
  M.show()

    Matrix R(1, 3);
    R.set(1.0, 1.0, 1.0);
    M.row(0) = R;
    M.show();
    //列の方も同様
}

/*
標準出力に次のように出力
1.0 2.0 3.0
4.0 5.0 6.0
7.0 8.0 9.0

1.0 1.0 1.0
4.0 5.0 6.0
7.0 8.0 9.0

*/

試したこと

Matrix&型ではなく、vector<double>&型を返す関数なら、行に関してのみ実装できました。列に関してはこっちもできていません。

vector<double>& Matrix::row(int n){
    return ele[n];
}


これは簡単ですね、、、。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

こんにちは。

事実上無理な気がします。

取り出したい「Matrixの参照」に対する「Matrixの実体」が必要です。しかし、取り出したいものは元のMatrixと形状が異なりますから、元のMatrixでは「実体」になりません。従って、row()やcol()のMatrixを取り出す度にその実体を生成する必要があり、それをいつ破棄するのか管理が難しすぎると思います。

素直にMatrixの実体を返却した方がまだ希望はあると思いますが、それでも難易度高そうです。
そのMatrix実体が元のMatrixが管理する配列へのポインタを保持し、かつ、外部に対して希望の形状のように見えるようI/Fする事になります。
その際「元のMatrixインスタンスから取り出したMatrixインスタンス」がアクセスする配列の所有権をどうするのか決めないとハマります。
元のMatrixインスタンスが管理する、もしくは、std::shared_ptrを使って共有管理するのどちらかだろうと思います。

M.row(0)が返却するものをSubMatrixのような別クラスのインスタンスにしておいた方が現実的なように感じます。それは元のMatrixへの参照と参照先の列番号や行番号を保持しておき、const Matrix&を受け取る代入演算子などを実装すれば、概ねやりたいことをできる筈です。
こちらならdoubleを返却するoperator[]を実装できるので自然ですし。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/10/19 00:12 編集

    回答ありがとうございます。

    >>いつ破棄するのか管理が難しすぎる
    確かにその通りだと思いました。また、「元の配列が持つ配列へのポインタを保持するMatrixインスタンス」を作る方も難しいのですね。


    別クラスのインスタンスを返却するというのは盲点でした。vector<vector<double>>*で元の配列へのポインタを保持するSubMatrixクラスをMatrixクラスに合成(MatrixがSubMatrixを包含)することで、下のような動作を実現することができました:main.cpp
    Matrix A(3, 3);
    Matrix B(3, 3);
    //それぞれ適当に要素をセット
    A.colum(0) = B.colum(0);

    これでやりたいことは概ねできました。ありがとうございます。


    しかしSubMatrixクラスに関し、一つ分からないことがあります。
    次のような動作を実装したいのですが:main.cpp
    Matrix A(1, 3);
    Matrix B(3, 3); //それぞれ適当に要素をセット
    M.row(0) = A; //この代入演算子を定義したい

    SubMatrixオブジェクトにMatrixオブジェクトを代入する演算は、二つのクラスの関係から定義できないように思います。上と同様の動作は
    M.row(0) = A.row(0);
    で実現できるのですが、演算子を定義しMatrixを直接代入することはできるのでしょうか。よろしければご教示ください。

    キャンセル

  • 2017/10/19 01:35

    SubMatrix& operator=(const Matrix& rhs);を定義すれば、できる筈と思います。1要素づつコピーすればできる筈ですよ。
    もちろんrhsの構造がSubMatrixの構造と一致していることを確認し、異なっていたら例外を投げる等の対処も必要です。std::vectorをアクセスする際に、operator[]ではなく、at()を使えば範囲外ならエラーになるので簡単かも。

    キャンセル

  • 2017/10/19 14:47

    クラスの相互参照の方法を知らなかったためエラーが出ていたようです。MatrixとSubMatridで相互参照することで SubMatrix& operator=(const Matrix& rhs); を実装することができました。

    >>at()を使えば範囲外ならエラーになるので簡単かも
    アドバイスありがとうございます。operator[]では境界チェックがめんどうだったのでat()を使おうと思います。


    とても勉強になりました。ありがとうございました!

    キャンセル

+1

std::valarray<double> を sliceする方が万倍楽ちゃいますか?

[追記]
columnを返すのやってみた:

#include <iostream>
#include <algorithm>

template<typename T>
class column {
public:
  column(T* data, size_t cols) : data_(data), cols_(cols) {}
  T& operator[](size_t row) { return data_[cols_*row]; }
private:
  T* data_;
  size_t cols_;
};

template<typename T>
class matrix {
public:
  matrix(size_t rows, size_t cols) : cols_(cols), rows_(rows) {
    data_ = new T[rows*cols];
  }
  ~matrix() { delete[] data_; }
  void fill(const T& val) { std::fill_n(data_, rows_*cols_, val); }
  column<T> operator()(size_t col) { return column<T>(data_+col, cols_); }
  T* operator[](size_t row) { return data_+cols_*row; }
private:
  T* data_;
  size_t cols_;
  size_t rows_;
};

// おためし
int main() {
  using namespace std;

  matrix<double> mtx(2,3);
  mtx.fill(0.0);
  column<double> clm = mtx(1);
  clm[0] = 1.23;
  clm[1] = 2.34;

  for ( size_t r = 0; r < 2; ++r ) {
    for ( size_t c = 0; c < 3; ++c ) {
      cout << mtx[r][c] << ' ';
    }
    cout << endl;
  }
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/10/17 23:15

    回答ありがとうございます。std::valarrayクラスは初めて知りました。
    確かに演算子オーバーロードの実装も含めとても楽で、sliceにより取り出したい行(列)の要素への参照を得ることはできました。しかし、これらを参照するMatrixオブジェクトを作ることができません・・・。良い方法はあるでしょうか?

    キャンセル

  • 2017/10/18 09:06

    あー...
    ならば double mtx[R][C] を用意し、第n列なら &mtx[0][n] をひとつだけ返せばいいんじゃないかしら。そこからRずつ進めた先が mtx[1][n], mtx[2][n]... なんだから。
    んで、Rずつ進めた先を勝手に参照してくれる class column を実装すれば:

    matrix m(R,C);
    column c = m.column(n); // n列を返す
    c[3] = 1.23; // みたいな。

    キャンセル

  • 2017/10/19 00:40

    確かに別のクラスcolumnを介せば所望する参照を得られますね。
    ありがとうございます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.51%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • C++

    3468questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。