PHPしか知らない僕がPythonを少し触ってみたよ 〜七色の路をループせずにブレイクする〜

この記事はだいぶ前に書かれたものなので情報が古いかもしれません
今期のアニメの中では一番好きな曲かも……ナナイロード

この記事を三行にまとめると

多重ループを一気に抜けることができない
Pythonはfor文やwhile文にもelseがある
gotoモジュールを使うという手もある
今日はループ処理の中などで時々利用するbreakやcontinueについて。

基本はPHPもPythonも一緒なのですが、多重ループになった時にはPHPと同じように書けないので、今回はそれを見ていきましょう。



continueやbreakについて

PHPでは「break 2」とかで多重ループを一気に抜けることができますが、Pythonにはそういう機能はないようです。

じゃあどーすればいいか。やり方はいくつかあるようですが、ここでは「else」を使うやり方を試してみたいと思います。

まずは九九の式と答えをだーっと表示するシンプルなコードをば。

for i in range(1, 10):
  line = []

  for j in range(1, 10):
    line.append(str(i * j))
        
  print(' '.join(line))

すごいざっくりですけど、とりあえずこんな感じでオッケーでしょう。

ではここで、全部出力するのではなく、5の段まで出力するようにコードを書き換えてみます。

for i in range(1, 10):
  line = []

  for j in range(1, 10):
    line.append(str(i * j))

    if i > 5 :
      break

  else:
    print(' '.join(line))

Pythonはfor文やwhile文にもelseがあります。elseの中身はbreakによってループを抜けなかった場合に実行されます。今回だと「iが5以下」の場合はbreakしないので、elseの中身が実行されるって感じです。

実際の動きとしてはbreakした瞬間に全てのループ処理が終わっているわけではなく、iが6以上であっても出力されないだけで処理自体は続いてます。つまり「i > 5」のところを「i == 6」とした場合、PHPの「break 2」なら5の段までの出力になりますが、Pythonの場合は6の段だけが出力されないみたいな処理になってしまいます。

//PHP
for($i = 1; $i < 10; $i++) {
  $line = array();

  for($j = 1; $j < 10; $j++) {
    $line[] = $i * $j;

    if($i == 6) {
      break 2;
    }
  }

  echo implode(' ', $line).'';
}

//出力結果(5の段で終了)
1 2 3 4 5 6 7 8 9
2 4 6 8 10 12 14 16 18
3 6 9 12 15 18 21 24 27
4 8 12 16 20 24 28 32 36
5 10 15 20 25 30 35 40 45

#Python
for i in range(1, 10):
  line = []

  for j in range(1, 10):
    line.append(str(i * j))

    if i == 6 :
      break

  else:
    print(' '.join(line))

#出力結果(6の段だけない)
1 2 3 4 5 6 7 8 9 
2 4 6 8 10 12 14 16 18 
3 6 9 12 15 18 21 24 27 
4 8 12 16 20 24 28 32 36 
5 10 15 20 25 30 35 40 45 
7 14 21 28 35 42 49 56 63 
8 16 24 32 40 48 56 64 72 
9 18 27 36 45 54 63 72 81

Pythonのforとelseの組み合わせた場合のbreakは、PHPで言えば「continue 2」の動きの方が近いかもしれませんね。

//continueを使った場合
for($i = 1; $i < 10; $i++) {
  $line = array();

  for($j = 1; $j < 10; $j++) {
    $line[] = $i * $j;

    if($i > 5) {
      continue 2;
    }
  }

  echo implode(' ', $line).'';
}

この場合も出力されるのは5の段までですが、ループを抜け出したわけではないので$iが6以上になっても処理は続いています。



gotoを使う

多重ループを抜けるもう一つのやり方として「goto」というのがあります。

gotoというのは、簡単に言えば指定した場所までジャンプするみたいな機能です。厳密に言えばループを抜ける機能ではなく、コードを指定のところまですっ飛ばす、あるいは戻すみたいな機能です。

実際に見た方が早いですかね。まずはPHPでgotoを使ってみます。

