#Javaおじさんがクロージャを理解したきっかけ
こんにちは、Javaおじさんです。
お仕事絶賛募集中です(定期)
クロージャの話が前回のコメントの中で出てきたので、[JavaScript] 猿でもわかるクロージャ超入門とか地味に見てるんだけど、わかりやすくていいページですこれ。
なお、クロージャってものに初めて触れた頃のJavaおじさんはFactoryMethodっぽいオブジェクトとして見たのが最初に理解できたきっかけでしたね(隙あらば自分語り
「関数を返す関数」っていう表現が「インターフェースを返すメソッド」にちょっと見えちゃったってだけのことなんだけど。
JavaでFactoryMethodっていうと
- FactoryMethod内でインターフェースの実体として具象クラスをインスタンス化して返却する
ってものなんだけど、「1つの関数しか持たないインターフェースとその実装を返すもの」としてクロージャを見るとちょっと理解できそうな気がしたんだよね。
もちろんいろいろ相違があることは確かなんですよ。
- そもそもクロージャは「関数を返す関数」そのものをオブジェクトとして扱って、「返す関数」は自身に内包しているので、インターフェースを返すFactoryMethodとして見るのは位置づけが全然違う、とか
- クロージャ内での変数がJavaで言うところのstaticな変数になるという性質はFactoryMethodじゃ説明できない、とか
でもまあ最初に上のような見方をしたことで「腑に落ちちゃった」のも事実なのよね。
もちろん虚心に説明を読んで素直に書いてあることを受け入れさえすれば何の問題もないんだけど、人間てのは経験が邪魔をする生き物でもあるので、経験に沿って類似した概念から新しい概念を理解する努力も必要なのかなと思うのよね。当然軌道修正は必要だし非効率なのも否めないけど。
ええ、もちろんアホみたいなことを言ってるのは百も承知ですよっと。
でも案外古くからJava使ってる人でこの辺の概念がピンと来てない人はいるんじゃないかなって。いやおれがそうだったってだけなんだけどw
#構造型
テキストの5章「構造型、可変性と高階関数」を読み進めている。
この章で扱う構造型としては以下の4つ。
- タプル
- list
- range
- dict
mapはまだ先らしい。
#タプル
文字列同様、不変な要素を順番に並べたもの。
文字列との違いとしては、「要素は文字でなくてもいい」。
個々の要素が互いに同じ型である必要もない。
初期化する際は丸括弧()を用いてこう書く。
t1 = (1, 'abc', 3.14)
タプルで気を付けないといけないのは要素が1個しかない場合の記述法。
OK:t1 = (1,)
NG:t2 = (1)
おわかりいただけただろうか?要素が1個しかない時もカンマつけないとエラーになるのである。
面倒くさっwなんでこんな文法にしたしw
文字列同様掛け算でタプルの複製を複数回つなげることもできるし、添え字やスライスも使用可能。
あと「タプルの要素としてタプルを入れられる」というのはタプルをJavaのListとして考えると腑に落ちるかもしれんね。あれもジェネリクスで型限定しなければ何でも入るしな。
で、このテキストは結構いろんなことを前触れなしでぶっこんでくるんだけど、Pythonの関数で戻り値をタプルや文字列などの「順序型」で返してくる場合、多重代入が可能なので戻り値を受ける変数を複数並べられるという話がここで突然出てくる。
そらまあ考えてみれば当たり前だよなあ。当たり前なんだけど結構衝撃的だよなあw
今まで常に戻り値は1つしか返せない言語ばっかやってきたからなあ。
あまりに衝撃的だったんでサンプルコード書いてみた。
def findExtremeDivisors(n1, n2):
"""正の整数n1とn2の最小公約数 > 1と最大公約数から成るタプルを返す
公約数がない場合(None, None)を返す。"""
minVal, maxVal = None, None
for i in range(2, min(n1, n2)+1):
if n1%i == 0 and n2%i == 0:
if minVal == None:
minVal = i
maxVal = i
return (minVal, maxVal)
minDivisor, maxDivisor = findExtremeDivisors(16, 32)
print('1より上の最小公約数', minDivisor,'最大公約数', maxDivisor)
divisors = findExtremeDivisors(9, 15)
print('1より上の最小公約数', divisors[0],'最大公約数', divisors[1])
実行結果は以下。
1より上の最小公約数 2 最大公約数 16
1より上の最小公約数 3 最大公約数 3
いけるなあ。こうなるとタプルで返してくるのを素直にタプルで受けなくても意味のある名前で受けてやることでより可読性は高まるわけか。ま、でもこれもケースバイケースかな。
追記:戻り値の変数の数がタプルの数と合わない場合どうなんだろうね?と思ったのでちょっとコード書いてみた。
def getThreeValue():
return (1, 'text', 3.14)
#戻り値を受ける変数がタプルのデータ総数より少ない場合
first, second = getThreeValue()
print('最初の要素', first, '2番目の要素', second)
#戻り値を受ける変数がタプルのデータ総数より多い場合
first, second, third, fourth = getThreeValue()
print('最初の要素', first, '2番目の要素', second)
print('3番目の要素', third, '4番目の要素', fourth)
Visual Studio Codeでこれ書くと実行前にpylintがエラーを教えてくれる。
なるほど、多すぎても少なすぎてもいかんのねー。
てことは何個入ってくるかわからんタプルの場合は素直にタプルで受けてやらんとあかんのね。
(追記終わり)
ところで、「順序型」ってワードもここで初めて出てきたんだけど、タプルと文字列の他になにがあるんだろ。たぶんlistもそうだろうと思うけど。後でググるか。
そーいや4章にNoneの話出てこなかった...?あ、読み飛ばしてた。
return文を見つけるまで、もしくは実行する文がなくなるまで、関数の本文内のコードが実行される。
...
一方、後者の場合、関数はNoneという値を返す。(returnの後に式がない場合、返り値はNoneである)
あ、そーゆーことか。つまり明示的に戻り値を指定しなかったときにのみ使用される値というわけね。
しかしなんでこんな値が必要なんだろ。関数は一貫性をもって何かの値を返さなければならない事情がある...?
さて、いったんここでこの記事は終了。