読者です 読者をやめる 読者になる 読者になる

'(you lisp me)

Lispって何だ

実践的とは

どうも、最近自動車学校の教習でヒイヒイ言ってるtamamuです。

なんだかブログの更新間隔が徐々に開いてますね。これはまずいぞ。
と、まあ別にサボっていたわけでは無いのですよ。ブログ以外のとこに文章を書いていたと言いましょうか。

先日、CollegeUnionというコミュが立ち上がりました。

hackmd.io

懲りずにコミュニティ作りました - をるふちゃんのブログ

特に目的があるコミュでは無く、Slackっぽいツール(discord)で情報共有したり、知識で殴り合ったりと和気あいあいとした感じでやってます。
一応情報系学生を主体として立ち上がったので、中身は学生が多いようです。とは言っても、プログラミングに心を奪われた野郎共の巣窟なので年齢は関係ありませんね。

で、上のリンクがそうなんですが、このコミュニティにはWikiが存在します。
エンジニアが集まると情報共有をこういうツールでやりたがるんですね(褒めてる)

大概は布教のために言語の入門記事を書いたりしています。現に僕もそうなんですけどね。
僕はCommon Lispが好きなので、CLをソフトウェア開発の選択肢に入れてもらえるようなものを書こうと思いました。
そこで実践入門です。

hackmd.io

CL本は結構充実してますし、Web上の入門記事も必要最低限存在していると思います。
しかし実践的な入門をまとめたものはあまり無いような気がしたので、これを書くに至っています。

ここで記事タイトルの話題になりますが、実践的とは何でしょうか。
実践Common Lispという本もありますが、これはプログラミング言語としてのCLの実践がメインです。
CLでのソフトウェア開発の実践は無いのでしょうか?

おそらく探せば無いことはないのでしょうが、ジャンルに特化した記事が点で存在している状況です。
なので、この辺りでまとめ記事っぽいのを作ってもバチは当たらないんじゃないか?と思った次第であります。

何より僕の書く記事の目的は布教です。
他の言語を書いている人に目を向けてもらうにはどうすればよいかを考えた結果、「それ、Lispで出来るよ」を素で言ったものを書くことにしました。
理想は「おいしいClojure入門」のCommon Lisp版となることです。

おいしいClojure入門 (Software Design plus)

こういう面白そうなことを集めた感じの記事が1つ2つあるだけでも世間のLispの見方が変わると思っているのですが、どうでしょうか。
何か間違いや付け加えた方が良さそうな文章があれば該当ページの方で修正してくださって構いませんので。
現状出来てる(書きかけ)ページが以下の2つです。

※まさかりを投げて頂ければドンドン僕の頭に突き刺さります

#| 疲れた頭で書いて文章がおかしいかもしれないので後で校正するかも |#

Caveman2でイベント管理システムを構築した話

気づけば1月末です。
あ! あけましておめでとうございます(遅い)

昨年末の話なのですが、Caveman2でイベント管理システムなるものを作っておりました。
現在はAmazon EC2Ubuntuインスタンスで運用していて、ドメインはお名前.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に書いたコードをそのまま使っているのでそちらを参考にしてください。

qiita.com

以上、雑なまとめでした。

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を使い始めました。

github.com

ずっとVimを使ってきたのでEmacsキーバインドはあまり馴染まないのですが、動作が軽快なので結構気に入ってます。
何よりCommon Lisp製なのが良いですね!その恩恵でREPLも内蔵されていて良い感じです。
そのうちプラグインも作ってみたいな。

ブラウザを作ろうとしてたらいつの間にか正規表現エンジンもどきを作り始めていた話

題の通りです。
ブラウザって言うと語弊があって、正確にはHTMLレンダリングエンジンを作ろうとしていました。
WebkitGeckoなどに相当するものですね。

経緯

最近、MozillaServoという新しいレンダリングエンジンを開発しているのはご存知でしょうか。
並列性やパフォーマンスを重視しているそうで、どんな実装になっているか興味を持つのは当然ですよね。
まだ若いプロジェクトだし、自分でも理解出来るんじゃないかと思ってソースコードを読んだりもしました。
まあ、ほとんど理解出来ませんでしたけどね!そもそもRustの知識が足りなかった……。

