コンテンツにスキップするには Enter キーを押してください

Python・JavaScript・PHPでのループ処理のやり方まとめ

最近、仕事でもプライベートでもよくPythonを使っているのですが、ループのやり方が今まで使ってきたJavaScriptやPHPと少し使い勝手が違っていて迷うことがよくあるので防備録としてまとめておきます。
比較対照として、JavaScriptとPHPで同じことをする方法もまとめてみました。
こうやって比較してみるとそれぞれ特徴があって面白かったですよ。

リスト(配列)編

リストの要素を順番に取得する

Pythonの場合

単純に、リストに収められている順番に取得するだけなら、for inで回すだけです。

list = ['foo', 'bar', 'baz']
for item in list:
    print item

実行結果

foo
bar
baz

JavaScriptの場合

forでインデックスを0から1つずつ増加させて参照していきます。
id:amachang曰く、配列では for inは使わないほうがいいらしいので。
http://d.hatena.ne.jp/amachang/20070202/1170386546

var list = ['foo', 'bar', 'baz'];
for( i=0; i < list.length; i++ ){
    document.write(list[i] + "\n");
}

jQueryを使った場合はこんな書き方もできます。

var list = ['foo', 'bar', 'baz'];
$.each(list, function(){
    document.write(this + "\n");
});

PHPの場合

foreach関数で回します。

$list = array('foo', 'bar', 'baz');
foreach( $list as $item ){
    echo $item."\n";
}

リストの要素とインデックスを順番に取得する

Pythonの場合

要素だけでなくインデックスも同時に取得したい場合は、enumerate関数を使います。

list = ['foo', 'bar', 'baz']
for index, item in enumerate(list):
    print '%s - %s' % (index, item)

実行結果

0 - foo
1 - bar
2 - baz

JavaScriptの場合

先ほどの例ですでにインデックスを使っているので、やり方は同じです。

var list = ['foo', 'bar', 'baz'];
for( i=0; i < list.length; i++ ){
    document.write(i + ' - ' + list[i] + "\n");
}

jQueryを使った場合、先程の例のコールバック関数の第1引数がインデックス、第2引数が要素になります。

var list = ['foo', 'bar', 'baz'];
$.each(list, function(index, item){
    document.write( index + ' - ' + item + "\n" );
});

PHPの場合

先ほどと同じforeach関数ですが、「as」の後に「$変数 =>」を追加するとインデックスが取れるようになります

$list = array('foo', 'bar', 'baz');
foreach( $list as $index => $item ){
    echo $index.' - '.$item."\n";
}

複数のリストを同時にループさせる

Pythonの場合

対象のリストをzip関数でタプルのリストに変換し、for inで回します。
まとめたリストと同じ数の変数を使って、タプルをアンパックします。

list_a = ['foo', 'bar', 'baz']
list_b = ['hoge', 'fuga', 'piyo']
list_c = [123, 456, 789]
for item_a, item_b, item_c in zip(list_a, list_b, list_c):
    #↑item_aにはlist_aの、item_bにはlist_bの、item_cにはlist_cの中身が
    #それぞれ順番に代入される
    print '%s - %s / %s'% (item_a, item_b, item_c)

実行結果

foo - hoge / 123
bar - fuga / 456
baz - piyo / 789

でもよく見ると、この場合はわざわざアンパックする必要がないですね。
そういうときは

list_a = ['foo', 'bar', 'baz']
list_b = ['hoge', 'fuga', 'piyo']
list_c = [123, 456, 789]
for tuple in zip(list_a, list_b, list_c):
    print '%s - %s / %s'% tuple

でOKです。

また、この場合でもenumerate関数を使えばインデックスが取れます。

list_a = ['foo', 'bar', 'baz']
list_b = ['hoge', 'fuga', 'piyo']
list_c = [123, 456, 789]
for index, (item_a, item_b, item_c) in enumerate(zip(list_a, list_b, list_c)):
    #      ↑ここのカッコは必須
    print index
    print '   %s + %s - %s' % (item_a, item_b, item_c)
print '-'*20
#アンパックしない例
for index, items in enumerate(zip(list_a, list_b, list_c)):
    print index
    print '   %s + %s - %s' % items

実行結果

