C#(Unity)で再帰のreturnについて

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
西野

C#(Unity)で再帰のreturnについて

#1

投稿記事 by 西野 » 8年前

C#(Unity)で再帰の勉強をしております。
C++での挙動と微妙に違う気がして、詳しい方のアドバイスを頂きたいです。

2次元配列から指定した方向に最短でパラメータをセットされている座標を取得するプログラムを書きたいです。

■配列例 stageMapDatas

1,2,2,2,2
0,0,0,0,2
0,3,0,0,2
2,0,0,0,0
2,2,2,2,2

(0,0)を基準点として下方向に検索する場合、(0,3)の値を取得
(0,0)を基準点として右方向に検索する場合、(1,0)の値を取得

それっぽい処理が実装できたのですが、C#で再起処理を行うと完了後にreturnで抜けた後の部分まで処理をしてしまっているようでして、この辺の回避方法がわかりません。

CheckCellを1回しか呼ばなくても、再帰した回数分Debug.Logが呼ばれてしまいます。
こちらを一番最初にreturnした時点で止める方法はありませんでしょうか?

コード:


public class CELL {
	public int pram;
	public int x;
	public int y;
};

CELL CheckCell(int x, int y, DIR dir){

	// Cellの初期化
	CELL cell = new CELL();
	cell.x = -1;
	cell.y = -1;

	//Debug.Log ("cc");
	// 範囲外
	if(x < 0 || x >= stageMapDatas[0].Count){
		cell.x = x;
		cell.y = y;
		Debug.Log ("step 1 " + cell.x + "," + cell.y);
		return cell;
	}
	if(y < 0 || y >= stageMapDatas.Count){
		cell.x = x;
		cell.y = y;
		Debug.Log ("step 2 " + cell.x + "," + cell.y);
		return cell;
	}

	// 判定
	if(stageMapDatas[y][x] > 0){
		cell.x = x;
		cell.y = y;
		Debug.Log ("step 3 " + x + "," + y);
		return cell;
	}else{
		
		int xx = x;
		int yy = y;
		switch (dir) {

		case DIR.DIR_LEFT:
			xx -= 1;
			break;

		case DIR.DIR_RIGHT:
			xx += 1;
			break;

		case DIR.DIR_UP:
			yy -= 1;
			break;

		case DIR.DIR_DOWN:
			yy += 1;
			break;

		}

		CheckCell(xx, yy, dir);
		Debug.Log ("step 4 " + cell.x + "," + cell.y);
		return cell; // ここが再帰回数分呼ばれてしまいます。
	}

	Debug.Log ("step 5 " + cell.x + "," + cell.y);
	return cell;

}

~略~

// メインの判定部分 cellには検索の基準点を保存

if (Mathf.Abs (diffX) > Mathf.Abs (diffY)) {
	if (diffX > 0) {
		check = CheckCell (cell.x+1, cell.y, DIR.DIR_RIGHT);
		Debug.Log ("→");
	} else {
		check = CheckCell (cell.x-1, cell.y, DIR.DIR_LEFT);
		Debug.Log ("←");
	}
} else {
	if (diffY > 0) {
		check = CheckCell (cell.x, cell.y-1, DIR.DIR_UP);
		Debug.Log ("↑");
	} else {
		check = CheckCell (cell.x, cell.y+1, DIR.DIR_DOWN);
		Debug.Log ("↓");
	}
}

Debug.Log (check.x + "," + check.y);

// 出力例
step 3 0,3
step 4 -1,-1
step 4 -1,-1

かずま

Re: C#(Unity)で再帰のreturnについて

#2

投稿記事 by かずま » 8年前

コード:

    stageMapDatas[0].Count
    stageMapDatas.Count
    stageMapDatas[y][x]
こんなに違った使い方があるとは驚きです。
stageMapDates はどのように宣言されているのですか?

かずま

Re: C#(Unity)で再帰のreturnについて