そして、とりあえず自分でレンダリングエンジンっぽいものを実装してみようと考えました。
Rustは追々勉強して行くということで、その構造だけでも理解してやろうというわけです。

言語は最近ハマったCommon Lispで書こうということで始まりました。
さらに若気の至りでGithubにOrganizationまで作って……実はこれ去年作ったんですよね(汗)

github.com

ちなみに開発にあたって、以下のページを大いに参考にさせていただいています。

ブラウザのしくみ: 最新ウェブブラウザの内部構造 - HTML5 Rocks
Let's build a browser engine! Part 1: Getting started

実際に開発を始めたのは先々月辺りからで、まずはHTMLをパースしてレイアウティングするところから始めました。
Geckoは描画バックエンドにCairoを使っているようなので、とりあえずそれを真似てcl-cffi-gtkに入っているCairoを使うようにしました。
が、しかし何故かcairo-show-glyphs関数が見当たらず。これが無いとテキスト表示位置を細かく制御出来ない……。

だったらバックエンドも自分で作ってしまえ、ということでOpenGLのシェーダーを利用して文字列を描画するライブラリも開発中です。

github.com

モチベーションは以下の2つ。

lab.sonicmoov.com

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パーサーは自分で作ることにしました。今回はその話です。

続きを読む

未来予想図

はてなブログ5周年ありがとうキャンペーンお題第2弾「5年後の自分へ」

http://blog.hatena.ne.jp/-/campaign/hatenablog-5th-anniversary

キャンペーン続きで後ろめたい!

最近はCaveman2でブログもどきを作ってみたり、会員制のWebアプリみたいなのを作って遊んでいます。
そのうち記事にするかもしれません。

さて、5年後の自分へということですが…。

僕は現在大学2年ということで、5年後は働いてるか大学院かな?
修士までは行くつもりですが、どうなっていることやら。

このブログもいつまで続くかな。
最近思ったのは、Lisp以外の話題も書きたいなってこと。
ここしばらくはLisp触ってる時間が一番長いのですが、他の言語も書きたい。
僕は言語フリークなのかもしれませんB^)

で、5年後の自分に宛ててメッセージでも書けばいいのかな。
タイムカプセルみたいなものですかね。
今年20になったので、中学の時の自分からもメッセージが届くことを思い出した。

それでは、5年後の予想と願望を書くことにします。
まずは願望。

――どこかの先端企業からヘッドハンティングされてそのままエリート街道を突っ走る……

冗談です。半分ぐらい。
自分の書きたいコードが伸び伸び書けてれば良いかな。
あっ、それと(美人で)プログラミングに理解がある嫁が居て欲しいです。

ここからは予想を書きます。

この先5年の間、おそらく一回ぐらいは海外行ったりするんじゃないでしょうか。
何の機会かまでは予想はつきませんが、そこで何かを得て、活動に幅が出る気がします。
具体的にはOSSとか、ですかね。

そして5年後、博士までは行かずに就職してると思います。
就職先はWebか人工知能系でしょうね。ゲーム開発を志していた頃もありましたが、趣味でやってるうちが一番楽しい気がします。
嫁は…どうでしょう。僕がその気にならないと無理でしょうね。

何はともあれ、心身共に健康で居て欲しいものです。
そしてLispの発展に少しでも寄与出来てたら誇らしいです。

以上です。
今度はLisp以外の記事も書いてみます。

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.はてなブログに一言

今後も良いサービス作りを頑張ってください。
自分も専門分野の中で何かサービス作りに貢献したいのでインターンシップ参加したいです(願望)。

http://blog.hatena.ne.jp/-/campaign/hatenablog-5th-anniversary

Lispで日本語を宇宙人言葉にする

Twitterでふざけてこんなツイートをしてみたところ、 '(元ネタ :もののけ姫)

こんな煽りを受けまして、

qiita.com

GroovyよりLispの方が圧倒的にイケメン言語に決まってる(過激派)。
これは対抗しないわけにはいかない。

しかしCommon Lispには良い感じの形態素解析器が無い…
MeCabか何かのバインディングを書いてもいいけれどそれは面倒くさい。
なので以下を参考にしてYahoo! JAPANの日本語形態素解析APIを利用しました。

read-eval-print.blogspot.jp

以下コード。

(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さんに感謝いたします:)