思考のための言語としてのLisp
プログラム言語はコンピューターに計算させるためのものです。しかし、自分の思考を表現するための道具という側面もあります。特にLispは数学との相性がよいので頭の中を整理するということにも役に立ちます。40年以上前の高校時代の生物、DNAを題材にしてLispで記述しました。Lispで記述することによりそれまでのぼんやりとした理解が明快になっていきます。暗記詰め込み型の受験教育にLispで一石を投じたいと以前から思っていました。
直感的なイメージ
YoutubeにDNAからタンパク質を生成するプロセスをわかりやすく画像でみせてくれる動画がありました。これは直感的な理解にとても役に立ちます。DNAは2重螺旋になっています。これを切り離して1本にして、mRNAに転写、リボゾームで暗号を解読して対応するアミノ酸を生成するという流れです。
パターンマッチングが役に立った
リボゾームにおいてはヌクレオチド3文字で1つのアミノ酸を表すようになっています。これをcond節で普通に書くと冗長になってしまいます。Easy-ISLisp用のライブラリに収録されているdeffpatternが役に立ちました。3文字をリストでくくっていくだけでパターンとして表すことができます。
;; generate Amino acid from mRNA
(defpattern ribosome
((empty-list) nil)
;;AUG is start-poinrt AUG is also met amino, distinguish them with _sw(nil,t)
(((#\A #\U #\G :rest _x) nil) (ribosome _x t))
;;UAA and UAG are end-point
(((#\U #\A #\A :rest _x) _sw) nil )
(((#\U #\A #\G :rest _x) _sw) nil )
(((#\U #\U #\U :rest _x) _sw) (cons 'phe (ribosome _x _sw)))
(((#\U #\U #\C :rest _x) _sw) (cons 'ala (ribosome _x _sw)))
(((#\U #\U #\A :rest _x) _sw) (cons 'leu (ribosome _x _sw)))
(((#\U #\U #\G :rest _x) _sw) (cons 'leu (ribosome _x _sw)))
(((#\C #\U #\U :rest _x) _sw) (cons 'leu (ribosome _x _sw)))
(((#\C #\U #\C :rest _x) _sw) (cons 'leu (ribosome _x _sw)))
(((#\C #\U #\A :rest _x) _sw) (cons 'leu (ribosome _x _sw)))
(((#\C #\U #\G :rest _x) _sw) (cons 'leu (ribosome _x _sw)))
(((#\A #\U #\U :rest _x) _sw) (cons 'ile (ribosome _x _sw)))
(((#\A #\U #\C :rest _x) _sw) (cons 'ile (ribosome _x _sw)))
・・・長いので途中省略・・・
(((#\U #\G #\G :rest _x) _sw) (cons 'trp (ribosome _x _sw)))
(((#\C #\G #\U :rest _x) _sw) (cons 'arg (ribosome _x _sw)))
(((#\C #\G #\C :rest _x) _sw) (cons 'arg (ribosome _x _sw)))
(((#\C #\G #\A :rest _x) _sw) (cons 'arg (ribosome _x _sw)))
(((#\C #\G #\G :rest _x) _sw) (cons 'arg (ribosome _x _sw)))
(((#\A #\G #\U :rest _x) _sw) (cons 'ser (ribosome _x _sw)))
(((#\A #\G #\C :rest _x) _sw) (cons 'ser (ribosome _x _sw)))
(((#\A #\G #\A :rest _x) _sw) (cons 'arg (ribosome _x _sw)))
(((#\A #\G #\G :rest _x) _sw) (cons 'arg (ribosome _x _sw)))
(((#\G #\G #\U :rest _x) _sw) (cons 'gly (ribosome _x _sw)))
(((#\G #\G #\C :rest _x) _sw) (cons 'gly (ribosome _x _sw)))
(((#\G #\G #\A :rest _x) _sw) (cons 'gly (ribosome _x _sw)))
(((#\G #\G #\G :rest _x) _sw) (cons 'gly (ribosome _x _sw))))
メチオンは開始記号でもあります。そこで_sw変数を用意しておいて開始記号なのか?メチオンなのか?を判別するようにしています。
Elixir風のパイプマクロも役に立った。
タンパク質生成はDNAの二重らせんを1本に分離し、これからmRNAに転写、さらにリボゾームに与えてアミノ酸に変換します。この流れを関数の入れ子にすると直観的なわかりやすさが失われます。Elixir風のPAIPマクロが役に立ちます。
(defun foo (x)
(pipe dna |> (car) |> (dna->rna) |> (add-pointer) |> (rna->amino)))
このfoo関数にヌクレオチドの文字列を与えるとアミノ酸に変換されます。
Easy-ISLisp Ver2.65
> (load "tests/dna.lsp")
T
> (foo dna)
(ARG ASN VAL TYR ILE PRO ALA SER GLN PHE GLN GLY CYS LYS)
> dna
("GCATTACAAATATAAGGACGAAGAGTTAAAGTTCCTACATTT" "CGTAATGTTTATATTCCTGCTTCTCAATTTCAAGGATGTAAA")
>
それにしても生命の不思議さよ
リボゾームは1つのアミノ酸を表すのに複数の3文字組み合わせを許容しています。このあたり、生命の性質を表しているように思います。少々の間違いがあってもなんとか機能してしまいます。デジタルコンピューターがほんの1bitの転写誤りでも完全に動作不能になるのと対照的です。ダーウィンの進化論に疑問をもってしまいます。何億年かかったとしても出鱈目な試行錯誤からこれほど精巧な仕組みが出来上がるものでしょうか?創造主の存在を感じます。
高校生のみなさん、勉強は楽しいよ
私の高校時代ときたら勉強=苦痛でした。何でこのような難行苦行を耐えなければならないのだろう?私はさっさとドロップアウトしました。後年、Lispを知り、再度大学に入れてもらって数学を勉強しました。数学に限らず学校で教わる伝統的な学問は非常に面白いものです。こんなにも面白いものをあれほど苦痛に教える高校教師というのはあれは一種の悪しき才能だなと思ったことを今でもはっきりと覚えています。Lispが学生さんの知的好奇心を刺激してくれて、受験の弊害をカバーしてくれたらうれしいと思いつつ、Lispに出会ってからかれこれ40年になりました。
ソースコードはここに置いてあります。