#3

投稿記事 by かずま » 8年前

かずま さんが書きました:こんなに違った使い方があるとは驚きです。
stageMapDates はどのように宣言されているのですか?
すみません。早とちりでした。
C# には疎いので、C++ のつもりで書いてしまいました。
よく考えたら、.Count は、Java の配列の .length と同じなんですね。

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: C#(Unity)で再帰のreturnについて

#4

投稿記事 by みけCAT » 8年前

西野 さんが書きました: それっぽい処理が実装できたのですが、C#で再起処理を行うと完了後にreturnで抜けた後の部分まで処理をしてしまっているようでして、この辺の回避方法がわかりません。
どのようにして「returnで抜けた後の部分まで処理をしてしまっているよう」と考えたのですか?
西野 さんが書きました:

コード:

		CheckCell(xx, yy, dir);
		Debug.Log ("step 4 " + cell.x + "," + cell.y);
		return cell; // ここが再帰回数分呼ばれてしまいます。
	}

	Debug.Log ("step 5 " + cell.x + "," + cell.y);
	return cell;

}
「ここ」とはどこでしょうか?
step 4のDebug.Logは明らかにreturnの前にありますが、step 5のDebug.Logが複数回呼ばれるということでしょうか?
西野 さんが書きました: C++での挙動と微妙に違う気がして
違う気がしただけですか?
C++でこれに相当するプログラムを実装し、本当に違うか試してみることをおすすめします。
西野 さんが書きました: CheckCellを1回しか呼ばなくても、再帰した回数分Debug.Logが呼ばれてしまいます。
再帰なので、内部でCheckCellが呼ばれているはずです。
西野 さんが書きました: こちらを一番最初にreturnした時点で止める方法はありませんでしょうか?
一番最初にreturnする時点(step 1~step 3)のみにDebug.Logを書くといいと思います。
オフトピック
Schemeで継続を使うみたいな大域脱出がしたい、ということだろうか…?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

西野

Re: C#(Unity)で再帰のreturnについて

#5

投稿記事 by 西野 » 8年前

>> かずま様

とんでもないです!勉強がてら制作しているので怪しい処理がかなりあるかと思います。
わざわざご確認ありがとうございました。

>> みけCAT様

どのようにして「returnで抜けた後の部分まで処理をしてしまっているよう」と考えたのですか?

こちら1回しかCheckCellを呼んでいなくても、Debug.Logが3回呼ばれてしまいます。

Debug.Log ("step 4 " + cell.x + "," + cell.y);
の下のreturnはC#で何かしら返さないとエラーが出てしまうので書いているおまじないになります。
そもそも、Debug.Log ("step 5 " + cell.x + "," + cell.y);にくることはないので、検証用に書いております。

C++だと近いソースが正常に動いているので、C#だと違うのかなーと・・・。

>>Schemeで継続を使うみたいな大域脱出がしたい、ということだろうか…?
こちらが理解できませんでした。説明が下手で申し訳ないです。。。

基準点から、指定した方向の最短で値がある位置を検索したい処理になります。

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: C#(Unity)で再帰のreturnについて

#6

投稿記事 by みけCAT » 8年前

西野 さんが書きました:C++だと近いソースが正常に動いているので、C#だと違うのかなーと・・・。
比較してみたいので、そのC++のコードを提示していただけますか?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

かずま

Re: C#(Unity)で再帰のreturnについて

#7

投稿記事 by かずま » 8年前

C++ に書き直してみました。

コード:

#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <cstdlib>
using namespace std;

class CELL {
public:
    int pram;
    int x;
    int y;
};

enum DIR { DIR_LEFT, DIR_RIGHT, DIR_UP, DIR_DOWN };

vector<vector<int>> stageMapDatas = {
    { 1, 2, 2, 2, 2 },
    { 0, 0, 0, 0, 2 },
    { 0, 3, 0, 0, 2 },
    { 2, 0, 0, 0, 0 },
    { 2, 2, 2, 2, 2 },
};

struct DEBUG {
    void Log(const char *s1, int x, const char *s2, int y, const char *s3, DIR dir) {
        cout << s1 << x << s2 << y << s3 << dir << endl;
    }
    void Log(const char *s1, int x, const char *s2, int y) {
        cout << s1 << x << s2 << y << endl;
    }
    void Log(const char *s) {
        cout << s << endl;
    }
} Debug;
 
CELL CheckCell(int x, int y, DIR dir)
{
    // Cellの初期化
    CELL& cell = *(new CELL());
    cell.x = -1;
    cell.y = -1;
 
    Debug.Log ("cc ", x, ", ", y, ": ", dir);
    // 範囲外
    if(x < 0 || x >= stageMapDatas[0].size()){
        cell.x = x;
        cell.y = y;
        Debug.Log ("step 1 ", cell.x, ",", cell.y);
        return cell;
    }
    if(y < 0 || y >= stageMapDatas.size()){
        cell.x = x;
        cell.y = y;
        Debug.Log ("step 2 ", cell.x, ",", cell.y);
        return cell;
    }
 
    // 判定
    if(stageMapDatas[y][x] > 0){
        cell.x = x;
        cell.y = y;
        Debug.Log ("step 3 ", x, ",", y);
        return cell;
    }else{
        
        int xx = x;
        int yy = y;
        switch (dir) {
 
        case DIR::DIR_LEFT:
            xx -= 1;
            break;
 
        case DIR::DIR_RIGHT:
            xx += 1;
            break;
 
        case DIR::DIR_UP:
            yy -= 1;
            break;
 
        case DIR::DIR_DOWN:
            yy += 1;
            break;
 
        }
 
        CheckCell(xx, yy, dir);
        Debug.Log ("step 4 ", cell.x, ",", cell.y);
        return cell; // ここが再帰回数分呼ばれてしまいます。
    }
 
    Debug.Log ("step 5 ", cell.x, ",", cell.y);
    return cell;
 
}
 
// メインの判定部分 cellには検索の基準点を保存
 
int main()
{
    int diffX = 0, diffY = -1;
    CELL cell = { 0 };
    CELL check;
    if (abs(diffX) > abs(diffY)) {
        if (diffX > 0) {
            check = CheckCell (cell.x+1, cell.y, DIR::DIR_RIGHT);
            Debug.Log ("→");
        } else {
            check = CheckCell (cell.x-1, cell.y, DIR::DIR_LEFT);
            Debug.Log ("←");
        }
    } else {
        if (diffY > 0) {
            check = CheckCell (cell.x, cell.y-1, DIR::DIR_UP);
            Debug.Log ("↑");
        } else {
            check = CheckCell (cell.x, cell.y+1, DIR::DIR_DOWN);
            Debug.Log ("↓");
        }
    }
     
    Debug.Log ("", check.x, ",", check.y);
}
実行結果

コード:

cc 0, 1: 3
cc 0, 2: 3
cc 0, 3: 3
step 3 0,3
step 4 -1,-1
step 4 -1,-1
↓
-1,-1
書いたとおりに動いています。

西野

Re: C#(Unity)で再帰のreturnについて

#8

投稿記事 by 西野 » 8年前

かずま様

わざわざ書き直し助かりました。
実際に同じもので確認できておらず申し訳ございません。。。

1回目のstep 3 0,3だけ取得したいのですが、こちら無理なのでしょうか。

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: C#(Unity)で再帰のreturnについて

#9

投稿記事 by みけCAT » 8年前

もしかしたら、元のコードの60行目

コード:

		CheckCell(xx, yy, dir);

コード:

		return CheckCell(xx, yy, dir);
にすると、求めるものになるかもしれません。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

返信

“C言語何でも質問掲示板” へ戻る