Lispを使っていろんな数列を書き出してみる

はじめに

こんにちは、野村です。

今回は、Lispというプログラム言語でいくつかの数列を書き出してみます。

実は今回のソース、Lispを全く知らない状態で入門サイトを見ながら書いたものです。
括弧を数えながらなんとか乗り切りました。

インストールと実行

インストール

管理ユーザになってsbclをインストール。

# apt install sbcl

ホントはclispをインストールしたかったけど、debian9のリポジトリにないのでsbclにした。
sbclはclispよりWARNINGが出やすいので、より厳密に書かないといけないようです。

実行

vimでスクリプトを書いたあと、vimのコマンドモードで以下のコマンドを実行。

:!sbcl --script %

素数

・1より大きい自然数で、正の約数が1と自分自身のみである数。

(defun range (s e)
  (if (>= s e) () (cons s (range (+ s 1) e))))

(defun sieve (n)
  (let (l)
    (setq l (lambda (x) (zerop (mod x (car n)))))
    (remove-if l n)))

(defun sosu (n)
  (if (<= (car n) (sqrt (car (last n))))
    (cons (car n) (sosu (sieve n)))
    n))

(print (sosu (range 2 100))) ; 100未満の素数を表示
(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97)

双子素数

・双子素数:差が2である2つの素数の組
・いとこ素数:差が4である2つの素数の組
・セクシー素数:差が6である2つの素数の組

(defun range (s e)
  (if (>= s e) () (cons s (range (+ s 1) e))))

(defun sieve (a i f)
  (let (l)
    (setq l (lambda (x) (zerop (mod i x))))
    (if (zerop (count-if l (remove-if (lambda (x) (> x (sqrt i))) a)))
      (let (b)
        (setq b (append a (list i)))
        (if (zerop (count-if (lambda (x) (= x (- i f))) b))
          (sieve b (+ i 1) f)
          b))
      (sieve a (+ i 1) f))))

(defun lam (a f)
  (let (b)
    (setq b (sieve (car a) (+ 1 (car (last (car a)))) f))
    (list b (append (nth 1 a) (list (car (last b)))))))

(defun twin (f c)
  (map 'list
    #'(lambda (x) (list (- x f) x))
    (nth 1 (reduce #'lam (fill (range 0 c) f) :initial-value '((2) ())))))

(print (twin 2 10)) ; 双子素数を10組表示
(print (twin 4 10)) ; いとこ素数を10組表示
(print (twin 6 10)) ; セクシー素数を10組表示
((3 5) (5 7) (11 13) (17 19) (29 31) (41 43) (59 61) (71 73) (101 103) (107 109))
((3 7) (7 11) (13 17) (19 23) (37 41) (43 47) (67 71) (79 83) (97 101) (103 107))
((5 11) (7 13) (11 17) (13 19) (17 23) (23 29) (31 37) (37 43) (41 47) (47 53))

幸運数

・ポーランドの数学者スタニスワフ・ウラムによって提案された数列。
・素数の性質を研究するために、素数に似たルールで導き出せる数列を考案したとのこと。

(defun range (s e &optional (p 1))
  (if (>= s e) () (cons s (range (+ s p) e p))))

(defun flt (b stp)
  (let (l)
    (let (e)
      (setq l (lambda (x) (zerop (mod x stp))))
      (setq e (remove-if l (range 0 (length b))))
      (map 'list #'(lambda (x) (nth x b)) e))))

(defun luc (a i)
  (let (b)
    (let (s)
      (setq b (cons 0 a))
      (setq s (nth i b))
      (if (< s (length b))
        (luc (flt b s) (+ i 1)))
        (cdr b))))

(print (luc (range 1 100 2) 2)) ; 100未満の幸運数を表示
(1 3 7 9 13 15 21 25 31 33 37 43 49 51 63 67 69 73 75 79 87 93 99)

階乗

・1 から n までのすべての整数の積。

(defun fact (s i n)
  (if (> i n) () (cons s (fact (* s i) (+ i 1) n))))

(print (fact 1 1 10)) ; 階乗を10個表示
(1 1 2 6 24 120 720 5040 40320 362880)

フィボナッチ数

・フィボナッチ数:イタリアの数学者レオナルド・フィボナッチにちなんで名付けられた数
・リュカ数:フランスの数学者エドゥアール・リュカにちなんで名付けられた数

(defun fib1 (a b i)
  (if (> i 0)
    (cons a (fib1 b (+ a b) (- i 1)))))

(print (fib1 0 1 10)) ; フィボナッチ数を10個表示
(print (fib1 2 1 10)) ; リュカ数を10個表示
(0 1 1 2 3 5 8 13 21 34)
(2 1 3 4 7 11 18 29 47 76)

パスカルの三角形

・二項展開における係数を三角形状に並べたもの。

(defun zipa (a0 a1)
  (if (and (consp a0) (consp a1))
    (cons (+ (car a0) (car a1)) (zipa (cdr a0) (cdr a1)))))

(defun pas (s i n)
  (if (> i n)
    ()
    (cons s (pas (zipa (cons 0 s) (append s '(0))) (+ i 1) n))))

(print (pas '(1) 1 10)) ; パスカルの三角形を10段表示
((1) (1 1) (1 2 1) (1 3 3 1) (1 4 6 4 1) (1 5 10 10 5 1) (1 6 15 20 15 6 1) (1 7 21 35 35 21 7 1) (1 8 28 56 70 56 28 8 1) (1 9 36 84 126 126 84 36 9 1))

undefined variable 対策

sbclでは、未定義の変数をsetqするとWARNINGが出る。
なので、変数はletで定義しないといけない模様。

* (setq a 10)
; in: SETQ A
;     (SETQ A 10)
;
; caught WARNING:
;   undefined variable: A
;
; compilation unit finished
;   Undefined variable:
;     A
;   caught 1 WARNING condition

10

修正例

* (let (a)
     (setq a 10))

10

最後に

以上、Lispを使っていくつかの数列を書き出してみました。

以前紹介したHaskellもそうだったけど、一般的なプログラムと勝手が全然違うので手間取りました。

Haskellでいろんな数列を書き出してみる
今回は、Haskellというプログラム言語でいろいろな数列を表示させてみます。とにかくソースがシンプル。ほとんど魔術。せっかくなので、Haskellを導入する方法も紹介します。

でもconsがちょっと解ってくると面白くなってきた。

Lispの書籍といえば、これかな?

『Land of Lisp』(オライリージャパン)。結構しつこく立ち読みしている本。いずれ買おうと思ってます。

というわけで、今回はこれにて。

シェアする

  • このエントリーをはてなブックマークに追加

フォローする

野村 野村のプロフィール
メインPCはWindows10のVirtualBox上のFreeBSD。Linux/Unixの小ネタを求めて日々右往左往してたりする。