lisp - 静かなる名辞 https://www.haya-programming.com/archive/category/lisp pythonとプログラミングのこと Thu, 07 May 2020 20:42:34 +0900 http://blogs.law.harvard.edu/tech/rss Hatena::Blog python使いのためのhy基本文法まとめ https://www.haya-programming.com/entry/2018/03/01/103207 <div class="section"> <h3>はじめに</h3> <p> hyの使い方の記事は一定の需要があると思い、まとめて公開する。</p><p> hyはpythonの抽象構文木に変換されるlisp。lispなのでマクロなどの素敵な機能が使えるらしいが、私もよくわからないのでその辺には触れない。とりあえずpythonのコードをhyに書き写せる水準を目標にする。</p><p> なお、公式ドキュメントはここ。<br /> <a href="http://docs.hylang.org/">Welcome to Hy&rsquo;s documentation! &mdash; hy 0.15.0 documentation</a><br /> </p> </div> <div class="section"> <h3>最初に覚えておくべきこと</h3> <p> hyはlispなのでS式を扱う必要がある。S式は(e1 e2 e3 ...)という形式で、これはlispにおけるリストである。</p><p> S式をそのまま書くと(一部特別な例外があるが)、e1を関数名、e2以下を引数として呼び出す。だから、</p> <pre class="code lang-lisp" data-lang="lisp" data-unlink>=&gt; <span class="synSpecial">(</span><span class="synStatement">+</span> <span class="synConstant">1</span> <span class="synConstant">2</span><span class="synSpecial">)</span> <span class="synConstant">3</span> </pre><p> という結果が得られる。</p><p> hyはlispであり、lispは関数型言語なので、考えられるほとんどのオブジェクト操作はこの関数呼び出しという形で実現することになる。こう書くとpythonのオブジェクト指向が使えなくて不便そうな気がするが、普通に使える。</p> <pre class="code lang-lisp" data-lang="lisp" data-unlink>=&gt; <span class="synSpecial">(</span>setv hoge <span class="synConstant">&quot;h o g e&quot;</span><span class="synSpecial">)</span> =&gt; <span class="synSpecial">(</span>hoge.split<span class="synSpecial">)</span> [<span class="synSpecial">'</span><span class="synIdentifier">h</span><span class="synSpecial">'</span><span class="synIdentifier">,</span> <span class="synSpecial">'</span><span class="synIdentifier">o</span><span class="synSpecial">'</span><span class="synIdentifier">,</span> <span class="synSpecial">'</span><span class="synIdentifier">g</span><span class="synSpecial">'</span><span class="synIdentifier">,</span> <span class="synSpecial">'</span><span class="synIdentifier">e</span><span class="synSpecial">'</span><span class="synIdentifier">]</span> </pre><p> setvはset variable・・・要するにpythonの代入文。そして二行目でpythonでいうhoge.split()をしている。なんてことはない。イメージとしては、インスタンスに属する関数オブジェクトへの参照をなんとかして作り、それをS式の形で関数として呼び出す感じである。</p><p> このことがわかれば、ほぼpythonと同じように書けると思う。<br /> <br /> </p> </div> <div class="section"> <h3>hyのインストール</h3> <p> pip install hyで入る。</p> </div> <div class="section"> <h3>import</h3> <p> 最も簡単なimportは次のように書ける。</p> <pre class="code lang-lisp" data-lang="lisp" data-unlink><span class="synSpecial">(</span><span class="synStatement">import</span> os<span class="synSpecial">)</span> </pre><p> これはpythonのimport osと等価である。</p><p> もう少し複雑なimportをしたい場合、カッコが増える。しかもカクカッコである。たとえばfromに相当するものを書くときは、</p> <pre class="code lang-lisp" data-lang="lisp" data-unlink><span class="synSpecial">(</span><span class="synStatement">import</span> [collections [defaultdict]]<span class="synSpecial">)</span> </pre><p> こんな感じである。これはfrom collections import defaultdictと等価である。もし複数階層あるモジュールからimportしたいときは、単にドットで繋げば良い。</p><p> もちろんasも使える。</p> <pre class="code lang-lisp" data-lang="lisp" data-unlink><span class="synSpecial">(</span><span class="synStatement">import</span> [numpy :as np]<span class="synSpecial">)</span> </pre><p> これでimport numpy as npと等価である。</p><p> 外側のカクカッコには一つのモジュールしか書けないが、内側の子モジュールのカッコには複数書けるようである。</p> <pre class="code lang-lisp" data-lang="lisp" data-unlink>=&gt; <span class="synSpecial">(</span><span class="synStatement">import</span> [os [listdir name]]<span class="synSpecial">)</span> </pre><p> また、一つのimport関数で複数のモジュールからimportすることも可能。</p> <pre class="code lang-lisp" data-lang="lisp" data-unlink><span class="synSpecial">(</span><span class="synStatement">import</span> [numpy :as np] [scipy :as sp]<span class="synSpecial">)</span> </pre> </div> <div class="section"> <h3>代入</h3> <p> 最初に出したが、setvがpythonの代入と等価である。</p> <pre class="code lang-lisp" data-lang="lisp" data-unlink>=&gt; <span class="synSpecial">(</span>setv hoge <span class="synConstant">10</span><span class="synSpecial">)</span> =&gt; hoge <span class="synConstant">10</span> </pre><p> setv自身は値を返さない(強いて言えばNoneを返す)。pythonの代入文にそのままコンバートされる都合上そうなっている。なんだかlisp的には妥協した仕様に見える。</p> </div> <div class="section"> <h3>組み込みコレクション型</h3> <p> pythonの組み込みコレクション型は使える(はずである)。表記もほぼpythonと同様だが、カンマがないのが違い。ただしreplはカンマ付きのpython表記を返してくる。ご愛嬌である。</p> <pre class="code lang-lisp" data-lang="lisp" data-unlink>=&gt; [<span class="synConstant">1</span> <span class="synConstant">2</span> <span class="synConstant">3</span> <span class="synConstant">4</span>] [<span class="synConstant">1</span>, <span class="synConstant">2</span>, <span class="synConstant">3</span>, <span class="synConstant">4</span>] =&gt; {<span class="synConstant">1</span> <span class="synConstant">2</span> <span class="synConstant">3</span> <span class="synConstant">4</span>} # 恐ろしいことにsetのつもりで書くとdictになる {<span class="synConstant">1</span>: <span class="synConstant">2</span>, <span class="synConstant">3</span>: <span class="synConstant">4</span>} =&gt; <span class="synSpecial">(</span><span class="synStatement">set</span> [1 <span class="synConstant">2</span> <span class="synConstant">3</span> <span class="synConstant">4</span>]<span class="synSpecial">)</span> # setにしたい場合 {<span class="synConstant">1</span>, <span class="synConstant">2</span>, <span class="synConstant">3</span>, <span class="synConstant">4</span>} =&gt; <span class="synSpecial">(</span>tuple [1 <span class="synConstant">2</span> <span class="synConstant">3</span> <span class="synConstant">4</span>]<span class="synSpecial">)</span> # tupleも同様に対応するとこうなる <span class="synSpecial">(</span><span class="synConstant">1</span>, <span class="synConstant">2</span>, <span class="synConstant">3</span>, <span class="synConstant">4</span><span class="synSpecial">)</span> =&gt; <span class="synSpecial">(</span>, <span class="synConstant">1</span> <span class="synConstant">2</span> <span class="synConstant">3</span> <span class="synConstant">4</span><span class="synSpecial">)</span> # 不思議なtupleの記法がある <span class="synSpecial">(</span><span class="synConstant">1</span>, <span class="synConstant">2</span>, <span class="synConstant">3</span>, <span class="synConstant">4</span><span class="synSpecial">)</span> </pre><p> 記法がlispとpythonで混ざる欠点がある。一応問題にならないよう努力はしていると思うが、わかりやすい構文かと言われると微妙。hyの割と大きな弱点だと思う。</p> </div> <div class="section"> <h3>条件分岐</h3> <p> 条件分岐はifとcondがある。ifの書式は次の通り(公式チュートリアルから引用)。</p> <pre class="code lang-lisp" data-lang="lisp" data-unlink><span class="synSpecial">(</span><span class="synStatement">if</span> <span class="synSpecial">(</span>try-some-thing<span class="synSpecial">)</span> <span class="synSpecial">(</span><span class="synStatement">print</span> <span class="synConstant">&quot;this is if true&quot;</span><span class="synSpecial">)</span> <span class="synSpecial">(</span><span class="synStatement">print</span> <span class="synConstant">&quot;this is if false&quot;</span><span class="synSpecial">))</span> </pre><p> lispのifを知っている人には違和感がないと思う。知らない人はpythonの三項演算子だと思えば良い。というか、恐らく三項演算子そのものにコンバートされる。</p><p> 三項演算子なのでif関数自体に返り値がある。ということはpythonのif文より使いみちは多い。</p><p> condもlispでは典型的なもの。これもチュートリアルから例を借用する。</p> <pre class="code lang-lisp" data-lang="lisp" data-unlink><span class="synSpecial">(</span>setv somevar <span class="synConstant">33</span><span class="synSpecial">)</span> <span class="synSpecial">(</span><span class="synStatement">cond</span> [<span class="synSpecial">(</span><span class="synStatement">&gt;</span> somevar <span class="synConstant">50</span><span class="synSpecial">)</span> <span class="synSpecial">(</span><span class="synStatement">print</span> <span class="synConstant">&quot;That variable is too big!&quot;</span><span class="synSpecial">)</span>] [<span class="synSpecial">(</span><span class="synStatement">&lt;</span> somevar <span class="synConstant">10</span><span class="synSpecial">)</span> <span class="synSpecial">(</span><span class="synStatement">print</span> <span class="synConstant">&quot;That variable is too small!&quot;</span><span class="synSpecial">)</span>] [True <span class="synSpecial">(</span><span class="synStatement">print</span> <span class="synConstant">&quot;That variable is jussssst right!&quot;</span><span class="synSpecial">)</span>]<span class="synSpecial">)</span> </pre><p> ま、lisp知ってる人には説明するまでもないし、知らない人はこんなページだけで勉強するなんて無謀なことはしてないはずなので、これといって説明はしない。あと、condも当然値を返す。</p> </div> <div class="section"> <h3>ループ構文</h3> <p> pythonのforとwhileがそのまま使える。まずfor。</p> <pre class="code lang-lisp" data-lang="lisp" data-unlink>=&gt; <span class="synSpecial">(</span>for [c <span class="synConstant">&quot;hoge&quot;</span>] <span class="synSpecial">(</span><span class="synStatement">print</span> c<span class="synSpecial">))</span> h o g e </pre><p> カクカッコに囲まれている部分がpythonで言うところのfor c in "hoge"のc in "hoge"に相当する。その次がbodyで、bodyは複数の関数を書けば逐次実行される。</p> <pre class="code lang-lisp" data-lang="lisp" data-unlink>=&gt; <span class="synSpecial">(</span>for [c <span class="synConstant">&quot;hoge&quot;</span>] <span class="synSpecial">(</span><span class="synStatement">print</span> c<span class="synSpecial">)</span> <span class="synSpecial">(</span><span class="synStatement">print</span> <span class="synSpecial">(</span><span class="synStatement">+</span> c <span class="synConstant">&quot;!&quot;</span><span class="synSpecial">)))</span> h h! o o! g g! e e! </pre><p> zipなどもpythonと同様に使える。構文がちょっとだけ変わるけど。</p> <pre class="code lang-lisp" data-lang="lisp" data-unlink>=&gt; <span class="synSpecial">(</span>for [[c1 c2] <span class="synSpecial">(</span>zip <span class="synConstant">&quot;hoge&quot;</span> <span class="synConstant">&quot;fuga&quot;</span><span class="synSpecial">)</span>] <span class="synSpecial">(</span><span class="synStatement">print</span> c1 c2<span class="synSpecial">))</span> h f o u g g e a </pre><p> 組み込み関数などはだいたい使える。hyはlispの皮をかぶったpythonに過ぎない。</p><p> whileも直感的なものである。(while condition *body)というフォーマットで書けば良い。</p> <pre class="code lang-lisp" data-lang="lisp" data-unlink>=&gt; <span class="synSpecial">(</span>setv i <span class="synConstant">0</span><span class="synSpecial">)</span> =&gt; <span class="synSpecial">(</span>while <span class="synSpecial">(</span><span class="synStatement">&lt;</span> i <span class="synConstant">10</span><span class="synSpecial">)</span> <span class="synSpecial">(</span><span class="synStatement">print</span> i<span class="synSpecial">)</span> <span class="synSpecial">(</span>+= i <span class="synConstant">1</span><span class="synSpecial">))</span> <span class="synConstant">0</span> <span class="synConstant">1</span> <span class="synConstant">2</span> <span class="synConstant">3</span> <span class="synConstant">4</span> <span class="synConstant">5</span> <span class="synConstant">6</span> <span class="synConstant">7</span> <span class="synConstant">8</span> <span class="synConstant">9</span> </pre><p> 新しくインクリメント演算子が登場した。このインクリメント演算子はNoneを返す。どうやらhyはpythonの文を片っ端からNoneを返す関数に変換して作ったlispらしい(for, whileともに返り値はNone)。やはりlispの皮をかぶったpythonに過ぎないという気がする。</p> </div> <div class="section"> <h3>関数定義</h3> <p> defnを使う。書式は(defn function-name [*args] *body)。</p> <pre class="code lang-lisp" data-lang="lisp" data-unlink>=&gt; <span class="synSpecial">(</span>defn str_hoge [string] <span class="synSpecial">(</span>setv s <span class="synSpecial">(</span><span class="synStatement">+</span> <span class="synStatement">string</span> <span class="synConstant">&quot;hoge&quot;</span><span class="synSpecial">))</span> <span class="synSpecial">(</span><span class="synStatement">print</span> s<span class="synSpecial">))</span> =&gt; <span class="synSpecial">(</span>str_hoge <span class="synConstant">&quot;fuga&quot;</span><span class="synSpecial">)</span> fugahoge </pre><p> ちなみにlisp的にはアンダーバーではなくハイフンを使うのが正しいらしい。また、hyはアンダーバー区切りとハイフン区切りを勝手に相互変換するので、str-hogeでも同じ関数が呼べる。python使い的には気持ち悪いので、とりあえずアンダーバー区切りで説明している。</p><p> また、関数オブジェクトはfnで作れる。これを利用してsetvで関数定義することも可能(pythonのlambda文を変数に代入することに相当。ただしhyでは複数行書いても良い)。</p> <pre class="code lang-lisp" data-lang="lisp" data-unlink>=&gt; <span class="synSpecial">(</span>setv str_hoge2 <span class="synSpecial">(</span>fn [string] <span class="synSpecial">(</span>setv s <span class="synSpecial">(</span><span class="synStatement">+</span> <span class="synStatement">string</span> <span class="synConstant">&quot;hoge&quot;</span><span class="synSpecial">))</span> <span class="synSpecial">(</span><span class="synStatement">print</span> s<span class="synSpecial">)))</span> =&gt; <span class="synSpecial">(</span>str_hoge2 <span class="synConstant">&quot;fuga&quot;</span><span class="synSpecial">)</span> fugahoge </pre><p> キーワード引数なども指定できる。lispでいうラムダリスト。</p> <pre class="code lang-lisp" data-lang="lisp" data-unlink>=&gt; <span class="synSpecial">(</span>defn piyo [str1 <span class="synType">&amp;optional</span> [str2 <span class="synConstant">&quot;piyo&quot;</span>]] <span class="synSpecial">(</span><span class="synStatement">print</span> <span class="synSpecial">(</span><span class="synStatement">+</span> str1 str2<span class="synSpecial">)))</span> =&gt; <span class="synSpecial">(</span>piyo <span class="synConstant">&quot;hoge&quot;</span><span class="synSpecial">)</span> hogepiyo =&gt; <span class="synSpecial">(</span>piyo <span class="synConstant">&quot;hoge&quot;</span> :str2 <span class="synConstant">&quot;fuga&quot;</span><span class="synSpecial">)</span> hogefuga </pre><p> 他に可変長引数、可変キーワード引数に対応した&restや&kwargsもあるが、使う機会は少ないので説明は省く。</p> </div> <div class="section"> <h3>オブジェクト指向</h3> <p> pythonはオブジェクト指向言語である。よってlispの皮を被ったpythonであるhyもオブジェクト指向言語である。</p><p> hyのオブジェクト指向はあまり深く考えずとも、直感的に使える。たとえばクラス定義してクラスメソッドを呼ぶというよくある処理。</p> <pre class="code lang-lisp" data-lang="lisp" data-unlink>=&gt; <span class="synSpecial">(</span><span class="synStatement">defclass</span> X [] ... <span class="synSpecial">(</span>defn __init__ [self x] <span class="synSpecial">(</span>setv self.x x<span class="synSpecial">))</span> ... <span class="synSpecial">(</span>defn <span class="synStatement">print</span> [self] <span class="synSpecial">(</span><span class="synStatement">print</span> self.x<span class="synSpecial">)))</span> =&gt; <span class="synSpecial">(</span>setv x <span class="synSpecial">(</span>X <span class="synConstant">&quot;xxx&quot;</span><span class="synSpecial">))</span> =&gt; <span class="synSpecial">(</span>x.print<span class="synSpecial">)</span> xxx </pre><p> 簡単そうである。クラスのインスタンス化がちょっと気持ち悪いと思うかもしれないが、クラス名は実質的にコンストラクタへの参照だと思えばそんなにキモくない。</p><p> ただし、python風の(普通のオブジェクト指向言語風の)ドット記法が使えるのは変数に束縛されたオブジェクトに対してだけである。</p> <pre class="code lang-lisp" data-lang="lisp" data-unlink>=&gt; <span class="synSpecial">(</span><span class="synConstant">&quot;hoge hoge&quot;</span>.split<span class="synSpecial">)</span> File <span class="synConstant">&quot;&lt;input&gt;&quot;</span>, line <span class="synConstant">1</span>, column <span class="synConstant">13</span> <span class="synSpecial">(</span><span class="synConstant">&quot;hoge hoge&quot;</span>.split<span class="synSpecial">)</span> ^-----^ HyTypeError: cannot <span class="synStatement">access</span> attribute on anything other than a name <span class="synSpecial">(</span>in order to <span class="synStatement">get</span> attributes of expressions, use <span class="synPreProc">`(</span>. &lt;expression&gt; split<span class="synPreProc">)</span>` <span class="synStatement">or</span> <span class="synPreProc">`(</span>.split &lt;expression&gt;<span class="synPreProc">)</span>`<span class="synSpecial">)</span> </pre><p> 親切なことにエラーメッセージがなすべきことを教えてくれる。</p> <pre class="code lang-lisp" data-lang="lisp" data-unlink>=&gt; <span class="synSpecial">((</span>. <span class="synConstant">&quot;hoge hoge&quot;</span> split<span class="synSpecial">))</span> [<span class="synSpecial">'</span><span class="synIdentifier">hoge</span><span class="synSpecial">'</span><span class="synIdentifier">,</span> <span class="synSpecial">'</span><span class="synIdentifier">hoge</span><span class="synSpecial">'</span><span class="synIdentifier">]</span> =&gt; <span class="synSpecial">(</span>.split <span class="synConstant">&quot;hoge hoge&quot;</span><span class="synSpecial">)</span> [<span class="synSpecial">'</span><span class="synIdentifier">hoge</span><span class="synSpecial">'</span><span class="synIdentifier">,</span> <span class="synSpecial">'</span><span class="synIdentifier">hoge</span><span class="synSpecial">'</span><span class="synIdentifier">]</span> </pre><p> 上の書き方だとsplitの関数オブジェクトが得られるので、呼び出すにはもう一回カッコでくくってやる必要がある。下の書き方は直接呼べる。</p> </div> <div class="section"> <h3>スライス・辞書などのキー参照</h3> <p> getという関数があり、(get a 0)はpythonのa[0]と等価である。次のように使うことができる。</p> <pre class="code lang-lisp" data-lang="lisp" data-unlink>=&gt; <span class="synSpecial">(</span>setv l [1 <span class="synConstant">2</span> <span class="synConstant">3</span>]<span class="synSpecial">)</span> =&gt; <span class="synSpecial">(</span><span class="synStatement">get</span> l <span class="synConstant">0</span><span class="synSpecial">)</span> <span class="synConstant">1</span> =&gt; <span class="synSpecial">(</span>setv d {1 <span class="synConstant">10</span> <span class="synConstant">2</span> <span class="synConstant">20</span>}<span class="synSpecial">)</span> =&gt; d {<span class="synConstant">1</span>: <span class="synConstant">10</span>, <span class="synConstant">2</span>: <span class="synConstant">20</span>} =&gt; <span class="synSpecial">(</span><span class="synStatement">get</span> d <span class="synConstant">1</span><span class="synSpecial">)</span> <span class="synConstant">10</span> =&gt; <span class="synSpecial">(</span>setv <span class="synSpecial">(</span><span class="synStatement">get</span> d <span class="synConstant">3</span><span class="synSpecial">)</span> <span class="synConstant">30</span><span class="synSpecial">)</span> =&gt; d {<span class="synConstant">1</span>: <span class="synConstant">10</span>, <span class="synConstant">2</span>: <span class="synConstant">20</span>, <span class="synConstant">3</span>: <span class="synConstant">30</span>} </pre> </div> <div class="section"> <h3>リスト内包表記</h3> <p> pythonのリスト内包表記がちゃんとhyにもある。安心して欲しい。次のように書く(チュートリアルの例)。</p> <pre class="code lang-lisp" data-lang="lisp" data-unlink><span class="synSpecial">(</span>setv odds-squared <span class="synSpecial">(</span>list-comp <span class="synSpecial">(</span>pow num <span class="synConstant">2</span><span class="synSpecial">)</span> <span class="synSpecial">(</span>num <span class="synSpecial">(</span>range <span class="synConstant">100</span><span class="synSpecial">))</span> <span class="synSpecial">(</span><span class="synStatement">=</span> <span class="synSpecial">(</span>% num <span class="synConstant">2</span><span class="synSpecial">)</span> <span class="synConstant">1</span><span class="synSpecial">)))</span> <span class="synComment">; And, an example stolen shamelessly from a Clojure page:</span> <span class="synComment">; Let's list all the blocks of a Chessboard:</span> <span class="synSpecial">(</span>list-comp <span class="synSpecial">(</span>, x y<span class="synSpecial">)</span> <span class="synSpecial">(</span>x <span class="synSpecial">(</span>range <span class="synConstant">8</span><span class="synSpecial">)</span> y <span class="synConstant">&quot;ABCDEFGH&quot;</span><span class="synSpecial">))</span> <span class="synComment">; [(0, 'A'), (0, 'B'), (0, 'C'), (0, 'D'), (0, 'E'), (0, 'F'), (0, 'G'), (0, 'H'),</span> <span class="synComment">; (1, 'A'), (1, 'B'), (1, 'C'), (1, 'D'), (1, 'E'), (1, 'F'), (1, 'G'), (1, 'H'),</span> <span class="synComment">; (2, 'A'), (2, 'B'), (2, 'C'), (2, 'D'), (2, 'E'), (2, 'F'), (2, 'G'), (2, 'H'),</span> <span class="synComment">; (3, 'A'), (3, 'B'), (3, 'C'), (3, 'D'), (3, 'E'), (3, 'F'), (3, 'G'), (3, 'H'),</span> <span class="synComment">; (4, 'A'), (4, 'B'), (4, 'C'), (4, 'D'), (4, 'E'), (4, 'F'), (4, 'G'), (4, 'H'),</span> <span class="synComment">; (5, 'A'), (5, 'B'), (5, 'C'), (5, 'D'), (5, 'E'), (5, 'F'), (5, 'G'), (5, 'H'),</span> <span class="synComment">; (6, 'A'), (6, 'B'), (6, 'C'), (6, 'D'), (6, 'E'), (6, 'F'), (6, 'G'), (6, 'H'),</span> <span class="synComment">; (7, 'A'), (7, 'B'), (7, 'C'), (7, 'D'), (7, 'E'), (7, 'F'), (7, 'G'), (7, 'H')]</span> </pre><p> 私はチュートリアルを読んでもよくわからなかったので、次の内包表記を自分でhyに変換してみることにした。</p> <pre class="code lang-python" data-lang="python" data-unlink>&gt;&gt;&gt; [[x*y <span class="synStatement">for</span> y <span class="synStatement">in</span> <span class="synIdentifier">range</span>(<span class="synConstant">1</span>,<span class="synConstant">10</span>)] <span class="synStatement">for</span> x <span class="synStatement">in</span> <span class="synIdentifier">range</span>(<span class="synConstant">1</span>,<span class="synConstant">10</span>)] </pre><p> 何の事はない掛け算九九である。</p><p> まず外側を作る。</p> <pre class="code lang-lisp" data-lang="lisp" data-unlink>=&gt; <span class="synSpecial">(</span>list-comp x <span class="synSpecial">(</span>x <span class="synSpecial">(</span>range <span class="synConstant">1</span> <span class="synConstant">10</span><span class="synSpecial">)))</span> [<span class="synConstant">1</span>, <span class="synConstant">2</span>, <span class="synConstant">3</span>, <span class="synConstant">4</span>, <span class="synConstant">5</span>, <span class="synConstant">6</span>, <span class="synConstant">7</span>, <span class="synConstant">8</span>, <span class="synConstant">9</span>] </pre><p> 次に内側を書く。</p> <pre class="code lang-lisp" data-lang="lisp" data-unlink>=&gt; <span class="synSpecial">(</span>list-comp <span class="synSpecial">(</span>list-comp <span class="synSpecial">(</span><span class="synStatement">*</span> x y<span class="synSpecial">)</span> <span class="synSpecial">(</span>y <span class="synSpecial">(</span>range <span class="synConstant">1</span> <span class="synConstant">10</span><span class="synSpecial">)))</span> <span class="synSpecial">(</span>x <span class="synSpecial">(</span>range <span class="synConstant">1</span> <span class="synConstant">10</span><span class="synSpecial">)))</span> [[<span class="synConstant">1</span>, <span class="synConstant">2</span>, <span class="synConstant">3</span>, <span class="synConstant">4</span>, <span class="synConstant">5</span>, <span class="synConstant">6</span>, <span class="synConstant">7</span>, <span class="synConstant">8</span>, <span class="synConstant">9</span>], [<span class="synConstant">2</span>, <span class="synConstant">4</span>, <span class="synConstant">6</span>, <span class="synConstant">8</span>, <span class="synConstant">10</span>, <span class="synConstant">12</span>, <span class="synConstant">14</span>, <span class="synConstant">16</span>, <span class="synConstant">18</span>], [<span class="synConstant">3</span>, <span class="synConstant">6</span>, <span class="synConstant">9</span>, <span class="synConstant">12</span>, <span class="synConstant">15</span>, <span class="synConstant">18</span>, <span class="synConstant">21</span>, <span class="synConstant">24</span>, <span class="synConstant">27</span>], [<span class="synConstant">4</span>, <span class="synConstant">8</span>, <span class="synConstant">12</span>, <span class="synConstant">16</span>, <span class="synConstant">20</span>, <span class="synConstant">24</span>, <span class="synConstant">28</span>, <span class="synConstant">32</span>, <span class="synConstant">36</span>], [<span class="synConstant">5</span>, <span class="synConstant">10</span>, <span class="synConstant">15</span>, <span class="synConstant">20</span>, <span class="synConstant">25</span>, <span class="synConstant">30</span>, <span class="synConstant">35</span>, <span class="synConstant">40</span>, <span class="synConstant">45</span>], [<span class="synConstant">6</span>, <span class="synConstant">12</span>, <span class="synConstant">18</span>, <span class="synConstant">24</span>, <span class="synConstant">30</span>, <span class="synConstant">36</span>, <span class="synConstant">42</span>, <span class="synConstant">48</span>, <span class="synConstant">54</span>], [<span class="synConstant">7</span>, <span class="synConstant">14</span>, <span class="synConstant">21</span>, <span class="synConstant">28</span>, <span class="synConstant">35</span>, <span class="synConstant">42</span>, <span class="synConstant">49</span>, <span class="synConstant">56</span>, <span class="synConstant">63</span>], [<span class="synConstant">8</span>, <span class="synConstant">16</span>, <span class="synConstant">24</span>, <span class="synConstant">32</span>, <span class="synConstant">40</span>, <span class="synConstant">48</span>, <span class="synConstant">56</span>, <span class="synConstant">64</span>, <span class="synConstant">72</span>], [<span class="synConstant">9</span>, <span class="synConstant">18</span>, <span class="synConstant">27</span>, <span class="synConstant">36</span>, <span class="synConstant">45</span>, <span class="synConstant">54</span>, <span class="synConstant">63</span>, <span class="synConstant">72</span>, <span class="synConstant">81</span>]] </pre><p> 仕組みはわかった。わかりやすいかと言われると微妙だが、pythonのリスト内包表記も最初は読みづらく感じたし、慣れればなんとかなるだろう。</p> </div> <div class="section"> <h3>まとめ</h3> <p> 内容は必要を認めれば追加する。</p><p> hyはけっこう楽しい(小並感)。</p> </div> Thu, 01 Mar 2018 10:32:07 +0900 hatenablog://entry/17391345971620822384 python hy lisp Pipeline 【python】hyを触ってみる https://www.haya-programming.com/entry/2018/02/28/112423 <p> なんか、たまにlisp使ってみたくなるんだよね。</p><p> 去年のこれくらいの時期にもcommon lispをやってみた記憶があるが、たぶん言語として悪くはないんだろうけど、ちゃんと書けるようになる前に飽きてしまった<a href="#f-6a8b8ca8" name="fn-6a8b8ca8" title="それからこのブログにも飽きて一年近く放置していたのを、最近になって性懲りもなく再開した訳だが・・・">*1</a>。</p><p> という訳で、今回はpython方言(lisp方言と言うべきか?)のhyを使う。これが何なのかというと、pythonにコンパイルされるlisp。・・・その発想がなかった訳じゃないけど、本当にやるとは思わなかった。</p><p> たぶん前回common lispに飽きてしまったのは「lispしなきゃ」って気張りすぎたのが原因なので、今回はpythonだと思って気楽に書くことにする。その上でpythonで書くのと比較してメリットが見えれば今後何かに使うのもありだし、なければさっさとゴミ箱に放り込むつもりである。</p><p> 公式サイト<br /> <a href="http://docs.hylang.org/en/stable/index.html">Welcome to Hy&rsquo;s documentation! &mdash; hy 0.15.0 documentation</a><br /> </p> <div class="section"> <h3>試す</h3> <p> hyのチュートリアルを一通り読んで「雰囲気はわかったけど、実際のプログラムがこんなので書けるの?」と思った。そこで簡単なプログラムをまずpythonで書いた。</p> <pre class="code lang-python" data-lang="python" data-unlink><span class="synComment"># coding: UTF-8</span> <span class="synPreProc">import</span> numpy <span class="synStatement">as</span> np <span class="synPreProc">from</span> sklearn.datasets <span class="synPreProc">import</span> load_iris <span class="synPreProc">from</span> sklearn.svm <span class="synPreProc">import</span> SVC <span class="synPreProc">from</span> sklearn.model_selection <span class="synPreProc">import</span> StratifiedKFold <span class="synPreProc">from</span> sklearn.metrics <span class="synPreProc">import</span> precision_recall_fscore_support <span class="synStatement">as</span> prf <span class="synStatement">def</span> <span class="synIdentifier">main</span>(): iris = load_iris() svm = SVC(C=<span class="synConstant">5</span>, gamma=<span class="synConstant">0.01</span>) trues = [] preds = [] <span class="synStatement">for</span> train_index, test_index <span class="synStatement">in</span> StratifiedKFold().split( iris.data, iris.target): svm.fit(iris.data[train_index], iris.target[train_index]) trues.append(iris.target[test_index]) preds.append(svm.predict(iris.data[test_index])) scores = prf(np.hstack(trues), np.hstack(preds), average=<span class="synConstant">&quot;macro&quot;</span>) <span class="synIdentifier">print</span>(<span class="synConstant">&quot;p:{0:.6f} r:{1:.6f} f1:{2:.6f}&quot;</span>.<span class="synIdentifier">format</span>( scores[<span class="synConstant">0</span>], scores[<span class="synConstant">1</span>], scores[<span class="synConstant">2</span>])) <span class="synStatement">if</span> __name__ == <span class="synConstant">&quot;__main__&quot;</span>: main() </pre><p> sklearnのsvmでirisを分類する。たった26行の、機械学習初心者が書いたみたいなコードだ。ちなみに0.96くらいのF1値になった(irisってつくづくちょろい奴だな)。</p><p> これを一行ずつhyに翻訳していく。まあこの程度の内容が書ければなんとか実用にはなるだろう、というつもりである。</p><p> ちなみに、公式ドキュメント以外で以下のページを参考にさせて頂きました。</p><p><a href="https://masatoi.github.io/2017/05/11/hy-tutorial">&#x30BC;&#x30ED;&#x304B;&#x3089;&#x59CB;&#x3081;&#x308B;Hy(hylang)</a><br /> <a href="https://masatoi.github.io/2017/05/17/hy-version-of-chainer-mnist-example">Chainer&#x306E;MNIST&#x306E;&#x4F8B;&#x3092;Hy&#x306B;&#x7FFB;&#x8A33;&#x3057;&#x3066;&#x307F;&#x308B;</a><br /> <a href="https://qiita.com/t-sin/items/07ac1c7e413b43f8b6a8">Python&#x3067;&#x66F8;&#x304B;&#x308C;&#x305F;&#x30A4;&#x30AB;&#x3057;&#x305F;Lisp: Hy - Qiita</a><br /> <a href="https://qiita.com/koji-kojiro/items/94c3bd97e1361557f7b9">Pythonista&#x306E;&#x305F;&#x3081;&#x306E;Lisp&#x5165;&#x9580;&#xFF08;Hy&#x30C1;&#x30E5;&#x30FC;&#x30C8;&#x30EA;&#x30A2;&#x30EB;&#x548C;&#x8A33;&#xFF09; - Qiita</a><br /> </p> <div class="section"> <h4>encoding指定</h4> <p> マジックコメントはしょせんコメントなのでコメントとして書けば良いらしい(こう書いてるところがあった)。</p> <pre class="code lang-lisp" data-lang="lisp" data-unlink><span class="synComment">;; -*- coding:utf-8; mode:hy -*-</span> </pre><p> でもこれ、emacsに認識させるだけなのでは? sjisとかにしたらちゃんとhy側で認識するのかしら。わざわざ試す必要は感じないけど。</p><p> あとhy-modeは入れた。</p> </div> <div class="section"> <h4>import</h4> <p> こうなりました。</p> <pre class="code lang-lisp" data-lang="lisp" data-unlink><span class="synSpecial">(</span><span class="synStatement">import</span> [numpy :as np] [sklearn.datasets [load_iris]] [sklearn.svm [SVC]] [sklearn.model_selection [StratifiedKFold :as SKF]] [sklearn.metrics [precision_recall_fscore_support :as prf]]<span class="synSpecial">)</span>  </pre><p> 階層構造をカッコで表現する、理にかなってるような、カッコいいような、キモいような、微妙な感じ。</p> </div> <div class="section"> <h4>main関数</h4> <p> なんかそれ用のマクロ(defmain)もあるっぽいが、便利マクロに頼り切るのもどうかと思ってifで素直に書いてみる。とりあえずhogeるmain関数を作成。</p> <pre class="code lang-lisp" data-lang="lisp" data-unlink><span class="synSpecial">(</span>defn main [] <span class="synSpecial">(</span><span class="synStatement">print</span> <span class="synConstant">&quot;hoge&quot;</span><span class="synSpecial">))</span> <span class="synSpecial">(</span><span class="synStatement">if</span> <span class="synSpecial">(</span><span class="synStatement">=</span> __name__ <span class="synConstant">&quot;__main__&quot;</span><span class="synSpecial">)</span> <span class="synSpecial">(</span>main<span class="synSpecial">))</span> </pre><p> そういや=の比較演算子なんだけど、eq、eq?、equal?、==の順に試してぜんぶダメで、最後に試した=が正解だった。勘弁してくれ。</p><p> で、hy2pyでpythonコードに変換してみた結果は、</p> <pre class="code lang-python" data-lang="python" data-unlink><span class="synStatement">def</span> <span class="synIdentifier">main</span>(): <span class="synStatement">return</span> <span class="synIdentifier">print</span>(<span class="synConstant">'hoge'</span>) main() <span class="synStatement">if</span> __name__ == <span class="synConstant">'__main__'</span> <span class="synStatement">else</span> <span class="synIdentifier">None</span> </pre><p> なにこれ。めっちゃウケる。「main() if __name__ == '__main__' else None」とか、まさか条件演算子にされるとは思わなかった(確かにその方がlispのifの機能を考えると自然だが)。この書き方にはけっこう「萌えた」ので、友達に渡したりするpythonコードで今後使っていきたい。</p> </div> <div class="section"> <h4>残り</h4> <p> あとは淡々とググりながら(グダりながら)書いただけだった。</p><p> ハマったポイント(最初わからなかったこと)としては、</p> <ul> <li>クラスからオブジェクトのインスタンスを作るときは「オブジェクトのインスタンスを返す関数呼び出し」だと思ってやればできる</li> <li>リストのスライスはgetで書く</li> <li>シンボルに束縛していないインスタンスのメソッドは「(. instance method-name)」で呼ぶ(これで関数オブジェクトが返るので、もう一回外にカッコを付けると呼び出せる)</li> </ul><p> あたり。後はなんか適当にドキュメント通りに書くとか、適当にpythonの記法で書いても(ドットとか)受け付けるので、そんなに苦労はしなかった。</p> </div> </div> <div class="section"> <h3>完成したもの</h3> <p> もしかしたら変なところがあるかもしれないが、とりあえず動きはした。</p> <pre class="code lang-lisp" data-lang="lisp" data-unlink><span class="synComment">;; -*- coding:utf-8; mode:hy -*-</span> <span class="synSpecial">(</span><span class="synStatement">import</span> [numpy :as np] [sklearn.datasets [load_iris]] [sklearn.svm [SVC]] [sklearn.model_selection [StratifiedKFold :as SKF train_test_split]] [sklearn.metrics [precision_recall_fscore_support :as prf]]<span class="synSpecial">)</span> <span class="synSpecial">(</span>defn main [] <span class="synSpecial">(</span>setv iris <span class="synSpecial">(</span>load_iris<span class="synSpecial">))</span> <span class="synSpecial">(</span>setv svm <span class="synSpecial">(</span>SVC :C <span class="synConstant">5</span> :gamma <span class="synConstant">0.01</span><span class="synSpecial">))</span> <span class="synSpecial">(</span>setv trues []<span class="synSpecial">)</span> <span class="synSpecial">(</span>setv preds []<span class="synSpecial">)</span> <span class="synSpecial">(</span>for [[train_index test_index] <span class="synSpecial">((</span>. <span class="synSpecial">(</span>SKF<span class="synSpecial">)</span> split<span class="synSpecial">)</span> iris.data iris.target<span class="synSpecial">)</span>] <span class="synSpecial">(</span>svm.fit <span class="synSpecial">(</span><span class="synStatement">get</span> iris.data train_index<span class="synSpecial">)</span> <span class="synSpecial">(</span><span class="synStatement">get</span> iris.target train_index<span class="synSpecial">))</span> <span class="synSpecial">(</span>trues.append <span class="synSpecial">(</span><span class="synStatement">get</span> iris.target test_index<span class="synSpecial">))</span> <span class="synSpecial">(</span>preds.append <span class="synSpecial">(</span>svm.predict <span class="synSpecial">(</span><span class="synStatement">get</span> iris.data test_index<span class="synSpecial">))))</span> <span class="synSpecial">(</span>setv scores <span class="synSpecial">(</span>prf <span class="synSpecial">(</span>np.hstack trues<span class="synSpecial">)</span> <span class="synSpecial">(</span>np.hstack preds<span class="synSpecial">)</span> :average <span class="synConstant">&quot;macro&quot;</span><span class="synSpecial">))</span> <span class="synSpecial">(</span><span class="synStatement">print</span> <span class="synSpecial">((</span>. <span class="synConstant">&quot;p:{0:.6f} r:{1:.6f} f1:{2:.6f}&quot;</span> <span class="synStatement">format</span><span class="synSpecial">)</span> <span class="synSpecial">(</span><span class="synStatement">get</span> scores <span class="synConstant">0</span><span class="synSpecial">)</span> <span class="synSpecial">(</span><span class="synStatement">get</span> scores <span class="synConstant">1</span><span class="synSpecial">)</span> <span class="synSpecial">(</span><span class="synStatement">get</span> scores <span class="synConstant">2</span><span class="synSpecial">))))</span> <span class="synSpecial">(</span><span class="synStatement">if</span> <span class="synSpecial">(</span><span class="synStatement">=</span> __name__ <span class="synConstant">&quot;__main__&quot;</span><span class="synSpecial">)</span> <span class="synSpecial">(</span>main<span class="synSpecial">))</span> </pre><p> 何回か実行したところ、心なしかpython版よりF1値が高めになった気がする(冗談です)。</p> </div> <div class="section"> <h3>感想</h3> <p> スライスをS式で書けないせいで、どうやっても冗長になるね、というのが率直な感想。まあ、こればっかりはしょうがないだろう。</p><p> 良かったところは・・・意外となんとかなったところかな。lisp特有の機能は特に使わなかったので、事実上pythonから構文をトランスレートしただけだが、とりあえず書けない構文はなかった。正直どこかで素直には書き写せない場所が出てきてもおかしくないと思ってたけど(numpy配列のファンシーインデックスとか)、とりあえず詰みはしなかった。でもnumpy配列の列抽出とか、できるのだろうか・・・。</p><p> あと、「これはlispだから、インスタンスメソッドだろうがなんだろうが、どんな処理だってしょせんは関数オブジェクトの呼び出しなんだ」と思って書いたので、初心者でもそんなに戸惑わなかった。文法が単純なのは素晴らしい。二週間もやればだいたい書けるようになると思う。</p><p> 悪かったところは、残念ながらいろいろ思いつく。</p> <ul> <li>冗長(python比で)</li> </ul><p> これはしょうがない。許す。</p> <ul> <li>支援ツール(flymakeやjedi相当のもの)がなさそう</li> </ul><p> これもしょうがないんだけど・・・マイナー言語って不利だよね。</p> <ul> <li>hyインタプリタの吐くエラーメッセージが黄色かった)</li> </ul><p> いつも使ってる開発環境(emacsのシェル)では凄いどぎつい色で表示された。これはしょうがなくない。なんで黄色にしたんだよ。全然読めねえんだけど。</p><p> 使えなくはないんだけど、日常的にnumpyとかsklearn走らせるのに使うかと言われると・・・って感じだな。でもpython環境上にいる強みはそのへんが使えることだと思うので、けっきょく存在意義がよくわからなくなってくる。</p><p> まあ、「lispで書いた方が楽」な処理<a href="#f-871e87b7" name="fn-871e87b7" title="真っ先に思い浮かんだのはS式のパースだった。あと構文解析器とか言語処理系、探索とか自動証明みたいな辺りは歴史的に使われてきたので、書きやすいのだろうか?">*2</a>をpythonで書きたくなったらその部分だけhyで書くとか、あるいはpython使いが趣味のlispの勉強に使うとか、ネタとして愛でるとか、そういう用途で使うものだな、これは。</p><p> やるメリットがあるのかどうかは微妙な感じだけど、関数型というか宣言型言語は面白そうと思ってるので、なんか強みを活かせるような感じでちょくちょくいじっていきたい。prologとかもやってみたいし<a href="#f-0db40812" name="fn-0db40812" title="いずれ気が向いたらhyでprologインタプリタでも書いてみようかしら">*3</a>。</p> </div><div class="footnote"> <p class="footnote"><a href="#fn-6a8b8ca8" name="f-6a8b8ca8" class="footnote-number">*1</a><span class="footnote-delimiter">:</span><span class="footnote-text">それからこのブログにも飽きて一年近く放置していたのを、最近になって性懲りもなく再開した訳だが・・・</span></p> <p class="footnote"><a href="#fn-871e87b7" name="f-871e87b7" class="footnote-number">*2</a><span class="footnote-delimiter">:</span><span class="footnote-text">真っ先に思い浮かんだのはS式のパースだった。あと構文解析器とか言語処理系、探索とか自動証明みたいな辺りは歴史的に使われてきたので、書きやすいのだろうか?</span></p> <p class="footnote"><a href="#fn-0db40812" name="f-0db40812" class="footnote-number">*3</a><span class="footnote-delimiter">:</span><span class="footnote-text">いずれ気が向いたらhyでprologインタプリタでも書いてみようかしら</span></p> </div> Wed, 28 Feb 2018 11:24:23 +0900 hatenablog://entry/17391345971620502049 python hy lisp 機械学習 【common lisp】common lispでn-gram https://www.haya-programming.com/entry/2017/03/12/065329 <p> 趣味でCommon Lispを始めました。とりあえず練習にn-gramを書いてみました。</p><p> 書き方は色々あると思いますが、ループ構文がまだいまいちわからないので再帰で書きます。</p> <pre class="code lang-lisp" data-lang="lisp" data-unlink><span class="synSpecial">(</span><span class="synStatement">defun</span> rec-ngram <span class="synSpecial">(</span><span class="synStatement">list</span> n <span class="synType">&amp;optional</span> <span class="synSpecial">(</span>ret-list <span class="synSpecial">'()))</span> <span class="synSpecial">(</span><span class="synStatement">if</span> <span class="synSpecial">(</span><span class="synStatement">eq</span> <span class="synSpecial">(</span><span class="synStatement">length</span> <span class="synStatement">list</span><span class="synSpecial">)</span> <span class="synSpecial">(</span><span class="synStatement">-</span> n <span class="synConstant">1</span><span class="synSpecial">))</span> ret-list <span class="synSpecial">(</span>rec-ngram <span class="synSpecial">(</span><span class="synStatement">cdr</span> <span class="synStatement">list</span><span class="synSpecial">)</span> n <span class="synSpecial">(</span><span class="synStatement">nconc</span> ret-list <span class="synSpecial">(</span><span class="synStatement">list</span> <span class="synSpecial">(</span><span class="synStatement">subseq</span> <span class="synStatement">list</span> <span class="synConstant">0</span> n<span class="synSpecial">))))))</span> </pre><p> いけてる書き方なのかどうかは良くわかりませんが、そう複雑なものではありません。末尾再帰で書いていますが、末尾再帰でなくて良いならもっと簡単に書けます。</p><p> 使い方はこんな感じ。</p> <pre class="code" data-lang="" data-unlink>* (rec-ngram &#39;(&#34;吾輩&#34; &#34;は&#34; &#34;猫&#34; &#34;で&#34; &#34;ある&#34; &#34;。&#34;) 2) ((&#34;吾輩&#34; &#34;は&#34;) (&#34;は&#34; &#34;猫&#34;) (&#34;猫&#34; &#34;で&#34;) (&#34;で&#34; &#34;ある&#34;) (&#34;ある&#34; &#34;。&#34;))</pre><p> けど、毎回listの長さを計算するのは馬鹿馬鹿しいと思ったので、最初に全体の長さを計算し、後は1ずつ引いていくことにしました。</p> <pre class="code lang-lisp" data-lang="lisp" data-unlink><span class="synSpecial">(</span><span class="synStatement">defun</span> rec-ngram <span class="synSpecial">(</span><span class="synStatement">list</span> n <span class="synType">&amp;optional</span> <span class="synSpecial">(</span><span class="synStatement">list-length</span> <span class="synStatement">nil</span><span class="synSpecial">)</span> <span class="synSpecial">(</span>ret-list <span class="synSpecial">'()))</span> <span class="synSpecial">(</span><span class="synStatement">if</span> <span class="synSpecial">(</span><span class="synStatement">eq</span> <span class="synStatement">list-length</span> <span class="synStatement">nil</span><span class="synSpecial">)</span> <span class="synSpecial">(</span><span class="synStatement">setq</span> <span class="synStatement">list-length</span> <span class="synSpecial">(</span><span class="synStatement">length</span> <span class="synStatement">list</span><span class="synSpecial">)))</span> <span class="synSpecial">(</span><span class="synStatement">if</span> <span class="synSpecial">(</span><span class="synStatement">eq</span> <span class="synSpecial">(</span><span class="synStatement">length</span> <span class="synStatement">list</span><span class="synSpecial">)</span> <span class="synSpecial">(</span><span class="synStatement">-</span> n <span class="synConstant">1</span><span class="synSpecial">))</span> ret-list <span class="synSpecial">(</span>rec-ngram <span class="synSpecial">(</span><span class="synStatement">cdr</span> <span class="synStatement">list</span><span class="synSpecial">)</span> n <span class="synSpecial">(</span><span class="synStatement">-</span> <span class="synStatement">list-length</span> <span class="synConstant">1</span><span class="synSpecial">)</span> <span class="synSpecial">(</span><span class="synStatement">nconc</span> ret-list <span class="synSpecial">(</span><span class="synStatement">list</span> <span class="synSpecial">(</span><span class="synStatement">subseq</span> <span class="synStatement">list</span> <span class="synConstant">0</span> n<span class="synSpecial">))))))</span> </pre><p> こんな感じでn-gramが作れますが、以前書いた<a href="http://hayataka2049.hatenablog.jp/entry/2017/02/06/215619">python&#x7248;</a>と比べると機能的に見劣りします。</p> <ul> <li>文字列を引数に取れない</li> <li>n-gramの要素がリストで返ってきても嬉しくない(区切り文字列で区切られた文字列の方が嬉しい)</li> </ul><p> とりあえずpython版と同じ機能になるように、適当に関数を増やしてラップしてみます。</p> <pre class="code lang-lisp" data-lang="lisp" data-unlink><span class="synSpecial">(</span><span class="synStatement">defun</span> ngram <span class="synSpecial">(</span>str <span class="synType">&amp;key</span> <span class="synSpecial">(</span>n <span class="synConstant">2</span><span class="synSpecial">)</span> <span class="synSpecial">(</span>splitter <span class="synConstant">&quot;-*-&quot;</span><span class="synSpecial">))</span> <span class="synComment">;文字列が来たら一文字ずつの文字列のリストに変換</span> <span class="synSpecial">(</span><span class="synStatement">if</span> <span class="synSpecial">(</span><span class="synStatement">typep</span> str <span class="synSpecial">'</span><span class="synIdentifier">string</span><span class="synSpecial">)</span> <span class="synSpecial">(</span><span class="synStatement">setq</span> str <span class="synSpecial">(</span><span class="synStatement">mapcar</span> <span class="synType">#'string</span> <span class="synSpecial">(</span><span class="synStatement">concatenate</span> <span class="synSpecial">'</span><span class="synIdentifier">list</span> str<span class="synSpecial">))))</span> <span class="synComment">;任意の区切り文字でn-gramの結果を連結</span> <span class="synSpecial">(</span><span class="synStatement">let</span> <span class="synSpecial">((</span>ngram-result <span class="synSpecial">(</span>rec-ngram str n<span class="synSpecial">)))</span> <span class="synSpecial">(</span><span class="synStatement">mapcar</span> <span class="synSpecial">(</span><span class="synStatement">lambda</span> <span class="synSpecial">(</span><span class="synStatement">list</span><span class="synSpecial">)</span> <span class="synSpecial">(</span><span class="synStatement">format</span> <span class="synStatement">nil</span> <span class="synSpecial">(</span><span class="synStatement">format</span> <span class="synStatement">nil</span> <span class="synConstant">&quot;~~{~~A~~^~A~~}&quot;</span> splitter<span class="synSpecial">)</span> <span class="synStatement">list</span><span class="synSpecial">))</span> ngram-result<span class="synSpecial">)))</span> </pre><p> nと区切り文字列はキーワード引数にしてみました。list-lengthをこっちの関数で計算する前提にすればrec-ngramのif文を外せますが、面倒くさいのでやっていません。formatを二段重ねにしてる辺りがとても気持ち悪いですが、formatの書式指定がよくわからないので勘弁してください。こんな感じで使えます。</p> <pre class="code" data-lang="" data-unlink>* (ngram &#34;吾輩は猫である。&#34;) (&#34;吾-*-輩&#34; &#34;輩-*-は&#34; &#34;は-*-猫&#34; &#34;猫-*-で&#34; &#34;で-*-あ&#34; &#34;あ-*-る&#34; &#34;る-*-。&#34;) * (ngram &#39;(&#34;吾輩&#34; &#34;は&#34; &#34;猫&#34; &#34;で&#34; &#34;ある&#34; &#34;。&#34;)) (&#34;吾輩-*-は&#34; &#34;は-*-猫&#34; &#34;猫-*-で&#34; &#34;で-*-ある&#34; &#34;ある-*-。&#34;) * (ngram &#34;吾輩は猫である。&#34; :n 3) (&#34;吾-*-輩-*-は&#34; &#34;輩-*-は-*-猫&#34; &#34;は-*-猫-*-で&#34; &#34;猫-*-で-*-あ&#34; &#34;で-*-あ-*-る&#34; &#34;あ-*-る-*-。&#34;) * (ngram &#34;吾輩は猫である。&#34; :n 3 :splitter &#34;!??!&#34;) (&#34;吾!??!輩!??!は&#34; &#34;輩!??!は!??!猫&#34; &#34;は!??!猫!??!で&#34; &#34;猫!??!で!??!あ&#34; &#34;で!??!あ!??!る&#34; &#34;あ!??!る!??!。&#34;)</pre><p> lispは書く分には楽しいですが、pythonと比べるとコード量が多いというか、低水準な印象です。使いこなせてない便利な機能も色々あると思うので、慣れてくればもう少し気楽に書けるようになるとは思います。とりあえず、しばらくは趣味で触っていくことにします。</p> <div class="section"> <h4>追記 2017/03/16</h4> <p> ループでも書きました。</p> <pre class="code lang-lisp" data-lang="lisp" data-unlink><span class="synSpecial">(</span><span class="synStatement">defun</span> loop-ngram <span class="synSpecial">(</span><span class="synStatement">list</span> n<span class="synSpecial">)</span> <span class="synSpecial">(</span><span class="synStatement">let</span> <span class="synSpecial">((</span>result-list <span class="synSpecial">'()))</span> <span class="synSpecial">(</span><span class="synStatement">dotimes</span> <span class="synSpecial">(</span><span class="synStatement">count</span> <span class="synSpecial">(</span><span class="synStatement">-</span> <span class="synSpecial">(</span><span class="synStatement">length</span> <span class="synStatement">list</span><span class="synSpecial">)</span> n <span class="synConstant">-1</span><span class="synSpecial">)</span> result-list<span class="synSpecial">)</span> <span class="synSpecial">(</span><span class="synStatement">setq</span> result-list <span class="synSpecial">(</span><span class="synStatement">cons</span> <span class="synSpecial">(</span><span class="synStatement">subseq</span> <span class="synStatement">list</span> <span class="synStatement">count</span> <span class="synSpecial">(</span><span class="synStatement">+</span> <span class="synStatement">count</span> n<span class="synSpecial">))</span> result-list<span class="synSpecial">)))))</span> </pre><p> シンプルですが、逆向きになって出てきます。</p> <pre class="code" data-lang="" data-unlink>* (loop-ngram &#39;(&#34;吾輩&#34; &#34;は&#34; &#34;猫&#34; &#34;で&#34; &#34;ある&#34; &#34;。&#34;) 2)) ((&#34;ある&#34; &#34;。&#34;) (&#34;で&#34; &#34;ある&#34;) (&#34;猫&#34; &#34;で&#34;) (&#34;は&#34; &#34;猫&#34;) (&#34;吾輩&#34; &#34;は&#34;))</pre><p> 逆向きがいやならreverseするか、最初からnconcでリストを作るだけなので難しいことはありません。</p> </div> Sun, 12 Mar 2017 06:53:29 +0900 hatenablog://entry/10328749687226162587 lisp ngram 主成分分析