<jupyter_start><jupyter_text>メモColab でLisp/Scheme を学んでみよう、というアイデア。テキスト:   お気楽 Common Lisp http://www.nct9.ne.jp/m_hiroi/clisp/index.html  Common Lisp Beginner https://common-lisp.net/project/common-lisp-beginner/links.html  Practical Common Lisp http://www.gigamonkeys.com/book/  Steel Bank Common Lisp http://www.sbcl.org/getting.html  land of lisp by conradbarski => lisp_landof.txt<jupyter_code>%%capture
!apt install sbcl<jupyter_output><empty_output><jupyter_text>sbcl は steel bank common lisp

以下実験学習なので、適当に実行する。<jupyter_code>%%script sbcl --noinform
(+ 2 3) ;;=> 5
(- 100 10 1) ;;=> 89<jupyter_output>* 
5
* 
89
*<jupyter_text>-- noinform を入れないと sbcl を立ち上げるたびにメッセージがでてしまう。

lisp のコメントはセミコロン 2つ。  プログラムをファイルにして読み込んで実行してみる<jupyter_code># プログラムをファイルにして読み込んで実行する
%%writefile fact01.lisp
(defun fact (n) (if (<= n 1) 1 (* n (fact (- n 1)))))
%%script sbcl --noinform
(load "fact01") ;;=> T
(fact 10) ;;=> 3628800<jupyter_output>* 
T
* 
3628800
*<jupyter_text>質問 T が true, NIL が false ですか? 0 とか 1, -1, () , "" とかは？
  質問 if 構文は lisp ではどうなりますか。 式 expression ですか 文 statement ですか。<jupyter_code>%%script sbcl --noinform
(+ 1 2 3 4 5) ;;=> 15<jupyter_output>* 
15
*<jupyter_text>S式とはシンボル式 = Symbolic Expression 、 アトムかリスト。Lisp ではデータもプログラムもS式。<jupyter_code># コメントはセミコロン 2つ
%%script sbcl --noinform
(defun fact  ;; 関数定義 関数名
    (n) ;; 引数名 型は宣言しない
    (if ;; 条件分岐
        (<= n 1) 1 ;; n が 1 以下ならば 1
        (* n 
            (fact 
                (- n 1))))) ;; そうでなければ n と fact の再帰呼び出し => FACT

(fact 10) ;;=> 3628800
%%script sbcl --noinform
(/ 7 2) ;;=> 
(/ 7.0 2.0) ;;=> 3.5
(/ 7 2.0) ;;=> 3.5
(mod 7 2) ;;=> 1
(mod -7 2) ;;=> 1
%%script sbcl --noinform
10 ;;=> 10
"this" ;;=> "this"
'(10 20) ;;=> (10 20)
'(this) ;;=> (THIS)
%%script sbcl --noinform
"こんにちは、世界!" ;;=> "こんにちは、世界!"
'(こんにちは、世界!) ;;=> (こんにちは、世界!) ;; 日本語は大文字にできないのでならない
%%script sbcl --noinform
(format t "hello, world") 
%%writefile helloworld01.lisp
(defun hello-world ()
  (format t "Hello, world!"))
%%script sbcl --noinform
(load "helloworld01")
(hello-world)
# リスト : たらいまわし関数 (Common Lisp)
%%script sbcl --noinform
(defun tak (x y z)
  (if (<= x y)
      z
    (tak (tak (1- x) y z)
         (tak (1- y) z x)
         (tak (1- z) x y))))

 (time (tak 22 11 0)) ;; Evaluation took: 5.788 seconds of real time
# リスト : たらいまわし関数 (Common Lisp)
# 速いバージョン
%%script sbcl --noinform
(defun tak (x y z)
  (declare (type fixnum x y z)
	   (optimize (speed 3) (safety 0)))
  (if (<= x y)
      z
    (tak (tak (1- x) y z)
         (tak (1- y) z x)
         (tak (1- z) x y))))

 (time (tak 22 11 0)) ;; Evaluation took: 1.649 seconds of real time
# CLOS (Common Lisp Object System) の場合、クラスはマクロ defclass を使って定義する。
# defclass は構造体 defstruct と似ている。

# (defclass クラス名 (スーパークラス ...)
#   ((スロット名 :accessor アクセスメソッド名 
#                :initform 初期値フォーム
#                :initarg  引数マークシンボル)
#     ・・・・
#    (スロット名 :accessor アクセスメソッド名 
#                :initform 初期値フォーム
#                :initarg  引数マークシンボル)))

# 参考 構造体
# (defstruct 構造体名
#     (スロット名 デフォルト値)
#     ・・・・
#     (スロット名 デフォルト値))
# インスタンスは関数 make-instance を使って生成する。

# make-instance クラス名 [引数マークシンボル Ｓ式 ...]
%%script sbcl --noinform
(defclass foo () ((a :accessor foo-a :initform 1 :initarg :a))) ;;=>  #<STANDARD-CLASS COMMON-LISP-USER::FOO>
(setq x (make-instance 'foo)) ;;=>  #<FOO {1001C6F173}>
(foo-a x) ;;=>  1
(setf (foo-a x) 2) ;;=> 2
(foo-a x) ;;=>  2
(typep x 'foo) ;;=> T
(type-of x) ;;=> FOO 
(setq y (make-instance 'foo :a 10)) ;;=> #<FOO {1001CF6A23}>
(foo-a y) ;;=> error
%%script sbcl --noinform
(format t "Hello, World") ;;=> Hello, World ;;=> NIL
;;=> 副作用が Hello, World で、返り値が NIL ということか?
%%script sbcl --noinform
(+ (* 1 2) (- 3 4)) ;;=> 1
%%script sbcl --noinform
format ;;=> The variable FORMAT is unbound.
%%script sbcl --noinform
(defvar a) ;;=> A こうしておかないといまの sbcl では warning が出る
(setq a 10) ;;=> 10
a ;;=> 10
%%script sbcl --noinform
(defvar a) ;;=> A 
(defvar b)  ;;=> B
(setq a (+ 1 2 3)) ;;=> 6
a ;;=> 6
(setq b a) ;;=> 6
b ;;=> 6
%%script sbcl --noinform
t ;;=> T
NIL ;;=> NIL
() ;;=> NIL
nil ;;=> NIL
%%script sbcl --noinform
(setq t 10) ;;=> T is a constant and thus can't be set.
%%script sbcl --noinform
(setq NIL 10) ;;=> NIL is a constant and thus can't be set.
# format は第 1 引数が T の場合、標準出力へデータを出力する。
# 通常引数は先に評価されてから代入される
%%script sbcl --noinform
(defvar x)
(defvar a)
(setq x 10) ;;=> 10
(setq a x) ;;=> 10
a ;;=> 10
# QUOTE ' をつけると評価されない
%%script sbcl --noinform
(defvar a) ;;=> A
(setq a 'x) ;;=> X
a ;;=> X
# リストも QUOTE をつけると評価しない
%%script sbcl --noinform
(defvar y) ;;=> Y
'y ;;=> Y
'(1 2 3 4)  ;;=> (1 2 3 4)
(setq y '(1 2 3 4))  ;;=> (1 2 3 4)
y  ;;=> (1 2 3 4)
# アトムとリストを合わせて「Ｓ式 (symbolic expression)」または「フォーム (form)」と言う。
# 関数 car はリストの先頭要素を取り出し、関数 cdr はリストの先頭要素を取り除いたリストを返す。 first rest でも同じ。
%%script sbcl --noinform
(cdr '(a)) ;;=> NIL
(car nil) ;;=> NIL
(cdr nil) ;;=> NIL
%%script sbcl --noinform
(car '((a b) (c d) (e f))) ;;=> (A B)
(cdr '((a b) (c d) (e f))) ;;=> ((C D) (E F))
(car (car '((a b) (c d) (e f)))) ;;=> A
# リストの 2番目の要素を取り出す
%%script sbcl --noinform
(car (cdr '(a b c d e f))) ;;=> B
(car '(b c d e f)) ;;=> B
%%script sbcl --noinform
(cadr '(a b c d)) ;;=> B
(caddr '(a b c d)) ;;=> C
(cadddr '(a b c d)) ;;=> D
(first '(a b c d))
(second '(a b c d))
(third '(a b c d))
(fourth '(a b c d))
(fifth '(a b c d e f g h i j k))
(sixth '(a b c d e f g h i j k))
(seventh '(a b c d e f g h i j k))
(eighth '(a b c d e f g h i j k))
(ninth '(a b c d e f g h i j k))
(tenth '(a b c d e f g h i j k))<jupyter_output><empty_output><jupyter_text>cons<jupyter_code># cons
%%script sbcl --noinform
(cons 'a nil) ;;=> (A)
(cons 'a ()) ;;=> (A)
(cons 'a 'a) ;;=> (A . A)
(cons 'b (cons 'a nil)) ;;=> (B A)
(cons 'c (cons 'b (cons 'a nil))) ;;=> (C B A)
(cons 'd (cons 'c (cons 'b (cons 'a nil)))) ;;=> (D C B A)<jupyter_output><empty_output><jupyter_text>質問 lisp の list は速いか遅いか?<jupyter_code># list 関数
%%script sbcl --noinform
(list) ;;=> NIL
(list 'a) ;;=> (A)
(list 'a 'b) ;;=> (A B)
(list '(a b) '(c d) '(e f)) ;;=> ((A B) (C D) (E F))
(list '(a b c d e f)) ;;=> ((A B C D E F))
# append 関数
%%script sbcl --noinform
(append '(a b c) '(d e f)) ;;=> (A B C D E F)
(append '((a b) (c d)) '(e f)) ;;=> ((A B) (C D) E F)
(append '((a b) (c d)) '((e f))) ;;=> ((A B) (C D) (E F)) 
%%script sbcl --noinform
(defvar v)
(setq v '(a b c d e)) ;;=> (A B C D E)
# immutability リストの操作は非破壊的
%%script sbcl --noinform
(defvar dvar)
(setq dvar '(a b c d e)) ;;=> (A B C D E)
(cdr dvar) ;;=> (B C D E)
dvar ;;=> (A B C D E)
;; var の値を変更したいときは setq や setf を使。
(setq dvar (cdr dvar)) ;;=> (B C D E)
dvar ;;=> (B C D E)<jupyter_output>* 
DVAR
* 
(A B C D E)
* 
(B C D E)
* 
(A B C D E)
* 
(B C D E)
* 
(B C D E)
*<jupyter_text>ドットリスト<jupyter_code># ドットリスト
%%script sbcl --noinform
(defvar a) ;;=> A
'(A . NIL) ;;=> (A)
'(A . (B . (C . NIL))) ;;=> (A B C)
'((A . (B . NIL)) . ((C . (D . NIL)) . NIL)) ;;=> ((A B) (C D)) 
'((A . (B . NIL)) . (C . (D . NIL))) ;;=> ((A B) C D)
# 問題 cons と list で次に示すリストを作る
# 各要素を取り出す S 式を作る
%%script sbcl --noinform
;; xs : (a (b) ((c)) (((d))))
;; ys : ((a b c) (d e f) (g h i))
;; zs : ((a . b) (c . d) (e . f))
;; (defvar xs)(defvar a)(defvar b)(defvar c)(defvar d)(defvar e)(defvar f)(defvar g)(defvar h)(defvar i)(defvar ys)(defvar zs)
(setq xs 
    (list 'a 
        (list 'b) 
        (list 
            (list 'c)) 
        (list 
            (list 
                (list 'd)))))
(setq ys 
    (list         
        (list 'a 'b 'c)         
        (list 'd 'e 'f)         
        (list 'g 'h 'i)))
(setq zs 
    (list 
        (cons 'a 'b) 
        (cons 'c 'd) 
        (cons 'e 'f))) ;;=> ((A . B) (C . D) (E . F))
(car xs) ;;=> A
(caadr xs) ;;=> B
(car (caaddr xs)) ;;=> C?
(caar (third xs)) ;;=> C 
(caaar (fourth xs)) ;;=> D?
(caaar (cadddr xs)) ;;=> D
(first (first ys)) ;;=> A
(second (second ys)) ;;=> E
(third (third ys)) ;;=> I
(caar zs) ;;=> A
(cdar zs) ;;=> B
(caadr zs) ;;=> C
(cdadr zs) ;;=> D
(caaddr zs) ;;=> E
(cdaddr zs) ;;=> F
# 数と算術演算
# 整数 (integer)、分数 (ratio)、
# 浮動小数点数 (floating-point number)、複素数 (complex number) という 4種類の数がある。

# 整数は処理系の内部で fixnum (63 bit)と bignum (多倍長整数) に分けている。
%%script sbcl --noinform
most-negative-fixnum ;;=> -4611686018427387904
most-positive-fixnum ;;=> 4611686018427387903
# 整数の奇数表記 #nnrddddd または #nnRddddd
# 2進数 #b の後ろに 0 と 1 の数値を記述
# 8進数 #o の後ろに 0 から 7 までの数値を記述
# 16進数 #x の後ろに 0 から f までの数値を記述
%%script sbcl --noinform
#4r1230123 ;;=> 6939
#b10101010 ;;=> 170
#o1234567  ;;=> 342391
#o-127     ;;=> -87
#xabcdef   ;;=> 11259375
%%script sbcl --noinform
1/2
2/3
4/3
11/13
-51/100
30517578125/32768
4/6  ;;=> 2/3
3/12 ;;=> 1/4
10/5 ;;=> 2 
# 浮動小数点数
# 小精度 (short-float), 単精度 (single-float), 倍精度 (double-float), 長精度 (long-float)
%%script sbcl --noinform
most-positive-short-float ;;=> 3.4028235e38
most-positive-single-float ;;=> 3.4028235e38
most-positive-double-float ;;=> 1.7976931348623157d308
most-positive-long-float ;;=> 1.7976931348623157d308
# 複素数は #C の後ろに実部と虚部のリストを付けて表す
%%script sbcl --noinform
#C(5 -3)
#C(1.2 2.4)
#C(1/2 2/3)
#C(0.5 2/3) ;;=> #C(0.5 0.6666667)<jupyter_output>* 
#C(5 -3)
* 
#C(1.2 2.4)
* 
#C(1/2 2/3)
* 
#C(0.5 0.6666667)
*<jupyter_text>いまここ<jupyter_code># 四則演算
%%script sbcl --noinform
(+)           ;;=> 0
(+ 1)         ;;=> 1
(+ 1 2 3)     ;;=> 6
(+ 1 2 3 1/2) ;;=> 13/2
(+ 1 2 3 4.5) ;;=> 10.5

(*)           ;;=> 1
(* 1)         ;;=> 1
(* 1 2 3)     ;;=> 6
(* 1 2 3 1/4) ;;=> 3/2
(* 1 2 3 4.5) ;;=> 27.0

(- 1)         ;;=> -1
(- 10 5 4)    ;;=> 1
(- 10 5/2)    ;;=> 15/2
(- 10 4.5)    ;;=> 5.5
(-)           ;;=> エラー  ; 引数が足りない
# 
%%script sbcl --noinform
(1+ 2)   ;;=> 3
(1+ 2.5) ;;=> 3.5
(1- 2)   ;;=> 1
(1- 2.5) ;;=> 1.5
%%script sbcl --noinform
(defvar x)
x

;; # (incf a)   ≡ (setf a (1+ a))
;; # (incf a n) ≡ (setf a (+ a n))
;; # (decf a)   ≡ (setf a (1- a))
;; # (decf a n) ≡ (setf a (- a n))
;; #incf は第 2 引数が省略されると、第 1 引数のシンボル
;; #格納されている数値に 1 を加えます。第 2 引数が与えられると、その値を加えます。
;; #incf は足し算した結果を返します。decf は incf とは逆に引き算を行います。


関数 / は割り算を行います。整数同士の割り算で割り切れない場合は分数になります。引数が 0 の場合はエラーになります。

(/ 2)     => 1/2    ; 引数の逆数を求める
(/ 8 4 2) => 1      ; 約分されて整数になる
(/)       => エラー
●数の変換
整数や分数を浮動小数点数に変換するには関数 float を使います。

float number [other]
float は number を other と同じ形式の浮動小数点数に変換します。other は浮動小数点数でなければならず、省略すると single-float に変換されます。

(float 1/3)     => 0.3333333
(float 1/3 1d0) => 0.3333333333333333d0
(float 3)       => 3.0
(float 3 1d0)   => 3.0d0
逆に、分数や浮動小数点数を整数に変換するには、次に示す関数を使います。

floor num1 [num2]
i ≦ num1 ＜ i + 1 を満たす整数 i を求める。
ceiling num1 [num2]
i - 1 ＜ num1 ≦ i を満たす整数 i を求める。
truncate num1 [num2]
小数点以下を切り捨てる。
round num1 [num2]
近い方の整数に丸める。ちょうど 0.5 の場合には偶数方向に丸める。
引数 num2 が与えられた場合は (/ num1 num2) を評価し、その結果を整数に変換します。

簡単な例を示しましょう。

* (floor 2.5)

2
0.5
* (floor -2.5)

-3
0.5
* (floor 5 2)

2
1
これらの関数は 2 つの値を返します。これを「多値 (multiple values)」といいます。最初の値は今までの関数と同じように受け取ることができます。2 番目の値を受け取る方法は、多値の使い方と一緒にあとで詳しく説明します。

2 番目の値は、引数 (数値) が 1 つの場合は "数値 - 求めた整数" です。引数が 2 つ (数値と除数) の場合は "数値 - 求めた整数 * 除数"、つまり剰余になります。

●剰余と累乗
剰余は関数 mod と rem で求めることができます。

mod number divisor
rem number divisor
mod は (floor number divisor) を実行し、その 2 番目の値を返します。rem は (truncate number divisor) を実行し、その 2 番目の値を返します。

簡単な例を示しましょう

* (mod 5 2)

1
* (rem 5 2)

1
* (floor 5 2)

2
1
* (truncate 5 2)

2
1
* (mod -5 2)

1
* (rem -5 2)

-1
* (floor -5 2)

-3
1
* (truncate -5 2)

-2
-1
関数 expt は累乗を計算します。

expt x y
(expt x y) は xy を返します。x が整数または分数で y が整数ならば、正確な値 (整数または分数) が返されます。そうでなければ浮動小数点数による近似値が返されます。

(expt 2 32)     => 4294967296
(expt 2 64)     => 18446744073709551616
(expt 1/2 16)   => 1/65536
(expt 1/2 32)   => 1/4294967296
(expt 0.5d0 32) => 2.3283064365386963d-10
(expt 2 0.5d0)  => 1.4142135623730951d0
●その他
関数 gcd はすべての引数の最大公約数を返します。関数 lcm はその引数の最小公倍数を返します。どちらの関数も引数は整数でなければいけません。

(gcd 91 49)     => 7
(gcd 63 42 35)  => 7
(lcm 14 35)     => 70
(lcm 1 2 3 4 5) => 60
関数 exp x は ex を求めます。e は自然対数の底です。関数 log x [b] は logb x を求めます。b を省略すると e になります。関数 sqrt x は√x を求めます。

* (exp 1d0)

2.718281828459045d0
* (log 10d0)

2.302585092994046d0
* (log 10d0 10)

1.0d0
* (log 10d0 2)

3.3219280948873626d0
* (sqrt 2d0)

1.4142135623730951d0
このほかにも、Common Lisp にはたくさんの数学関数が用意されています。興味のある方は調べてみてください。


●問題
次に示す数式を Lisp で計算してください。

1 + 2 + 3 - 4 + 5 + 6 + 78 + 9
n(n + 1) / 2
変数 n に適当な正整数をセットすること
(- b±√(b2 - 4ac)) / 2a
変数 a, b. c には適当な値をセットすること
d = (1 + √5) / 2, e = (1 - √5) / 2
(dn+1 - en+1) / √5
変数 n には適当な正整数をセットすること
1 + 2 + 22 + 23 + ... + 280 = 281 - 1
7654321 の末尾の数字

●解答
* (+ 1 2 3 -4 5 6 78 9)    ; -4 は (- 4) としてもよい

100
* (+ (+ (+ (+ (- (+ (+ 1 2) 3) 4) 5) 6) 78) 9)

100
* (setq n 10)

10
* (/ (* n (+ n 1)) 2)

55
* (setq n 100)

100
* (/ (* n (+ n 1)) 2)

5050
* (setq a 2 b -4 c -6)

-6
* (/ (+ (- b) (sqrt (- (* b b) (* 4 a c)))) (* 2 a))

3.0
* (/ (- (- b) (sqrt (- (* b b) (* 4 a c)))) (* 2 a))

-1.0
* (setq a 1 b 0 c 1)

1
* (/ (+ (- b) (sqrt (- (* b b) (* 4 a c)))) (* 2 a))

#C(0.0 1.0)
* (/ (- (- b) (sqrt (- (* b b) (* 4 a c)))) (* 2 a))

#C(0.0 -1.0)
* (setq d (/ (+ 1 (sqrt 5)) 2))

1.618034
* (setq e (/ (- 1 (sqrt 5)) 2))

-0.618034
* (setq n 10)

10
* (/ (- (expt d (1+ n)) (expt e (1+ n))) (sqrt 5))

89.00001
* (setq n 20)

20
* (/ (- (expt d (1+ n)) (expt e (1+ n))) (sqrt 5))

10946.002
* (setq d (/ (+ 1 (sqrt 5d0)) 2))

1.618033988749895d0
* (setq e (/ (- 1 (sqrt 5d0)) 2))

-0.6180339887498949d0
* (setq n 10)

10
* (/ (- (expt d (1+ n)) (expt e (1+ n))) (sqrt 5))

88.99999869328373d0
* (setq n 20)

20
* (/ (- (expt d (1+ n)) (expt e (1+ n))) (sqrt 5))

10945.999839288585d0
* (1- (expt 2 81))

2417851639229258349412351
* (mod (expt 7 654321) 10)

7
# いまここ

# http://www.nct9.ne.jp/m_hiroi/clisp/abcl02.html<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.20344679633867277, 'fraction_numerical': 0.08373855835240275, 'mean_word_length': 2.5567141403865716, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 197, 'https://': 1, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 7, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '9', 'dir': 'jupyter-structured-clean-dedup', 'id': '427022', 'license': 'MIT', 'n_tokens_mistral': 8961, 'n_tokens_neox': 7829, 'n_words': 1974, 'path': 'learnlisp.ipynb', 'repo_name': 'kalz2q/myjupyternotebooks', 'size': '13984'}