0
   foo + hoge - 123
1
   bar + fuga - 456
2
   baz + piyo - 789
--------------------
0
   foo + hoge - 123
1
   bar + fuga - 456
2
   baz + piyo - 789

JavaScriptの場合

zip関数みたいな機能はないので、同じインデックス数字を使って参照します

var list_a = ['foo', 'bar', 'baz']
var list_b = ['hoge', 'fuga', 'piyo']
var list_c = [123, 456, 789];
for( i=0; i < list_a.length; i++ ){
    document.write( list_a[i] + ' - ' + list_b[i] + ' / ' + list_c[i] + "\n" );
}

PHPの場合

JavaScriptと同じです。

$list_a = array('foo', 'bar', 'baz');
$list_b = array('hoge', 'fuga', 'piyo');
$list_c = array(123, 456, 789);
for( $i=0; $i < count($list_a); $i++ ){
    echo $list_a[$i] . ' - ' . $list_b[$i] . ' / ' . $list_c[$i] . "\n";
}

範囲編

指定回数ループさせる

JavaScriptの場合

for( i = 0; i < 10; i++ ){
    document.write(i+"\n");
}

PHPの場合

for( $i = 0; $i < 10; $i++ ){
    echo $i."\n";
}

Pythonの場合

Pythonにはこういう感じのforループがありません。
ではどうするかというと、range関数を使って数字のリストを作り、それをfor inで回します。

for i in range(10):
    print i

実行結果

0
1
2
3
4
5
6
7
8
9

for i in range(x,y,z)は、for( i=x; i<y; i+=z )(zが負の数の場合はfor( i=x; i>y; i+=z )) と同じ動きになります。

辞書(連想配列)編

辞書(連想配列)のキーと要素をひととおりなめる

Pythonの場合

辞書オブジェクトのiteritemsメソッドを使うと、キーと要素をセットで取り出しながらループできます。
リストのインデックスの取り方と似ています。

dict = {'foo':'spam', 'bar':'egg', 'baz':'bacon'}
for key, item in dict.iteritems():
    print '%s and %s and %s... and %s' % (item, item, key, item)

実行結果

bacon and bacon and baz... and bacon
spam and spam and foo... and spam
egg and egg and bar... and egg

この実行結果からもわかるとおり、取得できる順序は不定です。

JavaScriptの場合

for inを使うとキーが取り出せるので、それを使って要素を参照します。

var dict = {'foo':'spam', 'bar':'egg', 'baz':'bacon'}
for(key in dict){
    document.write(dict[key] + ' and ' + dict[key] + ' and ' + key + ' ... and ' + dict[key] + "\n");
}

実行結果

spam and spam and foo ... and spam
egg and egg and bar ... and egg
bacon and bacon and baz ... and bacon

こちらは定義した順で取り出せていますが、一応不定、みたいです。

PHPの場合

PHPは配列と連想配列の区別がほとんどないので、配列の時と同じくforeachを使います。

$dict = array('foo'=>'spam', 'bar'=>'egg', 'baz'=>'bacon');
foreach( $dict as $key => $item ){
    echo $key.' and '.$item.' and '.$key.' ... and '.$item."\n";
}

実行結果

spam and spam and foo ... and spam
egg and egg and bar ... and egg
bacon and bacon and baz ... and bacon

PHPの連想配列の順序は、定義した順で保証されています。

辞書(連想配列)のキーを昇順にソートして、要素を順番に取り出す

Pytnonの場合

辞書オブジェクトのkeysメソッドでキーのリストが取り出せるので、それをsorted関数でソートすればOKです。

dict = {'foo':'spam', 'bar':'egg', 'baz':'bacon'}
for key in sorted(dict.keys()):
    print '%s and %s and %s and %s... and %s' % (key, dict[key], dict[key], key, dict[key])

実行結果

bar and egg and egg and bar... and egg
baz and bacon and bacon and baz... and bacon
foo and spam and spam and foo... and spam

keyはbar→baz→fooなのでちゃんとソートされていますね。

もうひとつの方法としては、itemsメソッドでキーと要素のタプルが入ったリストが取り出せるので、これを独自の比較式を書いたsorted関数を使ってソートする方法があります。

