今更ながらS式についての説明&pythonでのパーサー方法
今回はS式についての説明をする。
経緯
S式について本気で考え始めたのは2日ほど前、以下の記事をあげるちょっと前になる。 なんと、robocup 2dシュミレーションサーバーのサーバーメッセージがS式で返ってくるのである。 S式なんてもんは195x年くらいからあるすごい古いものでLISPという言語にも使われているそうだが、なんにしろ古すぎて情報がほぼない。 なのでこんな感じでメモを書いている次第。
S式ってなんぞや
さっきからS式S式うるさいけどS式って何?という人がほとんどだと思う。自分もほんの1ヶ月前までは
S式?葬式の間違いじゃない(*´∀`*)?
とか言っていましたw。
なので簡単に説明を。 Wikipediaによると
In computing, s-expressions, sexprs or sexps (for "symbolic expression") are a notation for nested list (tree-structured) data, invented for and popularized by the programming language Lisp, which uses them for source code as well as data.
From Wikipedia, the free encyclopedia
へ?にほんごじゃないとわからんな。 ということなので日本語にすると、こんな感じ。
S式(エスしき、英: S-expression)とは、Lispで導入され、主にLispで用いられる、2分木ないしリスト構造の形式的な記述方式。SはSymbolに由来。
出典: フリー百科事典『ウィキペディア(Wikipedia)』
こんな感じ。意味がわからん。
例
例を示すとこんな感じ。
(hello! hhh (12345 "('$%RRR") (asdf 2222))
こんなのをリスト化すると
["hello!","hhh",["12345","('$%RRR"],["asdf","2222"]]
となる。なんとなくわかっただろうか。要するにS式はカッコがリストの大カッコ([])になり、空白がカンマになり、文字のダブルクォート囲みがないやつみたいに考えてもらえればいい。 考えるとあまり難しくないと思うが、以外に落とし穴がありかなり厄介。
落とし穴
先程のS式に特殊な文字があったのをおわかりだろうか。
"('$%RRR"
そう。これ。これが落とし穴。S式では特殊な文字(カッコ等)があればダブルクォートで囲む仕組みになっています。このように、いろいろ基準が曖昧なのですべてに適応できるプログラムを書くのがすごい大変です。自分もlibscipsを作っていてここまで曖昧なのは知らなかったから、特殊文字が出てきたときにエラーを吐いてびっくりした。
じゃあどうすればいい?
調べてみると、こんなものが。
sexpdata。S式の解析をしてくれるプログラムらしい。便利。使い方もすごい簡単。
そして、作っている人が日本人!(なぜ日本語版googleに乗ってないんだ)
pip install sexpdata
これで、sexpdataと言うやつが入る。
使い方
読み込み
S式の読み込みは以下の通り。たった2行。
from sexpdata import loads loads("""(hello! hhh (12345 "('$%RRR") (asdf 2222))""") # [Symbol('hello!'), Symbol('hhh'), [12345, "('$%RRR"], [Symbol('asdf'), 2222]]
しっかりとリスト形式を返してくる。 でも、Symbolとかいうよくわからないやつがある。これでは使いづらい。なので、こんなプログラムを書いてみる。
from sexpdata import Symbol, loads def func(text): if type(text) == Symbol: return text.value() elif type(text) == list: return tostring(text) else: return str(text) def tostring(text): return list(map(func,text)) tostring(loads("""(hello! hhh (12345 "('$%RRR") (asdf 2222))""")) # ['hello!', 'hhh', [12345, "('$%RRR"], ['asdf', 2222]]
これで文字列になったデータが返ってくる。
書き込み(変数に入れる)
以下のとおりでいい
dumps(['a', 'b']) # ("a" "b")
他にもいろいろなオプションがある。一応公式に乗っているのは以下の通り。
Basic usage:
>>> print(dumps(['a', 'b'])) ("a" "b") >>> print(dumps(['a', 'b'], str_as='symbol')) (a b) >>> print(dumps(dict(a=1))) (:a 1) >>> print(dumps([None, True, False, ()])) (() t () ()) >>> print(dumps([None, True, False, ()], ... none_as='null', true_as='#t', false_as='#f')) (null #t #f ()) >>> print(dumps(('a', 'b'))) ("a" "b") >>> print(dumps(('a', 'b'), tuple_as='array')) ["a" "b"]
More verbose usage:
>>> print(dumps([Symbol('a'), Symbol('b')])) (a b) >>> print(dumps(Symbol('a'))) a >>> print(dumps([Symbol('a'), Quoted(Symbol('b'))])) (a 'b) >>> print(dumps([Symbol('a'), Quoted([Symbol('b')])])) (a '(b))
詳しい解説は割愛。 詳しい解説がほしい方は以下のサイトまで。
最後に
googleさん、日本語版だと全然ヒットしないのに英語版だとめっちゃヒットする。今度から、英語版にしようかな。英語の練習にもなるし。
個人的な質問等はこちらまで。
https://forms.gle/V6NRhoTooFw15hJdA
また、自分が参加しているRobocup soccer シミュレーションリーグのチームでは参加者募集中です!活動の見学、活動に参加したい方、ご連絡お待ちしております!
詳しくはこちら