近況報告
最近ブログ書いてないなーと思いつつも、特にネタが無くて書けずにいました。
とりあえずここらで近況報告したいと思います。
免許取った
まず3月、普通車MT免許を取得しました。道交法改正直前だったので準中型も一応乗れるっぽいです。
まあ免許取っても車買えるわけじゃないので、あんまり実感は湧かないです。車乗りてーなって気持ちもあるのですが、レンタカー借りてまで遠出する気にもなれず。とりあえず合宿の日程通りに取れたことに安心しました。
英語勉強始めた
英語の読み書き、それと聴き取りとかを毎日やるようにしました。
実家に帰ると集中してコード書く環境を確保できないので、習慣付けは結構すんなりと。大体2週間毎日続ければ癖が付くようです。この機会にミニサイズの英和・和英辞書も買いました。
- 作者: 堀内克明
- 出版社/メーカー: 小学館
- 発売日: 2009/05/13
- メディア: 単行本
- 購入: 1人 クリック: 1回
- この商品を含むブログ (1件) を見る
「読み」はMediumの英語記事とかGitHubのissueとか見て、海外の人が普段使ってるような英語に集中して取り組んでいます。分からない表現とか、よく見かける表現は全部個人用Slackに書いておいて、1日の終わりにEvernoteにログとして残るようにしました。
「書き」は日記みたいな感じで、普段Twitterに呟いたりしてた独り言とか、その日にやったこととかを英語で書いてみるってことをしてます。これも全部Slackに投げてます。
「聴き取り」は、Elevateっていうアプリに頼っています。脳トレアプリみたいなんですが、丁度いい長さのリスニング素材がパッと得られるので継続したい人には良いかもしれません。他にも語彙を増やしたり速読の練習になったり、英語学習に特化してる感じで良いです。継続するぞという自戒も込めて年間課金もしました。僕の財布にとってはそんなに安くは無いですが、学びに金をケチるとその程度の人間になってしまうのでここは思いきって。
今は5月のTOEIC L&Rに向けて勉強していますが、目標はむしろ英語での円滑なコミュニケーションです。チャットにしろ会話にしろ、いちいち辞書開いてたんじゃ疲れますしね。
数学ちゃんとやる
機械学習専攻なので、関連する数学知識は身に付けないとなーと思って地道に勉強してはいたんですが、対象分野の数学だけで本当に良いのか疑問に感じたので、より基礎の部分からやるようにしました。具体的には微積分のε-δ論法から。これは難しいからって講義でも大して触れずに進んでしまい、これで書かれてる定義が読めなくて困っていました。なのでまずはここからやって、次にベクトル解析に進もうと思っています。完全に独学なので、数学が得意な方に道を示していただきたい…。最終的にはカオスとトポロジーに触れたいです。
Servoに貢献するドン
Mozilla主導のServoの開発に参加してみることにしました。もうRustも読み書き出来るようになったので、僕でも出来そうなissueを見つけてassignしてもらいました。コード全体を把握しなくても開発が出来るっていう実感が得られたので、まだ特に何もしていないような状態ですでに感動を味わっています。大規模開発しゅごい…
ところで僕にも出来そうとか書いたんですが、この記事書いてる時点で実は行き詰ってたりします。なので辞書めくりながらissueのとこに質問投げようとしてるところです。英語出来ないの辛い。この件については後ほど、mergeまで行けたら記事にしようかと思ってます。
LT大会やったよ
学内イベントってことでLT大会をぼちぼち主催してまして、先日第4回目が終わったところです。今回は新入生を囲おうと思ってたんですが3人しか来てくれず、見慣れた顔ばっかりでした。まあ継続してやっていける程度には発表があるので、長く続けていけたらなと思います。AWSの無料枠終わる前には引き継ぎたいですけどね。イベントの主催も楽じゃないです。
とまあ、春休み中やったことのまとめでした。あんまりコード書けてなくてちょっと不満です。
実践的とは
どうも、最近自動車学校の教習でヒイヒイ言ってるtamamuです。
なんだかブログの更新間隔が徐々に開いてますね。これはまずいぞ。
と、まあ別にサボっていたわけでは無いのですよ。ブログ以外のとこに文章を書いていたと言いましょうか。
先日、CollegeUnionというコミュが立ち上がりました。
特に目的があるコミュでは無く、Slackっぽいツール(discord)で情報共有したり、知識で殴り合ったりと和気あいあいとした感じでやってます。
一応情報系学生を主体として立ち上がったので、中身は学生が多いようです。とは言っても、プログラミングに心を奪われた野郎共の巣窟なので年齢は関係ありませんね。
で、上のリンクがそうなんですが、このコミュニティにはWikiが存在します。
エンジニアが集まると情報共有をこういうツールでやりたがるんですね(褒めてる)
大概は布教のために言語の入門記事を書いたりしています。現に僕もそうなんですけどね。
僕はCommon Lispが好きなので、CLをソフトウェア開発の選択肢に入れてもらえるようなものを書こうと思いました。
そこで実践入門です。
CL本は結構充実してますし、Web上の入門記事も必要最低限存在していると思います。
しかし実践的な入門をまとめたものはあまり無いような気がしたので、これを書くに至っています。
ここで記事タイトルの話題になりますが、実践的とは何でしょうか。
実践Common Lispという本もありますが、これはプログラミング言語としてのCLの実践がメインです。
CLでのソフトウェア開発の実践は無いのでしょうか?
おそらく探せば無いことはないのでしょうが、ジャンルに特化した記事が点で存在している状況です。
なので、この辺りでまとめ記事っぽいのを作ってもバチは当たらないんじゃないか?と思った次第であります。
何より僕の書く記事の目的は布教です。
他の言語を書いている人に目を向けてもらうにはどうすればよいかを考えた結果、「それ、Lispで出来るよ」を素で言ったものを書くことにしました。
理想は「おいしいClojure入門」のCommon Lisp版となることです。
こういう面白そうなことを集めた感じの記事が1つ2つあるだけでも世間のLispの見方が変わると思っているのですが、どうでしょうか。
何か間違いや付け加えた方が良さそうな文章があれば該当ページの方で修正してくださって構いませんので。
現状出来てる(書きかけ)ページが以下の2つです。
※まさかりを投げて頂ければドンドン僕の頭に突き刺さります
#| 疲れた頭で書いて文章がおかしいかもしれないので後で校正するかも |#
Caveman2でイベント管理システムを構築した話
気づけば1月末です。
あ! あけましておめでとうございます(遅い)
昨年末の話なのですが、Caveman2でイベント管理システムなるものを作っておりました。
現在はAmazon EC2のUbuntuインスタンスで運用していて、ドメインはお名前.comで一番安いやつを選びました。
現物はこれです。
経緯としましては、大学内で非公式にLT大会を運営していて、それの統合的な管理が出来るものが欲しくて作りました。
connpassのグループ機能を切り出してきたみたいな感じです。
もともとは調整さんで出欠管理してたんですが、情報学部生としてそれは雑魚過ぎるなと思ったので自分で作った流れになります。
Caveman2の情報が少ないので、実装の参考になりそうなことをメモ程度に書いておきます。
目次
- データベース連携
- スキーマ
- リモート データベースへの接続
- モデルの書き方
- <form>のデータ受け渡し
- ファイルのアップロード
- ログイン
- 外部サービス連携
データベース連携
スキーマ
(caveman2:make-project)
で作成されるdb/schema.sql
の使い道が不明だったのですが、quickdocs-databaseを見たところ、SQLのテーブルの定義を書いておくものみたいです。自動で生成されるからCaveman2に関係するファイルかと思いましたが、そうではないらしい(データベース周りの知識不足がバレてしまう)
使い方は例の通り、mysqlコマンドでデータベースを指定してリダイレクトしてやります。
$ mysql -uroot -e 'CREATE DATABASE quickdocs DEFAULT CHARACTER SET utf8' $ mysql -uroot quickdocs-database < db/schema.sql
ここはCaveman2関係無い話。
リモート データベースへの接続
AWSのRDSでMySQLサーバーを利用しているので、リモートのデータベースに接続する必要がありました。
以下をsrc/config.lisp
に書いておきます。
(defconfig |production| `(:databases ((:maindb :mysql :host "RDSインスタンスのエンドポイント" :port 1234 :database-name "hoge" :username "foo" :password "bar"))))
この設定を利用する時は以下のように環境変数を設定してアプリケーションを起動します。
$ APP_ENV=production clackup --server :woo --port 8080 app.lisp
モデルの書き方
テーブルごとの処理はそれぞれ1つのファイルにまとめてモデルとして扱ったほうが楽ですし、拡張もしやすいです。
quickdocsの例だとsrc/model/
以下にモジュールとして作成しているのでそれに倣います。
mitoが使えるならそっちを使った方が楽かもしれません。
- src/model/event.lisp
(in-package :cl-user) (defpackage ipu-lt-server.model.event (:use :cl :cl-annot.class :sxql) (:import-from :datafly :model :retrieve-all :retrieve-one :execute) (:import-from :datafly.inflate :octet-vector-to-string) (:import-from :cl-dbi :<dbi-database-error>) ;; ここから (:import-from :ipu-lt-server.model.himg :create-himg :himg-id) (:import-from :ipu-lt-server.model.user :user :user-id)) ;; ここまでは他のモデルのインポート (in-package :ipu-lt-server.model.event) (syntax:use-syntax :annot) @export-accessors @export @model (defstruct (event (:has-many (users user) (select :* (from :user) (where (:= :id (select :user_id (from :participant) (where (:= :event_id id))))) (order-by (:desc :id))))) id name description dates himg-id) @export 'event-users @export (defun create-event (name dates upfile desc) (if (null (dates-validate dates)) nil (let* ((himg (if (null upfile) nil (create-himg (get-universal-time) upfile))) (himgid (if (null himg) nil (himg-id himg)))) (handler-case (progn (execute (insert-into :event (set= :name name :dates (encode-date-str dates) :himg_id himgid :description desc))) (let ((event (retrieve-one (select :* (from :event) (where (:= :name name)) (limit 1)) :as 'event))) event)) (<dbi-database-error> (c) nil))))) @export (defun get-event (id) (retrieve-one (select :* (from :event) (where (:= :id id)) (limit 1)) :as 'event))
こんな感じで書いていきます。
これをsrc/web.lisp
から利用するには、src/config.lisp
の中でuse宣言を追加して、cl-reexport
でエクスポートします。
階層的なパッケージを作成したい時に利用するライブラリだそうです。
(in-package :cl-user) (defpackage ipu-lt-server.db (:use :cl :ipu-lt-server.model.user :ipu-lt-server.model.himg :ipu-lt-server.model.event :ipu-lt-server.model.participant :ipu-lt-server.model.link) ;; 〜中略〜 (in-package :ipu-lt-server.db) (cl-reexport:reexport-from :ipu-lt-server.model.user) (cl-reexport:reexport-from :ipu-lt-server.model.himg) (cl-reexport:reexport-from :ipu-lt-server.model.event) (cl-reexport:reexport-from :ipu-lt-server.model.participant) (cl-reexport:reexport-from :ipu-lt-server.model.link) ;; 〜以下略〜
続いてこれらのファイルをモジュールとして登録します。
asdファイルのcomponentsのところにmodel
を書き加えてください。
:components ((:module "src" :components ((:file "main" :depends-on ("config" "view" "db")) (:file "web" :depends-on ("view" "openid")) (:file "openid" :depends-on ("config" "view" "db")) (:file "view" :depends-on ("config")) (:file "db" :depends-on ("config" "model")) (:module "model" :components ((:file "event" :depends-on ("himg" "user")) (:file "user") (:file "himg") (:file "participant") (:file "link"))) (:file "config"))))
<form>のデータ受け渡し
基本的には、そのまま引数に内容が入る形になります。
(defroute ("/post" :method :POST) (&key |message|) (format nil "~A" |message|))
ファイルのアップロード
ファイルも同時に送信する場合はformタグにenctype="multipart/form-data"
属性を指定することになります。
この場合は(slot-value |file| 'flexi-streams::vector)
としてバイト列を取り出してやる必要があります。
※この辺りの挙動が謎で、localhostでやると上手く行かなかった。
例えば画像ファイルであれば、そのまま(write-sequence)
でファイルに書き出せばHTMLの方で表示出来る形になります。
(defroute ("/post" :method :POST) (&key |message| |file|) (let* ((time (write-to-string (get-universal-time))) (fname (merge-pathnames (format nil "~A.jpg" time) *static-directory*))) (with-open-file (out fname :direction :output :element-type '(unsigned-byte 8) :if-does-not-exist :create) (write-sequence (slot-value |file| 'flexi-streams::vector) out)) (format nil "~A" message)))
ログイン
外部サービス連携
今回作ったシステムのメインユーザーは学生なので、Googleアカウント認証を利用しました(学生メール=Gmail)
以前Qiitaに書いたコードをそのまま使っているのでそちらを参考にしてください。
以上、雑なまとめでした。
Lispで競技プログラミングしてみた話
もうすぐ2016年も終わりですね。
この1年の所感としては、色んな面で去年に比べて大分成長出来たかな、というところです。
今月始めはアドベントカレンダーの記事を書くのに大忙しでした。
調子に乗って5つも参加登録したせいなのですが、もしかしたらこの期間が一番成長に寄与したのでは…。
しかし1人で25日分の記事を書くような人もいるので、5つではまだ甘いようです。
せっかくなので、以下に書いた記事を並べておきます B^)
Common Lispに1年間入門して思ったこと - Qiita
過去に作ったノベルゲームエンジンの解剖 - Qiita
Common LispでOpenID Connect - Qiita
OCamlのSedlex+Menhir+LLVMでLispコンパイラを作ろうとしたが行き詰まった件 - Qiita
Common LispでGPUベクトルベースフォントレンダリング - Qiita
競プロやってみた
先日初めて、競技プログラミングというものをやってみました。
ちなみにアルゴリズム力には全く自信がありません。
競技プログラミングと言ったらAtCoderだろうということで、登録した後にまず過去問を解いてみました。
競プロ童貞なので、とりあえずAtCoder Beginner Contest(ABC)の方を何個か。
例えばこれなんかだと以下のコードでAC(accept)。
;;; AtCoder Beginner Contest 049-B Thin (defun split (str delim) (let ((res (make-array 1 :element-type 'string :fill-pointer 0 :adjustable t))) (loop for i from 0 below (length str) with start = 0 when (eq (char str i) delim) do (vector-push-extend (subseq str start i) res) (setf start (1+ i)) finally (let ((tail (subseq str start))) (when tail (vector-push-extend tail res)))) res)) (let* ((in (split (read-line) #\Space)) (h (parse-integer (aref in 0)))) (loop for i from 0 below h for line = (read-line) do (format t "~A~%~A~%" line line)))
※ちなみにsplit関数は予め別のファイルに用意して、問題に手を付ける度にコピペして使いまわしています。
※自前でsplitを実装すると結果をlistで返してしまいがちですが、多くの場合は速度低下に繋がって、競プロではTime Limit Exceededとなってしまうので配列で返すようにしましょう。
まあこんな感じに、Common Lispだからと言って大きなディスアドバンテージがあったりはしないようです。
本来なら事前に色々なマクロを組んでガチガチに固めた状態で臨むべきなのでしょうが、初回なので普通に組んでみました。
コンテストに出てみた
丁度良いタイミングで土日にコンテストが控えていたので、両方共参加してみました。
※普段はAtCoder Regular Contest(ARC)とABCが毎週土曜日に行われています。
まずは第3回 ドワンゴからの挑戦状 予選に参加しました。
僕にとってはこれがリアルタイムで参加する初めてのコンテストとなりました。
一応過去問も解いてはみたのですが、これがなかなか難しい。難易度はARCぐらいでしょうか。
結果は1/5問で、A問題しか解けませんでした。B問題に膠着しすぎたのかな…。
目標はB問題まで解くことだったので結構悔しかったです。
翌日はAtCoder Beginner Contest 050に参加しました。
こちらは初心者向けのコンテストなので、それほど苦戦はしませんでした。
が、しかしD問題だけ解けず…解説を見てもいまいちピンと来ません。おそらくこの辺りが自分のアルゴリズム力の閾値なのでしょう。
ちなみにABC全体でもD問題を解けた人は居なかったようです…。
あと、Common Lispで参加したのはどのコンテストを見ても僕だけでした:<
感想
思ったより楽しかったです。特に自分のコードに点数が付くのが新鮮でした。
まだ競プロを始めたことで新たに得た教訓はありませんが、今後も暇さえあれば挑戦してみたいと思います。
Lispで競プロをやるにあたっての奨め
- 出力はformatで、複数の時は
(format "~{~A~^ ~}~%" list)
などを活用 - splitはarrayで返す
- dotimes・loopマクロを活用
- 使える武器(マクロ)は予め作っておく
近況
そういえば最近lemを使い始めました。
ずっとVimを使ってきたのでEmacsキーバインドはあまり馴染まないのですが、動作が軽快なので結構気に入ってます。
何よりCommon Lisp製なのが良いですね!その恩恵でREPLも内蔵されていて良い感じです。
そのうちプラグインも作ってみたいな。
ブラウザを作ろうとしてたらいつの間にか正規表現エンジンもどきを作り始めていた話
題の通りです。
ブラウザって言うと語弊があって、正確にはHTMLレンダリングエンジンを作ろうとしていました。
Webkit、Geckoなどに相当するものですね。
経緯
最近、MozillaがServoという新しいレンダリングエンジンを開発しているのはご存知でしょうか。
並列性やパフォーマンスを重視しているそうで、どんな実装になっているか興味を持つのは当然ですよね。
まだ若いプロジェクトだし、自分でも理解出来るんじゃないかと思ってソースコードを読んだりもしました。
まあ、ほとんど理解出来ませんでしたけどね!そもそもRustの知識が足りなかった……。
そして、とりあえず自分でレンダリングエンジンっぽいものを実装してみようと考えました。
Rustは追々勉強して行くということで、その構造だけでも理解してやろうというわけです。
言語は最近ハマったCommon Lispで書こうということで始まりました。
さらに若気の至りでGithubにOrganizationまで作って……実はこれ去年作ったんですよね(汗)
ちなみに開発にあたって、以下のページを大いに参考にさせていただいています。
ブラウザのしくみ: 最新ウェブブラウザの内部構造 - HTML5 Rocks
Let's build a browser engine! Part 1: Getting started
実際に開発を始めたのは先々月辺りからで、まずはHTMLをパースしてレイアウティングするところから始めました。
Geckoは描画バックエンドにCairoを使っているようなので、とりあえずそれを真似てcl-cffi-gtkに入っているCairoを使うようにしました。
が、しかし何故かcairo-show-glyphs関数が見当たらず。これが無いとテキスト表示位置を細かく制御出来ない……。
だったらバックエンドも自分で作ってしまえ、ということでOpenGLのシェーダーを利用して文字列を描画するライブラリも開発中です。
モチベーションは以下の2つ。
GLyphy: high-quality glyph rendering using OpenGL ES2 shaders - Behdad Esfahbod from Behdad Esfahbod on Vimeo.
これについてはLisp Advent Calendar 2016の方で取り上げようと思っています。
正直どの程度のパフォーマンスで描画出来るのか未知数ですが、面白そうなのでやってるという感じです。
さあこれで開発が振り出しに戻ったどうしよう。とりあえずレンダーツリーを作れるようにしようか。
しかしブラウザ内部で使われているHTML・CSSパーサーはどうやら普通のやつとはちょっと違うみたいで、パースしながらレンダーツリーを構築している(?)らしいです。あと、多少記述が不正でも良い感じに解釈して処理してくれるとか。
ということは、パーサーも自分で作らないといけない感じですか。
常人ならこの辺で諦めそうですが、僕はしつこい男なので、なんとかやり遂げたい。
こんなことする暇あるのって学生のうちだけだろうし。
というわけでやっと本題に辿り着きましたね。
HTMLはcl-html5-parserというのがあったので、ひとまずそっちで処理しています。
CSSパーサーは自分で作ることにしました。今回はその話です。
Lispアドベントカレンダー2016とはてなブログ5周年ありがとうキャンペーン
Qiitaでは毎年12月にテーマに沿ってプログラミングに関する記事を12/1~12/25の間毎日みんなで投稿するイベントがあります。
由来はクリスマスまでの日数を数えるためのカレンダーだそうで。
そんなわけでLisp Advent Calendar 2016、作りました。
qiita.com
去年も一応投稿はしたのですが、"ずいぶんとダサいコードを書いているのね"って感じでした。
Shibuya.lispにも参加したこと無い自分が勝手に作って良いものか少し躊躇いましたが、気持ちを抑えきれず作ってしまいました=)
奮ってご参加ください。
僕は1年間Common Lispを学んで思ったことと、GPUフォントレンダリングについて書きたいと思っています。
前から色々とブログはやっていたので、記事書くの結構好きなんですよね。
それはそうと、はてなブログが5周年を迎えるそうです。
おめでとうございます!
僕がブログを始めた頃ははてなダイアリーしか無かったので、何だか嬉しい。
そっちを使っていた時代もありました(もう更新はしていませんが)。
それでは何だか企画があるそうなので、景品目当てで質問に答えておきます。
ボールペンはLAMYのものだそうで、なかなか良いセンスしてますね。
はてなブログ5周年ありがとうキャンペーンお題第1弾「はてなブロガーに5つの質問」
1.はてなブログを始めたきっかけは何ですか?
アウトプットの場が欲しかったから!Qiitaでも良かったけど何となくブログに書きたかった。
そして単純にはてなって会社が好きだからっていうのもあるし、良質な記事は大体はてなブログに書かれてるってイメージがあるから。
2.ブログ名の由来を教えて!
方針としてLispを通して世界と繋がっていきたいと思ったので、リストでyou(あなた、世界)とlispとme(僕)を繋げてみました。
なかなか良いネーミングセンスだと個人的には思っているんですが、どうでしょうかB-)
ちなみにURLの由来はCommon Lispの関数であるprognです。
Common Lispを学んでいて序盤で目にする関数の中で、これだけやたら手続き型っぽい印象が強くて記憶に残っていました。
3.自分のブログで一番オススメの記事
progn.hateblo.jp
これかなー。
数学っぽいプログラムを書くという人生初の試みだったのですが、意外と上手く書けたと(自分では)思うので。
これを書いたあたりから、Lispを書くのがどんどん楽しくなってきました。
4.はてなブログを書いていて良かったこと・気づいたこと
何よりもLispを書き続けて来られたこと。
新しくて良さそうな物に飛びつく癖がある自分が、Lispを片時も忘れずにいられたのはこのブログを書いていたからでしょう。
あ、もちろんLispの魅力っていうのもありますけどね!
気付いたことは、マークダウン記法が便利ってこと。
シンタックスハイライトもしてくれるし、はてなブログ最高ですね。
5.はてなブログに一言
今後も良いサービス作りを頑張ってください。
自分も専門分野の中で何かサービス作りに貢献したいのでインターンシップ参加したいです(願望)。
Lispで日本語を宇宙人言葉にする
Lispカク。
— (H(i d λ)e) (@cddadr) 2016年10月20日
Lispノチカラモラウ。
カッコヤッツケルチカラホシイ。
Twitterでふざけてこんなツイートをしてみたところ、 '(元ネタ :もののけ姫)
Goovy カッコ スクナイ
— こーじ@レガシーコード生産者 (@saba1024) 2016年10月20日
Lisp キタ ウチュウ エイリアン コワイ コワイ https://t.co/1w5shl5BhS
こんな煽りを受けまして、
@cddadr やはりエンジニアたるもの、こうやって毎回手動でカタカナに変換していてはイカンな、ということで早速スクリプトをGroovyで書いたよ!!
— Koji@レガシーコード生産者 (@saba1024) 2016年10月20日
[Groovy]文章を解析して日本語を学びかけの宇宙人のようにする。https://t.co/nnDQ7vgqH4
GroovyよりLispの方が圧倒的にイケメン言語に決まってる(過激派)。
これは対抗しないわけにはいかない。
しかしCommon Lispには良い感じの形態素解析器が無い…
MeCabか何かのバインディングを書いてもいいけれどそれは面倒くさい。
なので以下を参考にしてYahoo! JAPANの日本語形態素解析APIを利用しました。
以下コード。
(eval-when (:compile-toplevel :load-toplevel :execute) (require 'asdf) (require 'dexador) ; WebAPIを叩くために利用 (require 'cxml) ; XMLをS式に変換するために利用 (require 'alexandria)) ; リストをシャッフルするために利用 (defparameter *yahoo-appid* "Yahoo AppIDを入れてください") (defparameter *parse-url* "http://jlp.yahooapis.jp/MAService/V1/parse") (defun request-parse (text) "Yahoo! JAPANの日本語形態素解析APIで、引数で与えられた文字列を形態素に分割し、結果をXML文字列で返します。" (dex:post *parse-url* :content `(("appid" . ,*yahoo-appid*) ;("filter" . "1|2|9|10") ("sentence" . ,text)))) (defun parse (text) "形態素解析APIの結果XML文字列から(読み . 品詞名)のリストを生成して返します。" (destructuring-bind (_a _b (_c _d _e _f word-list)) (cxml:parse (request-parse text) (cxml-xmls:make-xmls-builder)) (declare (ignorable _a _b _c _d _e _f)) (loop for (_a _b (_c _d _e) (_f _g reading) (_h _i pos)) in (cddr word-list) collect (cons reading pos)))) (defun kanap (c) "引数で与えられた文字がひらがなかどうかを判定します。" (< #x3040 (char-code c) #x309f)) (defun htok (c) "引数で与えられた文字がひらがなであればカタカナに変換して返します。 そうでなければそのまま返します。" (if (kanap c) (code-char (+ (char-code c) #.(- (char-code #\ア) (char-code #\あ)))) c)) (defun hira->kata (text) "引数で与えられた文字列のひらがなをカタカナに変換して返します。" (map 'string #'htok text)) (defun leave-pos (wlist) "(読み . 品詞名)のリストから特定の品詞以外を取り除き、読みのリストを返します。" (loop for w in wlist for pos = (cdr w) then (cdr w) if (or (string= "名詞" pos) (string= "動詞" pos) (string= "形容詞" pos) (string= "形容動詞" pos) (string= "副詞" pos) (string= "接続詞" pos)) collect (car w))) (defun ->alien (text) "与えられた文字列を宇宙人言葉に変換して返します。" (format nil "~{~A~^ ~}" (alexandria:shuffle (mapcar #'hira->kata (leave-pos (parse text)))))) (->alien "プログラミング言語Common Lispは最高にクールです。")
API実行部分を除けばおよそ30行程度で、とても短いですね!
そして特殊な構文も少なくて分かり易い!
実行結果はランダムなのですが、今回は以下のようになりました。
"プログラミング クール ゲンゴ Common Lisp サイコウ"
なんて物分かりの良い宇宙人なのでしょうか。
宇宙人もCommon Lispを使っているのかも(?)
さらに走れメロスの冒頭でも試してみます。
(dolist (s '("メロスは激怒した。" "必ず、かの邪智暴虐の王を除かなければならぬと決意した。" "メロスには政治がわからぬ。" "メロスは、村の牧人である。" "笛を吹き、羊と遊んで暮して来た。" "けれども邪悪に対しては、人一倍に敏感であった。")) (format t "~A~%" (->alien s)))
以下結果です。
メロス ゲキド カナラズ ケツイ ノゾカ ジャチ ボウギャク オウ セイジ メロス ワカラ メロス ムラ ボクジン フエ フキ クラシ アソン ヒツジ キ タイシ ビンカン ジャアク ヒトイチバイ ケレドモ
動詞を原形に戻す処理を行っていないのでなんとなくヘンテコな感じになってしまいましたが、それらしくはなったと思います。
それはともかくとして、やっぱりCommon Lisp サイコウ クール プログラミング ゲンゴ
Common Lispはこれからも現役です。
ネタをくださいました@saba1024さんに感謝いたします:)