123-45-67+89=100

http://d.hatena.ne.jp/fortran66/20100502/1272731623
を見て面白そうだったのでやってみました。
方法は単純でルールの中で存在する可能性のある全ての式を作ってそれをevalするというもの。

Rule

  • 問題は、1 2 3 4 5 6 7 8 9 のどこかに数学記号を3つ入れて答えが100になるようにする
  • 記号は複数回使って良い
  • 数字は並べ替えちゃだめ

Code

(use srfi-42)
(use srfi-1)

(define (list->number xs)  ;;(<> '(1 2 3)) -> 123
  (fold (lambda (x n) (+ (* n 10 ) x)) 0 xs))

(define (add-tag tag xs) (cons tag xs))

(define (divide-n n xs) ;;n個のしきりで分割(tree)
  (cond ((= n 0) (list (add-tag 'number xs)))
	(else
	 (append-ec (: i 1 (length xs)) 
		    (receive (lhs rhs) (split-at xs i)
		      (cond ((= n 1) (map (cute list 'OP  <> (add-tag 'number rhs))
					  (divide-n(- n 1) lhs)))
			    (else
			     (append (map (cute list 'OP  <> (add-tag 'number rhs))
					  (divide-n(- n 1) lhs))
				     (map (cute list 'OP (add-tag 'number lhs) <>)
					  (divide-n(- n 1) rhs))))))))))

(define (transform ops tree) ;;OPを+ * - /などに(number 1 2 3)を123に変えた形に変換
  (match tree
    [('number . xs) (list (list->number xs))]
    [('OP x y)
     (list-ec (: op ops) (: lhs (transform ops x)) (: rhs (transform ops y))
	      (list op lhs rhs))]))

(define (find-answer ans ops n xs)
  (let1 env (interaction-environment)
    (filter (lambda (exp) (= ans (eval exp env)))
	    (append-map (cut transform ops <>) (divide-n n xs)))))

(define (unwrap-procedure tree);;#<subr +>のような表示を+に変える。
  (map (lambda (x)
	 (cond ((list? x) (unwrap-procedure x))
	       ((procedure? x) (procedure-info x))
	       (else x)))
       tree))

(define (solve ans n)
  (for-each (compose print unwrap-procedure)
	    (find-answer ans (list + * / -) n (iota 9 1))))

(solve 100 3)

;; gosh> (- 123 (+ 45 (- 67 89)))
;; (- 123 (- (+ 45 67) 89))
;; (+ (- 123 (+ 45 67)) 89)
;; (+ (- (- 123 45) 67) 89)