【Python / JavaScript】イテレータとクロージャについて

最近気になってるUnityという組み込みC向けのテストフレームワーク。組み込みはハードウェア依存があるからTDDしにくいと思ってたけど、凡ミスでもmakeが通ってしまい組み込んでからミスに気づいてたら当たり前だけど開発サイクルは良くない。これはCIツールと組み合わせて使いたい。

今回は Python/JavaScript(ECMAScript) のイテレータとクロージャの基礎について。

aa

Iterator

イテレータ(Iterator)はデータの各要素にアクセスを抽象化した仕組みです。

Python

もっとも簡単なのが、iter()を使う方法。そうすることでIteratableなオブジェクトとなる。

i = iter([1,2,3])

print i.next()# 1

for num in i:
 print num #2,3

__iter__()はイテレータ自身を返し、next()は次の要素を返す。

ary = [1,2,3,4,5,6,6,7,8,8,9,9,0]
 
# 外部イテレータとして
it = iter(ary)
while 1:
    try:
        print it.next()
    except StopIteration:
         # next()はStopIteration例外を発生させる
         break

# 内部イテレータとして
for element in ary:
    print element

どちらも結果は同様。独自で実装しているもあります。

JavaScript

jsもイテレータオブジェクトは作成されると、next() かfor…in および for each でアクセスしていくことができます。
Object.prototypeが持っているプロパティを参照しないのでその辺を気にしなくて良いメリットがあります。

var ary = { name: 'mameroot', birthYear: 1987, work: "engineer"};
var it = Iterator(ary);

try {
  while (true) {
    console.log(it.next());
  }
} catch (err if err instanceof StopIteration) {
  console.log("end");
} catch (err) {
  console.log("err: " + err.description);
}

nextメソッドで次の配列要素に切り替えていきます。
hasNext()が使えませんでしたので catch (err if err instanceof StopIteration) で例外キャッチする方法にしました。

Closure

クロージャ(Closure,閉包,関数オブジェクト)の実装も言語によって異なります。

Python

クロージャはpythonではlambda式になります。
下の例だとdefに比べ多少記述が短くなりますが、あまりlambdaのメリットを感じないと思いますが複雑に関数を組み合わせる時とか便利です。

def add(x, y):
     return x + y
     
print add(1, 2)# 3

lam = lambda x,y : x+y

print lam(1,2)# 3

上がdef宣言、同じ意味の式がlam。
()で括ることで複数行で記述できますし、配列にlambda式を入れることもできます。

bda = (lambda x,y: x+y)

print bda(1,2)# 3

ary  = [(lambda x: x+3), (lambda x: x**3), (lambda x: x/2)]

print ary[1](2) # 8

JavaScript

JavaScriptでは関数もオブジェクトです。
クロージャは生成された部分におけるローカル変数を参照できます。

function counter() {
  var i = 0;

    return function() { // 無名関数
        return i += 1;
    }
}
 
cnt = counter();
console.log(cnt()); // 1
console.log(cnt()); // 2 *ローカル変数(jsは関数スコープ)は保持
console.log(i); // もちろんダメ

上記例をカスタムしてcounterに引数を用意して this.getID = function() {return id} とかすればアクセサ指定子がないjsでもプライベート的な使い方ができるということです。

ちなみに名前空間で重複してもエラーが起きないので注意が必要が必要です。

Reminder

会社では月末に仕事で使った交通費や雑費の申請をしているのだけど忘れることが多いので、月末になると会社にReminderを送信するスクリプト。
実装は良くない。

# -*- coding: utf-8 -*-

# msg create
def create_message(from_addr, to_addr, subject, body):
    encoding = 'utf-8'
    msg = MIMEText(body.encode(encoding), 'plain', encoding)
    msg['Subject'] = subject
    msg['From'] = from_addr
    msg['To'] = to_addr
    msg['Date'] = formatdate()
    return msg

# send by gmail
def send_via_gmail(from_addr, to_addr, msg):
    s = smtplib.SMTP('smtp.gmail.com', 587)
    s.ehlo()
    s.starttls()
    s.ehlo()
    s.login('******@gmail.com', '*****')
    s.sendmail(from_addr, [to_addr], msg.as_string())
    s.close()

def sending_mail():
        from_addr = '****@gmail.com'
        to_addr = '*****'
        msg = create_message(from_addr, to_addr, 'Auto Send by ****','交通費/雑費申請を忘れずに.')
        send_via_gmail(from_addr, to_addr, msg)
        print('[Info] sent msg')

def main():
    time = datetime.datetime.today()
    if time.day > 25:
        # text_read&write_mode
        f = open('schedule_log.txt','r+')
        line = f.readline()
        b = False

        chk = '%s-%s\n' % (time.year,time.month)

        while line:
           print line
           line = f.readline()
           if line == chk:
              b = True

        if b == False:
            print '[Info] today is %s' % time.day
            sending_mail()
            f.write(chk)
        else:
            print '[Info] Already sent' 
        f.close

if __name__ == "__main__":
    main()