ardggy's blog

Esc - Meta - Alt - Ctrl - Shift

範囲を生成するようなあれ(パース編)

きのうのつづきで範囲構文を解析することにする。

パーサはさくっと構文解析ライブラリにまかせる。 python パーサ で検索しただけだけど、BNF っぽく書けることで雑に採用。 もっとも BNF がこれでいいのかは自信がないが、doctest が通ってるしいいことにしよう。

github.com

$ sudo easy_install pip
$ sudo pip install lark-parser

チュートリアルを眺めつつ、こんなかんじで書けた。

#!/usr/bin/env python
# coding: utf-8
'''
>>> list(parse("1"))
[1]

>>> list(parse("1,2"))
[1, 2]

>>> list(parse("1,2,5-7"))
[1, 2, 5, 6, 7]

>>> list(parse("1,2,5-7,9-10,13"))
[1, 2, 5, 6, 7, 9, 10, 13]
'''

import itertools
from lark import Lark, Transformer, v_args

@v_args(inline=True)
class RangeSyntax(Transformer):
    concat = itertools.chain

    def constant(self, x):
        return itertools.repeat(int(x), 1)

    def range(self, start, end):
        return xrange(int(start), int(end) + 1)

parser = Lark('''
?expr : expr "," expr     -> concat
      | NUMBER "-" NUMBER -> range
      | NUMBER            -> constant

%import common.NUMBER
%import common.WS
%ignore WS
''', start='expr', parser='lalr', transformer=RangeSyntax())

parse = parser.parse

if __name__ == '__main__':
    import doctest
    doctest.testmod()

範囲を生成するようなあれ

crontab の構文で "1-10,15,16" みたいなやつがあるけど、これを解析してイテレータがでてきたら便利そうである。 この場合、1 から 10 がでてきて、次に 15, 16 とでてくる。

#!/usr/bin/env python
# coding: utf-8

from itertools import repeat, chain

def once(x):
  return repeat(x, 1)

def main():
  expected = [1,2,3,4,5,6,7,8,9,10,15,16]
  actual = chain(xrange(1, 11), once(15), once(16))

  assert expected == list(actual)

if __name__ == '__main__':
  main()

こういうのを生成できるパーサをでっち上げる瞬発力がほしい。

連想睡眠法よかった

幼少のころから不眠のケがあって、だいぶ悩まされていた。

呼吸法が紹介されていれば試したし、サプリメントも試したし、運動もしてみたし、それでもぜんぜん眠れない。 もう医者で睡眠薬もらうしかないかなあと思っていた。

ところがひろゆきの『働き方 完全無双』という本を読んだら「連想睡眠法」というのが紹介されていて、これが大変良かった。

togetter.com

ほんと良い。ありがたい。