dict = {'foo':'spam', 'bar':'egg', 'baz':'bacon'}
for key, item in sorted(dict.items(), cmp=lambda x,y: cmp(x[0], y[0])):
    print '%s and %s and %s and %s... and %s' % (key, item, item, key, item)

ちなみに、いずれの方法でもsortedメソッドに reverse=True という引数を指定すると、降順でソートされます。
2つ目の方法の場合はx[0]とy[0]の位置を入れ替えても降順にできます。

JavaScriptの場合

まず一旦for inでキーのリストを作成して、それをソートし、さらにもう一度forループで要素を参照する、といった感じでしょうか。

var dict = {'foo':'spam', 'bar':'egg', 'baz':'bacon'}
var keys = [];
for(key in dict){
    keys.push(key);
}
keys.sort();
for( i = 0; i < keys.length; i++ ){
    document.write(keys[i] + ' and ' + dict[keys[i]] + ' and ' + dict[keys[i]] + ' and ' + keys[i] + ' ... and ' + dict[keys[i]] + "\n");
}

あるいはPythonのときと同じく、キーと要素のリストが入ったリストを作り、やはり独自の比較式を書いたsortメソッドでソートするという手もあります。

var dict = {'foo':'spam', 'bar':'egg', 'baz':'bacon'}
var items = [];
for(key in dict){
    items.push( [ key, dict[key] ] );
}
items.sort(function(x, y){ return x[0]==y[0] ? 0 : x[0]>y[0] ? 1 : -1 });
for( i = 0; i < items.length; i++ ){
    document.write(items[i][0] + ' and ' + items[i][1] + ' and ' + items[i][1] + ' and ' + items[i][0] + ' ... and ' + items[i][1] + "\n");
}

いずれにしてもなんか冗長ですね…。
なお、sortメソッドでソートしたあとにreverseメソッドを使うか、2つ目の方法の場合は比較式のx[0]>y[0]の不等号をひっくり返すと降順になります。

PHPの場合

連想配列にksort関数をかけるだけです。

$dict = array('foo'=>'spam', 'bar'=>'egg', 'baz'=>'bacon');
ksort($dict);
foreach( $dict as $key => $item ){
    echo $key.' and '.$item.' and '.$item.' and '.$key.' ... and '.$item."\n";
}

JSと比べると超楽ちん。降順の場合はkrsort関数を使います。

辞書(連想配列)の要素を昇順にソートして、キーと共に順番に取り出す

Pythonの場合

キーを昇順でソートしたときの、独自の比較式を少し変えるだけでいけます

dict = {'foo':'spam', 'bar':'egg', 'baz':'bacon'}
for key, item in sorted(dict.items(), cmp=lambda x,y: cmp(x[1], y[1])):
    print '%s and %s and %s... and %s' % (item, item, key, item)

実行結果

bacon and bacon and baz... and bacon
egg and egg and bar... and egg
spam and spam and foo... and spam

やはりsortedメソッドに reverse = True を引数に加えるか、x[1]とy[1]を入れ替えると降順になります。

JavaScriptの場合

こちらも、キーを昇順でソートしたときとの、独自の比較式を少し変えるだけでいけます

var dict = {'foo':'spam', 'bar':'egg', 'baz':'bacon'}
var items = [];
for(key in dict){
    items.push( [ key, dict[key] ] );
}
items.sort(function(x, y){ return x[1]==y[1] ? 0 : x[1]>y[1] ? 1 : -1 });
for( i = 0; i < items.length; i++ ){
    document.write(items[i][0] + ' and ' + items[i][1] + ' and ' + items[i][1] + ' and ' + items[i][0] + ' ... and ' + items[i][1] + "\n");
}

降順のやり方も同じです。

PHPの場合

連想配列にasort関数をかけるだけです。

$dict = array('foo'=>'spam', 'bar'=>'egg', 'baz'=>'bacon');
asort($dict);
foreach( $dict as $key => $item ){
    echo $key.' and '.$item.' and '.$item.' and '.$key.' ... and '.$item."\n";
}

さすが、PHPは連想配列の使い勝手がいいですね。
arsort関数を使えば降順になります。

まとめ

みんなちがって、みんないい。


コメントする

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください