Skip to content

Instantly share code, notes, and snippets.

@y2q-actionman
Last active June 21, 2019 02:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save y2q-actionman/184b9dd9973c0559e810f9b66002b047 to your computer and use it in GitHub Desktop.
Save y2q-actionman/184b9dd9973c0559e810f9b66002b047 to your computer and use it in GitHub Desktop.
repl 変数使えば anaphora いらんやん
(in-package :cl-user)
(defmacro with-repl-variables (&body body)
"BODY 中の式を、REPL 変数の `*', `+', `/', `-' 等を bind しながら順次実行する。"
(let ((form-results (gensym)))
`(let ((* *) (** **) (*** ***)
(+ +) (++ ++) (+++ +++) (- -)
(/ /) (// //) (/// ///))
,@(loop for form in body
collect
`(let* ((- ',form)
(,form-results (multiple-value-list ,form)))
(shiftf +++ ++ + -)
(shiftf /// // / ,form-results)
(shiftf *** ** * (first ,form-results))
(values-list ,form-results))))))
;;;;
(defun example1 (string)
(with-repl-variables
string
(coerce * 'list)
(remove-duplicates *)
(format t "~&String '~A' contains: ~{~C~^ ~}~%"
*** *)
**))
#|
CL-USER> (example1 "Hello, World!")
String 'Hello, World!' contains: H e , W o r l d !
(#\H #\e #\, #\ #\W #\o #\r #\l #\d #\!)
|#
#+()
(defun example2 ()
;; Clojure の threading macro の代替として `*' を使う
;; https://clojuredocs.org/clojure.core/-%3E
(with-repl-variables
"a b c d"
(string-upcase *)
(substitute #\A #\X *)
(split-sequence:split-sequence #\space *)
(first *))) ; => "A"
;;;;
(defun test-with-repl-variables ()
(assert
(equal (with-repl-variables
(values 'a1 'a2)
'b
(values 'c1 'c2 'c3)
(list * ** ***))
'(C1 B A1)))
(assert
(equal (with-repl-variables
(values 1 2 3)
/)
'(1 2 3)))
(assert
(= (with-repl-variables
(floor 22 7)
(+ (* (car /) 7) (cadr /)))
22))
(assert
(equal (with-repl-variables
(+ 0 1)
(- 4 2)
(/ 9 3)
(list + ++ +++))
'((/ 9 3) (- 4 2) (+ 0 1))))
(assert
(equal (with-output-to-string (*standard-output*)
(with-repl-variables
(format t "Evaluating ~S" -)))
"Evaluating (FORMAT T \"Evaluating ~S\" -)"))
t)
(in-package :cl-user)
(defmacro with-repl-variables! (&body forms)
"`with-repl-variables' に前の式を '!' で再評価する機能を足したもの。"
`(symbol-macrolet ((!!! (!!!)) (!! (!!)) (! (!)))
(flet ((!!! ())
(!! ())
(! ()))
(declare (ignorable (function !) (function !!) (function !!!)))
(with-repl-variables!-internal ,@forms))))
(defmacro with-repl-variables!-internal (&body forms)
"`with-repl-variables!' の下請け"
;; `with-repl-variables' と違い、 `let*' での再bindのみで実装してみた。
(let ((form1 (first forms))
(form-results (gensym)))
`(let* ((- ',form1)
(,form-results (multiple-value-list ,form1))
(+++ ++) (++ +) (+ -)
(/// //) (// /) (/ ,form-results)
(*** **) (** *) (* (first ,form-results)))
(flet ((!!! () (!!))
(!! () (!))
(! () ,form1))
(declare (ignorable (function !) (function !!) (function !!!)))
,(if (rest forms)
`(with-repl-variables!-internal ,@(rest forms))
`(values-list ,form-results))))))
;;;;
(defun test-with-repl-variables! ()
(assert
(equal (with-repl-variables!
(values 'a1 'a2)
'b
(values 'c1 'c2 'c3)
(list * ** ***))
'(C1 B A1)))
(assert
(equal (with-repl-variables!
(values 1 2 3)
/)
'(1 2 3)))
(assert
(= (with-repl-variables!
(floor 22 7)
(+ (* (car /) 7) (cadr /)))
22))
(assert
(equal (with-repl-variables!
(+ 0 1)
(- 4 2)
(/ 9 3)
(list + ++ +++))
'((/ 9 3) (- 4 2) (+ 0 1))))
(assert
(equal (with-output-to-string (*standard-output*)
(with-repl-variables!
(format t "Evaluating ~S" -)))
"Evaluating (FORMAT T \"Evaluating ~S\" -)"))
(assert
(equal (let ((tmp 0))
(with-repl-variables!
(incf tmp)
!
!)
tmp)
3))
t)
@y2q-actionman
Copy link
Author

y2q-actionman commented Sep 27, 2018

+ の symbol-macrolet 版を ! にするとかは思いつく

@y2q-actionman
Copy link
Author

もし lexical variable を使うなら、 with-repl-variables!-internal のように let の連鎖にしたほうがよいのだが。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment