🚀
Common LispでGemini
Common LispでGemini
Common LispからgeminiのAPIを利用するというのが流れてきたので、試してみる。
APIキーは環境変数経由で渡すので変数を設定してからライブラリを読み込むのがよさそう。
(setf (uiop:getenv "GOOGLE_API_KEY") "....")
(ql:quickload 'gemini)
まずは、普通に呼び出してみる。
(defun fib (n)
(values
(read-from-string
(gemini:generate
(fstring "fibonacci数の~D番目の数字を教えてください。返答は数値のみにしてください" n)))))
(time (fib 10))
→ 55
User time = 0.008
System time = 0.002
Elapsed time = 0.593
Allocation = 102600 bytes
5 Page faults
GC time = 0.000
(time (fib 100))
→ 354224848179261915075
Timing the evaluation of (fib 100)
User time = 0.051
System time = 0.010
Elapsed time = 0.908
Allocation = 314176 bytes
205 Page faults
GC time = 0.000
式の生成をマクロにしてみる。生成された式はマクロ展開で確認できる。
(defmacro generate-definition (words)
(read-from-string
(gemini:generate
(fstring "~A~%~A~%"
words
"解答形式は下記に従ってください。
Common Lispに入力できる形式
定義の式だけにしてください
Markdownのマークアップは不要です"))))
(generate-definition "xにnを足す関数を定義してください")
;;; ==> (defun add-n (x n) (+ x n)) ;生成される式
;;; Safety = 3, Speed = 1, Space = 1, Float = 1, Interruptible = 1
;;; Compilation speed = 1, Debug = 3, Fixnum safety = 3
;;; Source level debugging is on
;;; Source file recording is on
;;; Cross referencing is on
; (lw:top-level-form 0)
; add-n
(add-n 3 4)
→ 7
(generate-definition "fibonacci数を求める関数を定義してください")
(fibonacci 10)
→ 55
もうすこし関数定義のマクロっぽくしてみる
(defmacro define-function-generative (name words)
(read-from-string
(gemini:generate
(fstring "~Aを~Aとして定義してください~%~A~%"
words
name
"解答形式は下記に従ってください。
Markdownのマークアップは不要です
Common Lispに入力できる形式
関数定義の式だけにしてください
"))))
(define-function-generative fib "fibonacci数を求める関数")
(fib 10)
→ 55
(define-function-generative lucas "lucas数を求める関数")
;; ===>
;; (defun lucas (n) (if (zerop n) 2 (if (= n 1) 1 (+ (lucas (- n 1)) (lucas (- n 2))))))
(lucas 10)
→ 123
(define-function-generative fib-iter "fibonacci数を求める末尾再帰関数")
(fib-iter 100)
→ 354224848179261915075
(define-function-generative tarai "竹内関数")
;; ===>
;; (defun tarai (x y z)
;; (if (<= x y)
;; y
;; (tarai (tarai (- x 1) y z)
;; (tarai (- y 1) z x)
;; (tarai (- z 1) x y))))
(tarai 12 6 0)
→ 12
(define-function-generative wget "urlを受け取ってページのhtmlテキストを返す関数")
;; ==>
;; (defun wget (url)
;; (let ((process (uiop/run-program:run-program (format nil "wget -q -O - ~A" url) :output '(:string :stripped t))))
;; process))
(define-function-generative tak "tak関数")
(tak 18 12 6)
→ 7
(define-function-generative yyyy-mm-dd "本日の日付をyyyy-mm-dd形式の文字列で返す関数")
(yyyy-mm-dd)
→ "2025-02-24"
結構いける。ボディで英文を書くスタイルはどうか。
(defmacro define-function-generative (name &body words)
(let ((ans (gemini:generate
(print (fstring "define a function that ~{~A ~} as named ~A. ~%~A~%"
words
name
"## Response format:
Without markups.
Valid Common Lisp expressions.
Only definition form.")))))
(print ans)
`(progn
,@(w/instring (in (re.replace-all "```\\w*" ans ""))
(for (let xpr := (read in nil in))
(until (eq xpr in))
(collect xpr))))))
(define-function-generative fib
returns fibonacci number)
(fib 10)
→ 55
(define-function-generative lucas
returns lucas number)
(lucas 10)
→ 123
(define-function-generative yyyy-mm-dd
returns date string of today as "yyyy-mm-dd" format)
(yyyy-mm-dd)
→ "2025-02-24"
(define-function-generative cramers-rule
Cramers Rule to solve Ax + By = M and Cx + Dy = N
x = (MD - BN) / (AD - BC)
y = (AN - MC) / (AD - BC))
;; ==>
;; (progn
;; (defun cramers-rule (a b m c d n)
;; (let ((determinant (- (* a d) (* b c))))
;; (if (zerop determinant)
;; (values nil nil "Determinant is zero. System may have no solution or infinite solutions.")
;; (let ((x (/ (- (* m d) (* b n)) determinant)) (y (/ (- (* a n) (* m c)) determinant))) (values x y nil))))))
まとめ
1960年代にALISPという英文を記述するスタイルの構文を採用したLispがあったけど、geminiを使えば簡単に実現できそう。
Discussion