Nikon Transfer 2で撮影年月のフォルダに自動振り分けする方法
Canon EOS Kiss Digital
5年以上EOS Kiss Digital Xをメインで使用しています。カメラからMacにファイルを取り込むのにはCanon純正のEOS Utilityを使用しています。
このEOS Utilityは保存先のフォルダを自動生成してくれます。自動生成されるフォルダは自分の好みにカスタマイズすることができます。因みに僕は 2012_05 というように<撮影年_撮影月>というフォルダに写真を取り込むように設定しています。
月またぎで撮影した写真を取り込むと新しい月のフォルダを自動生成してくれるのでとっても便利です。
Nikon COOLPIX P300
釣った魚を撮るのに明るい広角のコンデジが欲しくなり、P300を購入しました。このカメラから写真を取り込むのには便利なEOS Utilityを使うことができません。しかもNikon純正のNikon Transfer 2でも僕が望むようなフォルダの自動生成は出来ません。
困ったもんです。仕方なく自分でフォルダを作って、自分で振り分けをしていました。かなり面倒くさい。
フォルダ監視
なんとかフォルダの自動生成・自動振り分けができないものかと色々調べているうちにEOS Utilityにはフォルダ監視という機能があることに気付きました。早速いじってみると普段使用している振り分けの設定がそのまま使用できることが分かりました。
配列のnil以外の要素数をカウントする
Conway's Game of Life(ライフゲーム)を実装していて、配列の要素のうちnilでない要素の数をカウントする必要がありました。
Array.map
早速Arrayのリファレンスを眺めてmapメソッドとcompactメソッドを使えば行けそうだぞってことで次のようなコードを書きました。
配列の要素をキーにハッシュから値を取得して、取得できた値の件数をカウントする例です。
なんか配列.map{|x| なんかハッシュ[x]}.compact.size
mapにより生成された配列にはnilが含まれる可能性があるのでcompactでnilを取り除くことを考えました。あまりスマートじゃないけどJavaではこのようには書けないのでとりあえずこれで満足していました。
Enumerable.count
でも後になってArrayのスーパークラスであるEnumerableにcountというメソッドがあることを知りました。なんだこれを使えばもっと簡単に書けるじゃん!てことで次のようなコードを書きました。
なんか配列.count{|x| !なんかハッシュ[x].nil?}
Javaプログラマ的な発想ですよね。いやnilの特性をよくわかっていなかったんです。
ただ、なんかnilの判定が気持ち悪くてRubyならもっとスマートに書けるんじゃない?と思ってnilの判定について調べました。そしたらありましたよ。リファレンスに。
おお!これだ!ってことでコードは次のようになりました。
なんか配列.count{|x| なんかハッシュ[x]}
すごい。シンプルすぎる。すごいよRuby。
ArrayとEnumerableのメソッドは全て一度は使ってみたほうがいいですね!
配列がif文を変える
Javaプログラマとしてやってきた僕がRubyの配列を使った衝撃的な記述方法を発見しました。いや多分Rubyistには当たり前のことなんだと思うけど。
例えば、「サイコロの目が1または4または5の場合、◯◯する」という処理を記述する場合を見てみましょう。変数faceにサイコロの目が入っているものとします。
Javaでは
if (face == 1 || face == 4 || face == 5) { なんか処理; }
と記述します。
Rubyでも同様に記述できますが、配列を使用して
if [1, 4, 5].include?(face) なんか処理 end
と記述することができます。これって凄くないですか!?感動しました。Javaではこの発想はありませんでしたが、Arrayのリファレンスを眺めていてひらめきました。Arrayは無限の可能性を秘めていそうです。Javaからスイッチした人はとりあえずArrayのリファレンスを隅々まで見たほうがいいよ!
RubyでFizzBuzzしてみる。ただしif文は使わない。(4)
3回目で終了かと思われたこのシリーズ、なんと4回目になりました。それというのもid:keyesberryさんから次のようなコメントを頂いたからです。
joinとmapでもう少し短くしてみました:) puts (1..100).map{|i| [[:Fizz][i % 3], [:Buzz][i % 5]].join.sub(/^$/, i.to_s)}
ちなみに3回目の実装はこんな感じでした。
(1..100).each{|i| puts((["Fizz"][i % 3].to_s + ["Buzz"][i % 5].to_s).sub(/^$/, i.to_s))}
さて、教えていただいたコードには僕が知らないメソッドや記法があるので1つずつ見ていきましょう。
":文字列"ってなんだ?
"Fizz"と文字列を表していた部分が:Fizzに変わっています。これで1文字分の節約に成功しています。一体これは何でしょう?
これはシンボルと呼ばれ、Symbolクラスのインスタンスなのだそうです。
リテラル#シンボル
Symbolクラス
シンボルを表すクラス。シンボルは任意の文字列と一対一に対応するオブジェクトです。
文字列の代わりに用いることもできますが、必ずしも文字列と同じ振る舞いをするわけではありません。同じ内容のシンボルはかならず同一のオブジェクトです。
コードの本質に影響はないのですがコードの記載量とオブジェクト生成コストの削減に貢献しています。
join
joinはArrayクラスのメソッドです。
Array#join
配列の要素を連結してくれるメソッドで、3回目のコードの特に冗長な部分である
配列の要素.to_s + 配列の要素.to_s
という記述を
[配列の要素, 配列の要素].join
とシンプルにまとめることができます。このメソッドの肝は
文字列でない要素に対しては、to_str があれば to_str、なければ to_s した結果を連結します。
でしょうか。この仕様のお陰で配列の要素がnilの場合の処理を別に記述する必要がなくなりました。
map
mapはEnumerableのメソッドです。
Enumerable#map
Array#eachと似ていますが、ブロックを評価した結果が配列として返ってくるという点で異なります。
Array#eachはブロックを評価するだけなのでブロック内でputsしていましたが、Enumerable#mapはブロックを評価した結果が配列として返ってくるので返ってきた配列をputsしています。
個人的には、繰り返しの中で1件ずつ出力するより返ってきた配列を一気に出力する方が好きです。コストは増えると思いますが…
総評
4回に渡りFizzBuzz問題をRubyで実装してきましたが、特に強く感じたのはRubyの配列に対する異常なまでの配慮です。ここで異常と表現したのは僕が長年javaを使用してきうえでRubyを使用したときに、配列を扱うのがすごく楽しく感じたからです。賞賛の意を最大限に表した言葉なのです。
javaにおいては配列を使用したコードというのはあまり好きではありませんでした。扱いづらいし。でもRubyにおていは積極的に配列を使用したくなってしまいます。だって便利なんだもん。多分処理コストは増えるのかもしれないけど解決したいことがシンプルなコードで表現できるということは本当に気持ちいいです。
リファレンスでブロックについて見てみると
ブロック付きメソッドとは制御構造の抽象化のために用いられるメソッドです。最初はループの抽象化のために用いられていたため、特にイテレータと呼ばれることもあります。
とあります。ここからも、ともすると煩雑になりがちな繰り返し処理の記述をシンプルに記述できるようにという配慮を感じることができます。
今回RubyでFizzBuzzしてみて本当によかったと思います。Rubyと配列が好きになりました。
それとFizzBuzzも。単純だけど奥が深い。
みんな、新しい言語を始めたらまずはFizzBuzzしてみるといいと思うよ!
伝統に従ってHello World!するのは良いけどFizzBuzzするとその言語のもつチカラをより体感できると思います。
RubyでFizzBuzzしてみる。ただしif文は使わない。(3)
さてこのシリーズも3回目である。今までの実装を振り返ってみよう。
今までの実装
1回目
fizz = ["Fizz", "*", "*"] buzz = ["Buzz", "*", "*", "*", "*"] for i in 1..100 do s = fizz[i % 3] + buzz[i % 5] puts(s.sub("*", "").sub("*", i.to_s)) end
いかにもRuby初心者が書きそうなコードである。いやまだ初心者だけど。
2回目
for i in 1..100 do puts((["Fizz", "", ""][i % 3] + ["Buzz", "", "", "", ""][i % 5]).sub(/^$/, i.to_s)) end
かなりすっきりしたが、1行が長い。無理矢理っぽい。
今回の実装
for i in 1..100 do puts((["Fizz"][i % 3].to_s + ["Buzz"][i % 5].to_s).sub(/^$/, i.to_s)) end
14文字短縮できた。今回の実装の肝は、配列。Javaプログラマなら、配列といえばArrayIndexOutOfBoundsExceptionというくらい配列のインデックスに敏感になってしまうところだが、なんとRubyでは例外が発生しない。そう、範囲外のインデックスを指定しても処理が続くのである。
具体的に言うと、Rubyでは配列の範囲外を参照するとnilが返ってくる。
しかも、nilもオブジェクトであり、メソッドを持っていてto_sなんかが使えてしまうのである!Javaプログラマには驚きの展開。
てなわけで、配列の要素は1個にすることができた。3または5の倍数でない場合はnil.to_sつまり""が連結されことになる。
3の倍数または5の倍数または15の倍数の時は連結した文字列をそのまま出力し、3でも5でも割り切れない場合は""を数値に置換している。
そしてさらにforループをなくすと…
(1..100).each{|i| puts((["Fizz"][i % 3].to_s + ["Buzz"][i % 5].to_s).sub(/^$/, i.to_s))}
なんと1行で済んでしまった。forループが1行で書けるなんて!
ただ1行が長いけどね。
これ以上はもうどうにもならないでしょ!?
配列 Array
配列の作成と要素の参照
array = [1, 2, 3] p array[0] #=> 1 p array[5] #=> nil p array[5].to_s #=> "" array[2] = 10 p array #=> [1, 2, 10]
指定したインデックスが存在しない場合はnilが返ってきます。Javaでは例外ArrayIndexOutOfBoundsExceptionが発生しますがRubyだと処理が続行できます。しかもnilもオブジェクトなのでメソッドを持っています。Javaプログラマなら間違いなくカルチャーショックを受けるでしょう!
配列のサイズ
sizeメソッドとlengthメソッドがあります。
ary = ["a", "b", "c", "d", "e"] p ary.size #=> 5 p ary.length #=> 5
配列からnilを取り除く
compactメソッドを使用します。
ary = ["a", nil, "b", "c", nil, "d", "e"] p ary.compact #=> ["a", "b", "c", "d", "e"]
配列の要素を出力する
array = ["a", "b", "c"] array.each{|x| puts x} a b c
変数に代入しなくてもメソッドを使用することができます。
[1, 2, 3].each{|i| puts i * 10} 10 20 30