//指定の場所まですっ飛ばす
echo 'あいうえお';
echo 'かきくけこ';
goto last;
echo 'さしすせそ';
echo 'たちつてと';
echo 'なにぬねの';
last:
echo 'わをん';

//出力結果
あいうえおかきくけこわをん

//指定の場所まで戻る
repeat:
echo 'あいうえお';
echo 'かきくけこ';
goto repeat;
echo 'さしすせそ';
echo 'たちつてと';
echo 'なにぬねの';
echo 'わをん';

//出力結果
あいうえおかきくけこが延々と続く

両方とも「goto 〇〇」というコードがありますね。これが「〇〇」のところにジャンプするという命令になります。この場合の〇〇の部分をラベルと言います。ラベル名は何でもいいですが、「ラベル名:」というようにコロンをつけ忘れないように注意です。

ちなみに上記の後半の「repeat」の方は、この書き方だと処理が無限ループになっちゃうので書き方としてはNGです。今回はあえてやっちゃいましたが。

このgotoをループ処理の中に書くことで、多重ループを抜けることが可能です。

for($i = 1; $i < 10; $i++) {
  $line = array();

  for($j = 1; $j < 10; $j++) {
    $line[] = $i * $j;

    if($i == 6) {
      goto endloop;
    }
  }

  echo implode(' ', $line).'';
}

endloop:
echo 'ループを抜けました';

//出力結果
1 2 3 4 5 6 7 8 9
2 4 6 8 10 12 14 16 18
3 6 9 12 15 18 21 24 27
4 8 12 16 20 24 28 32 36
5 10 15 20 25 30 35 40 45
ループを抜けました

このgotoはPythonにもあるのですが、モジュールのインポートが必要な上、どうやらdatetimeやmathみたく最初から使える状態ではないっぽいです。自分でサーバーにモジュールをインストールして使わないといけないらしい。



サーバーにgotoモジュールがインストールされてるかどうかは、SSHとかでサーバーに接続して以下のコマンドを実行すれば確認できます。

$ python -c "help('modules goto')"

これで「goto」という名前が返ってきたらすでにインストールされてるので使えます。なければインストールが必要。

gotoモジュールをインストールするには、まず下記のページからパッケージファイルをダウンロードして、解凍したものをサーバーの適当な場所にアップします。アップしてから解凍してもいいけど。

goto

解凍するとフォルダの中に「setup.py」というファイルがあるので、フォルダのパスまで移動し、以下のコマンドを実行する。

例えば「/var/tmp/」というフォルダに下に解凍した場合。

$ cd /var/www/goto-1.0
# python setup.py install

正常にインスールが完了すればgotoモジュールが使えるようになります。

from goto import goto, label

for i in range(1, 10):
  line = []

  for j in range(1, 10):
    line.append(str(i * j))

    if i == 6 :
      goto .endloop

  print(' '.join(line))
  
label .endloop

だいたいPHPと同じ書き方で良いですが、ラベル名の先頭にはピリオドをつける必要があります。あとコロンが不要な代わりにラベルの前に「label」を記述する。

一応これでPHPのgotoと同じ動きを実装できますが、上記のダウンロードページにも書いてある通り、このモジュールはエイプリルフールのネタとして作ったものなんだそうです。動作はするけど本番運用などでは使うなと製作者さんが言っているので、ちょっとお試しで使ってみる程度に留めとく方が良いかもしれません。モジュール自体も古いしね。






長々書いておきながら上記のgotoに関しては使用を推奨できないので、やるならfor文とelseの組み合わせの方を使う方が良さそうですね。

僕の感覚だとPHPに慣れているせいか、breakしても実は処理が続いてるってのはうっかり処理の書き間違いが発生しちゃいそうで(上記で言うと「i > 5」のところを「i == 6」と書くような)、ちょびっとだけ具合が良くないかなーという気もしますが、まあやってやれないことはないので、ひとまずはこれで良しとしておきましょう。



その他のPythonを少し触ってみたの記事はこちら
前書きと索引的な
 もしかしたら何か関連しているかも? 
 質問や感想などお気軽にコメントしてください