Chapter 09

【付録】fpLISPの活用

TAKIZAWA Yozo
TAKIZAWA Yozo
2021.12.28に更新

第一章で述べていますように,本書の本編で用いているプログラミング言語fpLISPは,筆者が独自に定めた極小仕様のLISP系言語で[1],実用的な目的,すなわち,大量のデータを処理したり様々な周辺機器を制御したりといった用途は想定していません.そもそも,標準入出力やファイル入出力といった機能が存在せず,入出力はあくまでプログラムコード内の引数指定や関数出力表示のみであることから,実用目的にはほど遠い言語です.加えて,『代入』に相当する機能は全て排除しているため,定義した関数規則に名前を付けて保存するという機能もありません.

ですが,小さい仕様ゆえに多くのコンピュータ環境で利用可能であること,プログラム処理におけるアルゴリズムという観点ではそのほとんどが実現可能[2]であることから,プログラミングを楽しんだり研究したりするには大変手軽な言語です.この章では,fpLISPをもう少し幅広く利用するためのノウハウを挙げていきます.なお,筆者による実装例およびサンプルコードは,第一章でも紹介しましたGitHubリポジトリより参照可能です.

https://github.com/ytaki0801/fpLISP/

fpLISP実行環境の紹介(2021/12/13現在)

第一章で紹介しましたJavaScriptによるfpLISP実装は,関係ファイルを一度取得すれば,オフラインでもWebブラウザのみで利用可能です.これは,Macintoshを含むPC環境やAndroid,iOS/iPadOSに基づく携帯端末環境でも同様です.一方,他のプログラミング言語による実装では,JavaScript実装よりも高速だったり,小さな組込機器でも利用できたりします.ここでは,実装言語ではなく,OSなどの利用環境ごとの解説を進めます.

Windows

筆者がC言語で実装したfpLISPは,ごく基本的なC言語仕様のみ用いており,特別なライブラリも要しません[3].ISOのC99規格を満たすC言語コンパイラ,たとえば,Windowsで稼働するTiny C Compiler等の小さいコンパイラでもコンパイル・実行が可能で,処理も高速です.

該当リポジトリよりfpLISP.cを取得し,コンパイルしてfpLISP.exeを生成すれば利用できますが,筆者の実装では入力部に編集機能がなく,EOF(ファイルの終わり)が来るまでプログラムコードを標準入力から読み込んで実行します.このため,次の通り,テキストファイルにサンプルコードを作成し,コマンドプロンプト等でリダイレクト入力するのが簡単でしょう.これは,他のプログラミング言語による実装でも基本的には同様です.

>curl -sO https://raw.githubusercontent.com/ytaki0801/fpLISP/master/C/fpLISP.c

>tcc fpLISP.c

>type test.fplisp
((lambda (fib)
   (fib 21))
 (lambda (n)
   (((lambda (u) (u u)) (lambda (u) (lambda (n a b)
       (if (lt n 0) nil (cons a ((u u) (- n 1) b (+ a b)))))))
    n 0 1)))


>fpLISP.exe < test.fplisp
(0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946)

>

リポジトリにはfpLISP-x86_64-Windows.exeの名前で実行ファイルが置いてありますので,それをダウンロードして利用しても良いでしょう(ただし,ソースコードからの最新版ではない可能性があります).

curl -sL https://github.com/ytaki0801/fpLISP/blob/master/C/fpLISP-x86_64-Windows.exe?raw=true > fpLISP.exe

Node.jsがインストールされていれば,JavaScript版のfpLISP.jsがサンプルコードのファイル読み込みで利用可能です.ただし,HTML経由との兼用のため,サンプルコードのファイルはリダイレクトではなく,引数にファイル名を指定する形で実装しています.

>curl -sO https://raw.githubusercontent.com/ytaki0801/fpLISP/master/JavaScript/fpLISP.js

>node fpLISP.js test.fplisp
(0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946)

>

変わり種として,POSIX仕様のシェルスクリプトで実装されたfpLISP.shがあります.処理は遅いのですが,超軽量なシェル環境であるBusyBoxのWindows版でも稼働することから,コマンドプロンプトで利用する実装の中では最も手軽と言えます.

