テンパズルを解いてみました
はじめに
これまでRubyでちょこちょこプログラムを書いてきましたが,今年はLispを学ぼうということで,Schemeの本を読んでいます.
そんな訳で,基礎的な練習としてテンパズルをやってみることにしました.
テンパズルとは
wikipedia:テンパズルにずばり書いてあるので,ご覧下さい.(笑)
ただし,数字の並べ替えは面倒なので行わないことにしました.並べ替えを許すと表示も美しくなさそうですし...
実際にやってみた
処理系はGaucheを使いました.
Lispは括弧が多く見づらいのだけど,書いてるうちに気にならなくなってくるのが不思議.
以下,ソースです.
(use srfi-1) (use srfi-13) (use util.combinations) ;; 定数設定 (define *start* 1234) (define *count* 50) (define *operators* '(+ - * /)) (define *figure* (string-length (number->string (+ *start* *count*)))) ;; 切符番号を数値が並んだリストに変換する (define (trans-to-list num) (map digit->integer (string->list (string-pad (number->string num) *figure* #\0)))) ;; 演算子リストからn個の重複組合せを得る (もっとうまいやり方がありそうだけど...) (define (repeated-combination ops n) (define (mul-list lis n) (if (zero? n) () (append lis (mul-list lis (- n 1))))) (delete-duplicates (combinations (mul-list ops n) n))) ;; 与えられた演算子と数をLisp式に変換する (define (make-expr num-list ops) (define (make-expr-iter nums ops expr) (if (null? nums) expr (make-expr-iter (cdr nums) (cdr ops) (list (car ops) expr (car nums))))) (make-expr-iter (cdr num-list) ops (car num-list))) ;; 与えられた演算子と数で10になる場合に式を返す (define (expr-if-10 num-list ops) (let ((expr (make-expr num-list ops))) (if (= (eval expr (interaction-environment)) 10) expr #f))) ;; 全ての演算をためして,10になる組合せを見つける (define (find-10 num-list) (filter-map (cut expr-if-10 num-list <>) (repeated-combination *operators* (- *figure* 1)))) ;; メインループ (for-each print (filter pair? (map find-10 (map trans-to-list (iota *count* *start*)))))
動作確認
1234から1284まで計算してみた結果です.
前置記法なのでピンとこないかもしれないですが10になっています.
((+ (+ (+ 1 2) 3) 4) (+ (* (* 1 2) 3) 4)) ((* (+ (- 1 2) 3) 5) (+ (+ (* 1 2) 3) 5)) ((+ (+ (- 1 2) 3) 8)) ((+ (/ (+ 1 2) 3) 9)) ((- (* (+ 1 2) 4) 2) (+ (* (* 1 2) 4) 2)) ((+ (+ (+ 1 2) 4) 3)) ((+ (+ (* 1 2) 4) 4)) ((* (* (/ 1 2) 4) 5)) ((+ (+ (- 1 2) 4) 7)) ((+ (* (/ 1 2) 4) 8)) ((+ (* (* 1 2) 5) 0) (- (* (* 1 2) 5) 0)) ((/ (* (* 1 2) 5) 1) (* (* (* 1 2) 5) 1)) ((+ (+ (+ 1 2) 5) 2)) ((+ (+ (* 1 2) 5) 3)) ((* (* (/ 1 2) 5) 4)) ((- (* (+ 1 2) 5) 5)) ((+ (+ (- 1 2) 5) 6)) ((+ (+ (+ 1 2) 6) 1)) ((* (+ (- 1 2) 6) 2) (+ (+ (* 1 2) 6) 2) (- (* (* 1 2) 6) 2)) ((+ (+ (- 1 2) 6) 5)) ((+ (* (/ 1 2) 6) 7)) ((- (* (+ 1 2) 6) 8)) ((- (+ (+ 1 2) 7) 0) (+ (+ (+ 1 2) 7) 0)) ((* (+ (+ 1 2) 7) 1) (/ (+ (+ 1 2) 7) 1) (+ (+ (* 1 2) 7) 1)) ((+ (+ (- 1 2) 7) 4) (- (* (* 1 2) 7) 4)) ((- (+ (* 1 2) 8) 0) (+ (+ (* 1 2) 8) 0)) ((- (+ (+ 1 2) 8) 1) (* (+ (* 1 2) 8) 1) (/ (+ (* 1 2) 8) 1)) ((+ (+ (- 1 2) 8) 3))
ちなみに,上記のコードでは*start*を1234として4桁で計算していましたが,任意の桁が扱えるので値を変えてみます.
(define *start* 235711) (define *count* 1)
実行すると...
((+ (+ (+ (/ (+ 2 3) 5) 7) 1) 1) (* (- (+ (+ (- 2 3) 5) 7) 1) 1) (/ (- (+ (+ (- 2 3) 5) 7) 1) 1) (- (* (+ (+ (- 2 3) 5) 7) 1) 1) (- (/ (+ (+ (- 2 3) 5) 7) 1) 1) (+ (+ (+ (- (* 2 3) 5) 7) 1) 1))
10になっているのが分かりますか?