最近、仕事でもプライベートでもよく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関数を使えば降順になります。
まとめ
みんなちがって、みんないい。
コメントする