icoke
@icoke

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

分岐の書き方

Discussion

Closed

解決したいこと

皆さんの意見を知りたいです

自分はif文を書くとき、無駄なインデントを嫌って ! と continue; break; return; を多用した文を書きます。
例えばナンプレのプログラムを作るときに、下記のように縦横のラインと3かけ3のブロックで同じ数字がないか走査する関数を作ったとして

private bool horizontalScan ( int x, int y )
private bool verticalScan( int x, int y )
private bool blockScan( int x, int y )
// 同じ数字が見つかればfalseを返す

この場合、同じ数字が見つからなかったら次の処理をを行う操作は

if ( horizontalScan(x, y) ) {
    if ( verticalScan(x, y) ) {
        if ( blockScan(x, y) ) {
            // 次の処理
        }
    }
}

とするより

if ( !horizontalScan(x, y) ) { continue; // または return; など }
if ( !verticalScan(x, y) ) { continue; // または return; など }
if ( !blockScan(x, y) ) { continue; // または return; など }
// 次の処理

にしたほうがスマートだと感じています。

まぁそもそも

private bool scan ( int x, int y ) {
    if ( horizontalScan(x, y) ) {
        if ( verticalScan(x, y) ) {
            if ( blockScan(x, y) ) {
                return true;
            }
        }
    }
    return false;
}

public void Func ( int x, int y ) {
    if ( scan(x, y) ) {
        // 次の処理
    }
}

と関数にしてしまえばいい話ですが

上の例は極端な例なので場合によるとは思います。

ただ自分はループ以外であまりインデントを使うと可読性が失われると思っているのでこういう書き方をよくしてます。

皆さんはどう思いますか?
また、入れ子にしたほうが良いと思う意見があれば聞きたいです。勉強中の身なので

1

私は「一度に把握すべき情報は少ないほう良い」と考えているので次のような方針で書いています。

  • ネストが深いよりは、浅いほうが良い
  • 先まで読まないと結果がわからない状態より、早期リターンですぐに結果がわかるほうが良い

入れ子にしたほうが良い具体的なケースは思いつかなかったのですが、例えばロジックの元になっている情報(仕様やマニュアル等)が入れ子のような形で表現されている場合はそれに合わせた表現が適切かもしれません。元になっている情報と表現を揃えることで対応付けを行う、という考えです。

また、これらは可読性を重視した考え方なので、パフォーマンスなどの他の要素を優先する場合にあえて逆の方針を選ぶことはあると思います。

1Like

自分も深いネストは嫌います。
なお、if文がネストしていて、それぞれのelseがないならば、それは単にand条件なので、&&でつなぎます。

処理の分岐でなく、設定値の振り分けなら、三項演算子やKotlinならif式/when式、Swiftならif式/switch式を使います。

2Like

各言語によって「コーディング規約」となる基準がありますから基本はそれに従うのも良いでしょう。しかし可読性が下がるなど様々な理由があるなら無視してあえて自分のスタイルで書くことはあります。
言語によっては「コーディング規約」にとりつかれすぎるのも良くないと書かれていたりします。

例えばPythonは的を得ていると思います。
https://pep8-ja.readthedocs.io/ja/latest/#id3

スマートだと感じています。

「スマート」の解釈が私と違います。

コードをパッと見て分かりやすく、かつコード量が少なくできるものを「スマート」と呼んでいます。
言語によってはcontinueが使えなかったり、ブロック内のコードが増えてしまうならまだ私は入れ子(ネスト)が深い方を選びます(ただ深すぎるネストを良いとは思っていません)。

もちろんif (exp) { }のように一行で書くやり方もします。
しかしこの場合、ブロック内のコードが意味のある物に限ります。
例えば変数を代入{ a = 1; }するなど。
continuereturnだけの為には使いません。
ブロック内のコードとしてあまり意味を成さず、入れ子にすればわざわざcontinueを書かずそれだけで一つ遠回りなコードが生成される訳なのでその点がスマートでは無いと感じています。

解釈の違いは当然ありますから、参考までに。

1Like

フローを人に説明するのだと考えたときに,以下のどちらの言い方をした方がわかりやすいと思われるか? みたいな点で決めれば良いと思う.

質問文内の例だと
早期に return する書き方のイメージは,

  1. !horizontalScan(x,y) であれば false
  2. !VerticalScan(x,y) であれば false
  3. !blockScan(x,y) であれば false
  4. それ以外であれば true

みたいな.
対して,3重の if を用いた書き方は

  1. horizontalScan(x,y) であって,且つ VerticalScan(x,y) であって,且つblockScan(x,y) である場合には true
  2. そうでなければ false

みたいな.

言うほどの差が無いと思うのであれば,「ネストが深いと単純に見辛い」みたいな別の要素が判断基準として強くなる…かな.

1Like

質問のコードを見ると、

横、縦、ブロックを順にチェックしていって、いずれかに同じ数字がある場合は「次の処理」は行わない

というのがやりたいことで、その条件をチェックするのに if 文のネストを深くしたくないということと理解してます。

その理解で正しければ、以下のようにしてはいかがですか?

if (horizontalScan(x, y) && // 横に同じ数字があれば false
    verticalScan(x, y) &&   // 縦に同じ数字があれば false
    blockScan(x, y) )       // ブロックに同じ数字があれば false
    {
        // 次の処理
    }
}

if 文の ( ) 内の一番左の horizontalScan から一番右の blockScan まで順に評価されていきますが、&& でつないでいるので途中でどれかが false になればそこで評価は終了して、if 文の ( ) 内は false になります (短絡評価)。

条件付き論理 AND 演算子 &&
https://learn.microsoft.com/ja-jp/dotnet/csharp/language-reference/operators/boolean-logical-operators#conditional-logical-and-operator-

質問の 3 つ目のコードブロックの continue とか return を使う方法は自分的にはスマートとは思えません。他の方がコメントされてますが、バグのもとにもなりそうですし。

さらに言わせてもらえると、メソッドの名前も

// 同じ数字が見つかればfalseを返す

が、メソッド名を見ただけで分かるように考え直した方が良さそうに思います。

1Like

Your answer might help someone💌