3 ms·
If I were to start it in Common Lisp, I'd probably do something like this. Next steps might be to make normal variable declarations automatic by scanning guard
by martinflack 10y ago
If I were to start it in Common Lisp, I'd probably do something like this. Next steps might be to make normal variable declarations automatic by scanning guard expressions (which would prevent unwittingly mentioning a variable too early); and depending on the intended usage, maybe permit infix math via the infix-math package.
(defpackage solver (:use :cl :iterate) (:shadow pi))
(in-package :solver)
(defmacro defsolver (name default constraints result)
(flet ((normal-var (var) `(iter (for ,var ,@(rest default))))
(custom-var (var spec) `(iter (for ,var in ,spec)))
(guard (expr) `(when ,expr))
(solved () `(return-from ,name ,result)))
(macrolet ((yield (form) `(append ,form (list (constraint (rest cs))))))
(labels ((constraint (cs &aux (c (first cs)))
(cond ((null cs) nil)
((eq c :solved) (solved))
((symbolp c) (yield (normal-var c)))
((eq (first c) 'for) (yield (custom-var (second c) (third c))))
(t (yield (guard c))))))
`(defun ,name () ,(constraint `(,@constraints :solved)))))))
(defsolver result
(:default from 1 to 26)
(th
o
r
(= (+ (* 2 (expt th 2)) (expt o 2) (expt r 2)) 1000)
s
z
(= (+ (* (expt th 2) (expt z 2) (- s z)) (expt s 2)) 7225)
g
d
xi
(= (+ (expt (1- g) 2) (expt d 2) (expt xi 2) (- xi)) 600)
(for (et i) `((,(- xi 7) ,(- xi 11)) (,(- xi 11) ,(- xi 7))
(,(+ xi 7) ,(+ xi 11)) (,(+ xi 11) ,(+ xi 7))))
(and (< 0 et) (<= et 26) (< 0 i) (<= i 26))
(for (a mu) '((4 1) (2 2) (1 4)))
pi
(= (* 4 g) (* a (+ a pi)))
b
n
(= (+ (expt b 3) (expt z 3) (expt n 3) (expt o 9)) 1997)
(for k '(3 4))
e
(= (+ (expt (/ a k) 2) (expt (/ b n) 2) (expt (/ d xi) 2)
(expt (/ e pi) 2) (expt (/ et mu) 2) (expt (/ i s) 2)) 6))
(list a b g d e z et th i k th mu n xi o pi r s))
SOLVER> (time (result))
Evaluation took:
0.010 seconds of real time
0.012000 seconds of total run time (0.012000 user, 0.000000 system)
120.00% CPU
23,160,723 processor cycles
851,968 bytes consed
(4 9 19 12 15 3 1 20 5 4 20 1 9 12 2 15 14 5)
Edit: formatting, simplification
The b, n, k, e definitions are reordered better as in @eutectic's haskell example.
- kazinator 10y agoI don't see why yield has to be a macrolet rather than a labels.
- martinflack 10y agoYou're right, it could, if you prefer. In the same labels form with an extra parameter cs, which the yield's all pass in; or in a labels/flet form inside constraint. (defmacro defsolver (name default constraints result) (flet ((normal-var (var) `(iter (for ,var ,@(rest default)))) (custom-var (var spec) `(iter (for ,var in ,spec))) (guard (expr) `(when ,expr)) (solved () `(return-from ,name ,result))) (labels ((constraint (cs &aux (c (first cs))) (flet ((yield (form) (append form (list (constraint (rest cs)))))) (cond ((null cs) nil) ((eq c :solved) (solved)) ((symbolp c) (yield (normal-var c))) ((eq (first c) 'for) (yield (custom-var (second c) (third c)))) (t (yield (guard c))))))) `(defun ,name () ,(constraint `(,@constraints :solved))))))
- kazinator 10y agoAll your functions can be in a single labels block now. It's okay for a non-recursive function to be a labels. flet is really for those situations in which labels cannot be used, like defining an inner foo which shadows and calls (i.e wraps) an outer foo.