>curl -sO https://raw.githubusercontent.com/ytaki0801/fpLISP/master/POSIX-Shell/fpLISP.sh

>busybox sh fpLISP.sh < test.fplisp
(0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946)

>

Windowsについては,Windows Subsystem for Linuxを用いて,ほぼ完全なUNIX環境を構築することができ,開発リポジトリの複製と合わせてどの実装も利用可能ですが,ここでは省略します.

Android OS

スマートフォンのオペレーティングシステムであるAndroidは,Linuxカーネルとシェル環境を使用しているため,Windowsの節で紹介したシェルスクリプト版fpLISP.shがそのまま動くだけでなく,動的ライブラリに依存しない形でコンパイルされたC言語版fpLISP.cのLinuxバイナリを稼働させることができます.従来,シェル環境にアクセスするためには,管理者権限の取得であるroot化が必要とされてきたのですが,最近は,TermuxTermOne Plusといった仮想端末アプリを用いることで,一般ユーザ権限のままでも,Linuxバイナリやシェルスクリプトの実行のみを行わせることが可能です.

開発リポジトリには,筆者の環境でコンパイルできた64ビット版のx86(fpLISP-x86_64-Linux)およびARM(fpLISP-aarch64-Linux)のバイナリが置いてあり,次の通り,仮想端末アプリ上で自動的にアーキテクチャをチェックしてLinuxバイナリを取得するシェルスクリプトが利用可能です.

$ curl -O https://raw.githubusercontent.com/ytaki0801/fpLISP/master/C/download-fpLISP-bin.sh
$ sh download-fpLISP-bin.sh
fpLISP-[アーキテクチャ名]-Linux has been downloaded.
$ ./fpLISP-[アーキテクチャ名]-Linux
(cons 10 20)
[ここでCtrlキーとDキーを同時に押す](10 . 20)
$ curl -s https://raw.githubusercontent.com/ytaki0801/fpLISP/master/samples/03-fibonacci.fplisp | ./fpLISP-[アーキテクチャ名]-Linux
(0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946)

シェルスクリプト版は,単純に,fpLISP.shを取得して付属のシェル(mksh)で実行できます.

$ curl -sO https://raw.githubusercontent.com/ytaki0801/fpLISP/master/POSIX-Shell/fpLISP.sh
$ sh fpLISP.sh
(cons 10 (quote 20 30))
[ここで空行を入れる]
(10 . 10)
$ curl -s https://raw.githubusercontent.com/ytaki0801/fpLISP/master/samples/03-fibonacci.fplisp | sh fpLISP.sh
(0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946)

Termuxについては,独自のシェル環境(userland)をもつことから,Windowsと同じくCコンパイラやNode.jsなどをインストールすることで,各言語による実装が利用可能です.

macOS

Macintosh用のOSであるmacOSは,Linuxとは異なるUNIX系統のBSD(Darwin)をベースとしたオペレーティングシステムであり,標準でインストールされている仮想端末ソフトウェア(ターミナル)を用いてシェル環境が利用可能です.fpLISP.shがそのまま実行できる他,開発環境であるXcodeを別途インストールすればCコンパイラ(clang)が利用でき,fpLISP.cをコンパイル・実行できます.もちろん,Node.jsを用いてJavaScript版fpLISP.jsを直接実行することも可能です.

64ビット版ARMアーキテクチャのmacOS用バイナリを開発リポジトリに置いてありますので,Androidの際と同じスクリプトでバイナリを取得できます.

$ curl -O https://raw.githubusercontent.com/ytaki0801/fpLISP/master/C/download-fpLISP-bin.sh
$ sh download-fpLISP-bin.sh
fpLISP-arm64-Darwin has been downloaded.
$ ./fpLISP-arm64-Darwin
(cons 10 20)
*ここでCtrlキーとDキーを同時に押す*(10 . 20)
$ curl -s https://raw.githubusercontent.com/ytaki0801/fpLISP/master/samples/03-fibonacci.fplisp | ./fpLISP-arm64-Darwin
(0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946)

iOS/iPadOS

iPhone/iPod touch/iPadのオペレーティングシステムであるiOS/iPadOSも,Macintoshと同じくDarwinをベースとしていますが,シェル環境を直接扱うアプリは基本的に想定されていません.サードパーティのアプリケーションソフトウェアがしばらくWebベースだったこともあって,fpLISPについても,Webサーバから公開されるfpLISP.htmlfpLISP.jsが基本となるかと思います[4]

なお,Alpine Linuxをx86エミュレータ上で稼働させるiSH Shellについては,ほぼ完全なUNIX環境が構築できることから,WindowsのWSLと同じく,ここでは省略します.

関数の事前定義(2021/12/28現在)

冒頭で述べましたように,fpLISPには,定義した関数規則に名前を付けて保存するという機能はありません.もし,実行前にあらかじめ関数規則を定義して名前を付けたい場合は,foldなどがそうなのですが,fpLISPのプログラムコード本体に直接追加することになります.ですが,当然ながら,どのように追加すれば良いかは,fpLISPを実装しているプログラミング言語に依存します.

下記に,階乗計算を行う関数factの追加例を,git diffの形式で掲載しますので,関心のある方は参考にしてみて下さい.

fpLISP.js:JavaScript,fpLISP.sh:POSIXシェル

diff --git a/JavaScript/fpLISP.js b/JavaScript/fpLISP.js
index a1849ff..c182694 100644
--- a/JavaScript/fpLISP.js
+++ b/JavaScript/fpLISP.js
@@ -202,6 +202,14 @@ const ENVINIT = "(quote ( \
        i0 a0 (if (eq b0 nil) nil (car b0))))           \
     (car x) (car (cdr x)) (car (cdr (cdr x)))          \
     (cdr (cdr (cdr x))))))                             \
+(fact .                                   \
+  (lambda (x)                             \
+    (((lambda (u) (u u))                  \
+      (lambda (u)                         \
+        (lambda (x r)                     \
+          (if (eq x 0) r                  \
+              ((u u) (- x 1) (* x r)))))) \
+     x 1)))                               \
 ))";
 const envinit = fp_eval(fp_read(ENVINIT), null);
 function fp_rep(e) { return fp_string(fp_eval(fp_read(e), fp_read("()"))); }

fpLISP.c:C言語

diff --git a/C/fpLISP.c b/C/fpLISP.c
index 321a8cf..acff78f 100644
--- a/C/fpLISP.c
+++ b/C/fpLISP.c
@@ -354,6 +354,14 @@ const char *INITENV = "(quote ("
 "       i0 a0 (if (eq b0 nil) nil (car b0))))           "
 "    (car x) (car (cdr x)) (car (cdr (cdr x)))          "
 "    (cdr (cdr (cdr x))))))                             "
+"(fact .                                   "
+"  (lambda (x)                             "
+"    (((lambda (u) (u u))                  "
+"      (lambda (u)                         "
+"        (lambda (x r)                     "
+"          (if (eq x 0) r                  "
+"              ((u u) (- x 1) (* x r)))))) "
+"     x 1)))                               "
 "))";

 void fp_eval_string(char *s)

fpLISP.lisp:Common Lisp,fpLISP.scm:Scheme

diff --git a/CommonLisp/fpLISP.lisp b/CommonLisp/fpLISP.lisp
index ace874a..f7c5149 100644
--- a/CommonLisp/fpLISP.lisp
+++ b/CommonLisp/fpLISP.lisp
@@ -22,6 +22,14 @@
                           ((u u) (f (car e)) (cons (cdr e) r))))))
                    (f seed) (if (eq i nil) nil (car i))))
                 (car a) (car (cdr a)) (cdr (cdr a)))))
+   (fact .
+     (lambda (x)
+       (((lambda (u) (u u))
+         (lambda (u)
+           (lambda (x r)
+             (if (eq x 0) r
+                 ((u u) (- x 1) (* x r))))))
+        x 1)))
    (fold .   (lambda x
                ((lambda (f i0 a0 b0)
                   (((lambda (u) (u u)) (lambda (u) (lambda (i a b)
脚注
  1. 実際のところ,言語機能としては,本編で述べたものが全てです. ↩︎

  2. プログラムコードの読みやすさはともかく,ですが. ↩︎

  3. 不要となったメモリ領域を逐次開放するガーベージコレクションさえ行っていないとも言えます. ↩︎

  4. 本編でJavaScript版の実装を取り上げた最大の理由です. ↩︎